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 \ libglvnd \
libinput \ libinput \
libliftoff \ libliftoff \
libxcursor \
libxcvt \ libxcvt \
libxfont2 \ libxfont2 \
libxkbcommon \ libxkbcommon \
@ -73,6 +74,11 @@ runs:
run: | 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 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 - name: Get Xorg pacman pkgs
shell: bash shell: bash
if: inputs.INSTALL_XORG_PKGS == 'true' if: inputs.INSTALL_XORG_PKGS == 'true'

8
.gitignore vendored
View file

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

4
.gitmodules vendored
View file

@ -7,7 +7,3 @@
[submodule "subprojects/tracy"] [submodule "subprojects/tracy"]
path = subprojects/tracy path = subprojects/tracy
url = https://github.com/wolfpld/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) cmake_minimum_required(VERSION 3.27)
include(CheckIncludeFile) include(CheckIncludeFile)
include(ExternalProject)
include(GNUInstallDirs) include(GNUInstallDirs)
# Get version # Get version
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/props.json PROPS) file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
string(JSON VER GET ${PROPS} version) string(STRIP ${VER_RAW} VER)
project(Hyprland project(
Hyprland
DESCRIPTION "A Modern C++ Wayland Compositor" DESCRIPTION "A Modern C++ Wayland Compositor"
VERSION ${VER} VERSION ${VER})
)
set(HYPRLAND_VERSION ${VER}) set(HYPRLAND_VERSION ${VER})
set(PREFIX ${CMAKE_INSTALL_PREFIX}) set(PREFIX ${CMAKE_INSTALL_PREFIX})
@ -22,18 +21,13 @@ set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
message(STATUS "Gathering git info") message(STATUS "Gathering git info")
# Get git info # Get git info hash and branch
# hash and branch execute_process(COMMAND ./scripts/generateVersion.sh
execute_process(
COMMAND ./scripts/generateVersion.sh
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
# udis # udis
add_subdirectory("subprojects/udis86") add_subdirectory("subprojects/udis86")
# wlroots
message(STATUS "Setting up wlroots")
if(CMAKE_BUILD_TYPE) if(CMAKE_BUILD_TYPE)
string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER) string(TOLOWER ${CMAKE_BUILD_TYPE} BUILDTYPE_LOWER)
if(BUILDTYPE_LOWER STREQUAL "release") if(BUILDTYPE_LOWER STREQUAL "release")
@ -53,18 +47,6 @@ else()
set(BUILDTYPE_LOWER "release") set(BUILDTYPE_LOWER "release")
endif() 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) find_package(PkgConfig REQUIRED)
pkg_get_variable(WaylandScanner wayland-scanner wayland_scanner) pkg_get_variable(WaylandScanner wayland-scanner wayland_scanner)
@ -81,17 +63,16 @@ else()
message(STATUS "Configuring Hyprland in Release with CMake") message(STATUS "Configuring Hyprland in Release with CMake")
endif() endif()
include_directories( include_directories(. "src/" "subprojects/udis86/" "protocols/")
.
"src/"
"subprojects/wlroots-hyprland/include/"
"subprojects/wlroots-hyprland/build/include/"
"subprojects/udis86/"
"protocols/")
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
add_compile_definitions(WLR_USE_UNSTABLE) add_compile_options(
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wall
-Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith -Wextra
-Wno-unused-parameter
-Wno-unused-value
-Wno-missing-field-initializers
-Wno-narrowing
-Wno-pointer-arith
-fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=) -fmacro-prefix-map=${CMAKE_SOURCE_DIR}/=)
set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE) set(CMAKE_EXECUTABLE_ENABLE_EXPORTS TRUE)
@ -108,13 +89,33 @@ else()
endif() endif()
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
pkg_check_modules(deps REQUIRED IMPORTED_TARGET pkg_check_modules(
xkbcommon uuid deps
wayland-server wayland-client wayland-cursor wayland-protocols REQUIRED
cairo pango pangocairo pixman-1 IMPORTED_TARGET
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm aquamarine
hyprlang>=0.3.2 hyprcursor>=0.1.7 hyprutils>=0.2.0 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) find_package(hyprwayland-scanner 0.3.10 REQUIRED)
@ -127,7 +128,6 @@ if(USE_TRACY)
endif() endif()
add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES}) add_executable(Hyprland ${SRCFILES} ${TRACY_CPP_FILES})
add_dependencies(Hyprland wlroots-hyprland)
set(USE_GPROF ON) set(USE_GPROF ON)
@ -192,7 +192,20 @@ if(NO_XWAYLAND)
add_compile_definitions(NO_XWAYLAND) add_compile_definitions(NO_XWAYLAND)
else() else()
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...") 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) 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) target_link_libraries(Hyprland PkgConfig::xdeps)
endif() endif()
@ -209,7 +222,8 @@ include(CPack)
message(STATUS "Setting precompiled headers") 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") message(STATUS "Setting link libraries")
@ -227,19 +241,22 @@ function(protocol protoPath protoName external)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
COMMAND ${WaylandScanner} server-header ${path} protocols/${protoName}-protocol.h COMMAND ${WaylandScanner} server-header ${path}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} protocols/${protoName}-protocol.h
) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c
COMMAND ${WaylandScanner} private-code ${path} protocols/${protoName}-protocol.c COMMAND ${WaylandScanner} private-code ${path}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 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(
target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h) 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() endfunction()
function(protocolNew protoPath protoName external) function(protocolnew protoPath protoName external)
if(external) if(external)
set(path ${CMAKE_SOURCE_DIR}/${protoPath}) set(path ${CMAKE_SOURCE_DIR}/${protoPath})
else() else()
@ -248,74 +265,83 @@ function(protocolNew protoPath protoName external)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ COMMAND hyprwayland-scanner ${path}/${protoName}.xml
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/protocols/
) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/${protoName}.cpp protocols/${protoName}.hpp) target_sources(Hyprland PRIVATE protocols/${protoName}.cpp
target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp) protocols/${protoName}.hpp)
target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp)
endfunction() endfunction()
function(protocolWayland) function(protocolWayland)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
COMMAND hyprwayland-scanner --wayland-enums ${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ COMMAND hyprwayland-scanner --wayland-enums
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ${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(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp) target_sources(generate-protocol-headers
PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp)
endfunction() endfunction()
target_link_libraries(Hyprland target_link_libraries(Hyprland OpenGL::EGL OpenGL::GL Threads::Threads
${CMAKE_SOURCE_DIR}/subprojects/wlroots-hyprland/build/libwlroots.a libudis86 uuid)
OpenGL::EGL
OpenGL::GL
Threads::Threads
libudis86
uuid
)
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true) protocol("protocols/wlr-screencopy-unstable-v1.xml"
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true) "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true) protocol(
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) "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-gamma-control-unstable-v1" true)
protocolNew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true) protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
protocolNew("protocols" "wlr-output-power-management-unstable-v1" true) protocolnew("protocols" "wlr-output-power-management-unstable-v1" true)
protocolNew("protocols" "virtual-keyboard-unstable-v1" true) protocolnew("protocols" "virtual-keyboard-unstable-v1" true)
protocolNew("protocols" "wlr-virtual-pointer-unstable-v1" true) protocolnew("protocols" "wlr-virtual-pointer-unstable-v1" true)
protocolNew("protocols" "input-method-unstable-v2" true) protocolnew("protocols" "input-method-unstable-v2" true)
protocolNew("protocols" "wlr-output-management-unstable-v1" true) protocolnew("protocols" "wlr-output-management-unstable-v1" true)
protocolNew("protocols" "kde-server-decoration" true) protocolnew("protocols" "kde-server-decoration" true)
protocolNew("protocols" "wlr-data-control-unstable-v1" true) protocolnew("protocols" "wlr-data-control-unstable-v1" true)
protocolNew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1" true) protocolnew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1"
protocolNew("protocols" "wlr-layer-shell-unstable-v1" true) true)
protocolNew("protocols" "wayland-drm" true) protocolnew("protocols" "wlr-layer-shell-unstable-v1" true)
protocolNew("staging/tearing-control" "tearing-control-v1" false) protocolnew("protocols" "wayland-drm" true)
protocolNew("staging/fractional-scale" "fractional-scale-v1" false) protocolnew("staging/tearing-control" "tearing-control-v1" false)
protocolNew("unstable/xdg-output" "xdg-output-unstable-v1" false) protocolnew("staging/fractional-scale" "fractional-scale-v1" false)
protocolNew("staging/cursor-shape" "cursor-shape-v1" false) protocolnew("unstable/xdg-output" "xdg-output-unstable-v1" false)
protocolNew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false) protocolnew("staging/cursor-shape" "cursor-shape-v1" false)
protocolNew("unstable/relative-pointer" "relative-pointer-unstable-v1" false) protocolnew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false)
protocolNew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false) protocolnew("unstable/relative-pointer" "relative-pointer-unstable-v1" false)
protocolNew("staging/alpha-modifier" "alpha-modifier-v1" false) protocolnew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false)
protocolNew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1" false) protocolnew("staging/alpha-modifier" "alpha-modifier-v1" false)
protocolNew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false) protocolnew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1"
protocolNew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false) false)
protocolNew("unstable/text-input" "text-input-unstable-v3" false) protocolnew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false)
protocolNew("unstable/pointer-constraints" "pointer-constraints-unstable-v1" false) protocolnew("unstable/keyboard-shortcuts-inhibit"
protocolNew("staging/xdg-activation" "xdg-activation-v1" false) "keyboard-shortcuts-inhibit-unstable-v1" false)
protocolNew("staging/ext-idle-notify" "ext-idle-notify-v1" false) protocolnew("unstable/text-input" "text-input-unstable-v3" false)
protocolNew("staging/ext-session-lock" "ext-session-lock-v1" false) protocolnew("unstable/pointer-constraints" "pointer-constraints-unstable-v1"
protocolNew("stable/tablet" "tablet-v2" false) false)
protocolNew("stable/presentation-time" "presentation-time" false) protocolnew("staging/xdg-activation" "xdg-activation-v1" false)
protocolNew("stable/xdg-shell" "xdg-shell" false) protocolnew("staging/ext-idle-notify" "ext-idle-notify-v1" false)
protocolNew("unstable/primary-selection" "primary-selection-unstable-v1" false) protocolnew("staging/ext-session-lock" "ext-session-lock-v1" false)
protocolNew("staging/xwayland-shell" "xwayland-shell-v1" false) protocolnew("stable/tablet" "tablet-v2" false)
protocolNew("stable/viewporter" "viewporter" false) protocolnew("stable/presentation-time" "presentation-time" false)
protocolNew("stable/linux-dmabuf" "linux-dmabuf-v1" 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 # tools
add_subdirectory(hyprctl) add_subdirectory(hyprctl)
@ -324,12 +350,12 @@ add_subdirectory(hyprpm)
# binary and symlink # binary and symlink
install(TARGETS Hyprland) install(TARGETS Hyprland)
install(CODE "execute_process( \ install(
CODE "execute_process( \
COMMAND ${CMAKE_COMMAND} -E create_symlink \ COMMAND ${CMAKE_COMMAND} -E create_symlink \
${CMAKE_INSTALL_BINDIR}/Hyprland \ ${CMAKE_INSTALL_FULL_BINDIR}/Hyprland \
${CMAKE_INSTALL_BINDIR}/hyprland ${CMAKE_INSTALL_FULL_BINDIR}/hyprland
)" )")
)
# session file # session file
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
@ -337,8 +363,7 @@ install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.desktop
# wallpapers # wallpapers
file(GLOB_RECURSE WALLPAPERS "assets/wall*") file(GLOB_RECURSE WALLPAPERS "assets/wall*")
install(FILES ${WALLPAPERS} install(FILES ${WALLPAPERS} DESTINATION ${CMAKE_INSTALL_DATADIR}/hyprland)
DESTINATION ${CMAKE_INSTALL_DATADIR}/hyprland)
# default config # default config
install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf install(FILES ${CMAKE_SOURCE_DIR}/example/hyprland.conf
@ -350,34 +375,24 @@ install(FILES ${CMAKE_SOURCE_DIR}/assets/hyprland-portals.conf
# man pages # man pages
file(GLOB_RECURSE MANPAGES "docs/*.1") file(GLOB_RECURSE MANPAGES "docs/*.1")
install(FILES ${MANPAGES} install(FILES ${MANPAGES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
# pkgconfig entry # pkgconfig entry
install(FILES ${CMAKE_BINARY_DIR}/hyprland.pc install(FILES ${CMAKE_BINARY_DIR}/hyprland.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig) 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 # protocol headers
set(HEADERS_PROTO "${CMAKE_CURRENT_SOURCE_DIR}/protocols") set(HEADERS_PROTO "${CMAKE_CURRENT_SOURCE_DIR}/protocols")
install(DIRECTORY ${HEADERS_PROTO} install(
DIRECTORY ${HEADERS_PROTO}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING PATTERN "*.h*") FILES_MATCHING
PATTERN "*.h*")
# hyprland headers # hyprland headers
set(HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src") set(HEADERS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src")
install(DIRECTORY ${HEADERS_SRC} install(
DIRECTORY ${HEADERS_SRC}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hyprland
FILES_MATCHING PATTERN "*.h*") FILES_MATCHING
PATTERN "*.h*")

View file

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

View file

@ -13,10 +13,10 @@
<br> <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, 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>
<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 - All of the eyecandy: gradient borders, blur, animations, shadows and much more
- A lot of customization - 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 - Custom bezier curves for the best animations
- Powerful plugin support - Powerful plugin support
- Built-in plugin manager - 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 - Config reloaded instantly upon saving
- Fully dynamic workspaces - Fully dynamic workspaces
- Two built-in layouts and more available as plugins - Two built-in layouts and more available as plugins
- Uses forked wlroots with QoL patches
- Global keybinds passed to your apps of choice - Global keybinds passed to your apps of choice
- Tiling/pseudotiling/floating/fullscreen windows - Tiling/pseudotiling/floating/fullscreen windows
- Special workspaces (scratchpads) - Special workspaces (scratchpads)
@ -86,7 +85,7 @@ easy IPC, much more QoL stuff than other wlr-based compositors and more...
<br> <br>
**[wlroots]** - *For their amazing library* **[wlroots]** - *For powering Hyprland in the past*
**[tinywl]** - *For showing how 2 do stuff* **[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 .TP
\f[B]-c\f[R], \f[B]--config\f[R] \f[B]-c\f[R], \f[B]--config\f[R]
Specify config file to use. 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 .SH BUGS
.TP .TP
Submit bug reports and request features online at: Submit bug reports and request features online at:

View file

@ -41,6 +41,12 @@ OPTIONS
**-c**, **--config** **-c**, **--config**
Specify config file to use. 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 BUGS
==== ====

View file

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

View file

@ -7,6 +7,14 @@
# <https://github.com/nix-systems/nix-systems> # <https://github.com/nix-systems/nix-systems>
systems.url = "github:nix-systems/default-linux"; 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 = { hyprcursor = {
url = "github:hyprwm/hyprcursor"; url = "github:hyprwm/hyprcursor";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";

View file

@ -4,4 +4,4 @@ Name: Hyprland
URL: https://github.com/hyprwm/Hyprland URL: https://github.com/hyprwm/Hyprland
Description: Hyprland header files Description: Hyprland header files
Version: @HYPRLAND_VERSION@ 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"; std::cerr << "\n" << Colors::RED << "" << Colors::RESET << " Could not check out revision " << rev << ". shell returned:\n" << ret << "\n";
return false; 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; 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"); progress.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " commit pin " + plugin + " matched hl, resetting");
execAndGet("cd " + m_szWorkingPluginDirectory + " && git reset --hard --recurse-submodules " + plugin); 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 else
headers = ""; headers = "";
if (PATH.ends_with("protocols") || PATH.ends_with("wlroots-hyprland")) if (PATH.ends_with("protocols"))
continue; continue;
verHeader = trim(PATH.substr(2)) + "/hyprland/src/version.h"; verHeader = trim(PATH.substr(2)) + "/hyprland/src/version.h";
@ -493,11 +502,6 @@ bool CPluginManager::updateHeaders(bool force) {
return false; 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.printMessageAbove(std::string{Colors::GREEN} + "" + Colors::RESET + " configured Hyprland");
progress.m_iSteps = 4; progress.m_iSteps = 4;
progress.m_szCurrentMessage = "Installing sources"; progress.m_szCurrentMessage = "Installing sources";

View file

@ -1,5 +1,5 @@
project('Hyprland', 'cpp', 'c', 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 : [ default_options : [
'warning_level=2', 'warning_level=2',
'default_library=static', 'default_library=static',
@ -24,8 +24,6 @@ if cpp_compiler.check_header('execinfo.h')
add_project_arguments('-DHAS_EXECINFO', language: 'cpp') add_project_arguments('-DHAS_EXECINFO', language: 'cpp')
endif 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_dep = dependency('xcb', required: get_option('xwayland'))
xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland')) xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland'))
xcb_errors_dep = dependency('xcb-errors', 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') udis = cmake.subproject('udis86')
udis86 = udis.dependency('libudis86') udis86 = udis.dependency('libudis86')
if get_option('xwayland').enabled() and not have_xwlr if not xcb_dep.found()
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
add_project_arguments('-DNO_XWAYLAND', language: 'cpp') add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif endif
@ -86,5 +79,5 @@ import('pkgconfig').generate(
url: 'https://github.com/hyprwm/Hyprland', url: 'https://github.com/hyprwm/Hyprland',
description: 'Hyprland header files', description: 'Hyprland header files',
install_dir: pkg_install_dir, install_dir: pkg_install_dir,
subdirs: ['', 'hyprland/protocols', 'hyprland', 'hyprland/wlr'], subdirs: ['', 'hyprland/protocols', 'hyprland'],
) )

View file

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

View file

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

View file

@ -6,7 +6,10 @@
#include "managers/PointerManager.hpp" #include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp" #include "managers/SeatManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp" #include "managers/eventLoop/EventLoopManager.hpp"
#include <aquamarine/output/Output.hpp>
#include <random> #include <random>
#include <cstring>
#include <filesystem>
#include <unordered_set> #include <unordered_set>
#include "debug/HyprCtl.hpp" #include "debug/HyprCtl.hpp"
#include "debug/CrashReporter.hpp" #include "debug/CrashReporter.hpp"
@ -22,15 +25,20 @@
#include "protocols/core/Compositor.hpp" #include "protocols/core/Compositor.hpp"
#include "protocols/core/Subcompositor.hpp" #include "protocols/core/Subcompositor.hpp"
#include "desktop/LayerSurface.hpp" #include "desktop/LayerSurface.hpp"
#include "render/Renderer.hpp"
#include "xwayland/XWayland.hpp" #include "xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String; #include <aquamarine/input/Input.hpp>
#include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/resource.h> #include <sys/resource.h>
using namespace Hyprutils::String;
using namespace Aquamarine;
int handleCritSignal(int signo, void* data) { int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo); 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() { void CCompositor::bumpNofile() {
if (!getrlimit(RLIMIT_NOFILE, &m_sOriginalNofile)) if (!getrlimit(RLIMIT_NOFILE, &m_sOriginalNofile))
Debug::log(LOG, "Old rlimit: soft -> {}, hard -> {}", m_sOriginalNofile.rlim_cur, m_sOriginalNofile.rlim_max); 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)]; m_szCurrentSplash = SPLASHES[distribution(engine)];
} }
static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;
//
void CCompositor::initServer() { void CCompositor::initServer() {
m_sWLDisplay = wl_display_create(); m_sWLDisplay = wl_display_create();
@ -199,102 +227,179 @@ void CCompositor::initServer() {
if (envEnabled("HYPRLAND_TRACE")) if (envEnabled("HYPRLAND_TRACE"))
Debug::trace = true; Debug::trace = true;
wlr_log_init(WLR_INFO, NULL); Aquamarine::SBackendOptions options;
options.logFunction = aqLog;
if (envEnabled("HYPRLAND_LOG_WLR")) std::vector<Aquamarine::SBackendImplementationOptions> implementations;
wlr_log_init(WLR_DEBUG, Debug::wlrLog); Aquamarine::SBackendImplementationOptions option;
else option.backendType = Aquamarine::eBackendType::AQ_BACKEND_HEADLESS;
wlr_log_init(WLR_ERROR, Debug::wlrLog); 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) { if (!m_pAqBackend) {
Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues."); Debug::log(CRIT,
throwError("wlr_backend_autocreate() failed!"); "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; // TODO: headless only
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);
if (isHeadlessOnly) { initAllSignals();
m_sWLRRenderer = wlr_renderer_autocreate(m_sWLRBackend); // TODO: remove this, it's barely needed now.
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 { } else {
m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend); Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
if (m_iDRMFD < 0) { }
Debug::log(CRIT, "Couldn't query the DRM FD!");
throwError("wlr_backend_get_drm_fd() failed!");
} }
m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD); if (m_szWLDisplaySocket.empty()) {
Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto");
const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay);
if (SOCKETSTR)
m_szWLDisplaySocket = SOCKETSTR;
} }
if (!m_sWLRRenderer) { if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues."); Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
throwError("wlr_gles2_renderer_create_with_drm_fd() failed!"); throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
} }
m_sWLRAllocator = wlr_allocator_autocreate(m_sWLRBackend, m_sWLRRenderer); Debug::log(LOG, "Setting WAYLAND_DISPLAY to {}", m_szWLDisplaySocket);
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
if (!m_sWLRAllocator) { setenv("XDG_SESSION_TYPE", "wayland", 1);
Debug::log(CRIT, "m_sWLRAllocator was NULL!");
throwError("wlr_allocator_autocreate() 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!");
}
initManagers(STAGE_BASICINIT); 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); initManagers(STAGE_LATE);
for (auto& o : pendingOutputs) {
onNewMonitor(o);
}
pendingOutputs.clear();
} }
void CCompositor::initAllSignals() { void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend"); m_pAqBackend->events.newOutput.registerStaticListener(
addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend"); [this](void* p, std::any data) {
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer"); 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) m_pAqBackend->events.newPointer.registerStaticListener(
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM"); [](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) m_pAqBackend->events.newKeyboard.registerStaticListener(
addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session"); [](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() { 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() { void CCompositor::cleanEnvironment() {
@ -308,7 +413,7 @@ void CCompositor::cleanEnvironment() {
unsetenv("XDG_BACKEND"); unsetenv("XDG_BACKEND");
unsetenv("XDG_CURRENT_DESKTOP"); unsetenv("XDG_CURRENT_DESKTOP");
if (m_sWLRSession) { if (m_pAqBackend->hasSession()) {
const auto CMD = const auto CMD =
#ifdef USES_SYSTEMD #ifdef USES_SYSTEMD
"systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " "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) { for (auto& m : m_vMonitors) {
g_pHyprOpenGL->destroyMonitorResources(m.get()); g_pHyprOpenGL->destroyMonitorResources(m.get());
wlr_output_state_set_enabled(m->state.wlr(), false); m->output->state->setEnabled(false);
m->state.commit(); m->state.commit();
} }
@ -388,14 +493,8 @@ void CCompositor::cleanup() {
g_pHyprCtl.reset(); g_pHyprCtl.reset();
g_pEventLoopManager.reset(); g_pEventLoopManager.reset();
if (m_sWLRRenderer) if (m_pAqBackend)
wlr_renderer_destroy(m_sWLRRenderer); m_pAqBackend.reset();
if (m_sWLRAllocator)
wlr_allocator_destroy(m_sWLRAllocator);
if (m_sWLRBackend)
wlr_backend_destroy(m_sWLRBackend);
if (m_critSigSource) if (m_critSigSource)
wl_event_source_remove(m_critSigSource); wl_event_source_remove(m_critSigSource);
@ -441,6 +540,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the PointerManager!"); Debug::log(LOG, "Creating the PointerManager!");
g_pPointerManager = std::make_unique<CPointerManager>(); g_pPointerManager = std::make_unique<CPointerManager>();
Debug::log(LOG, "Creating the EventManager!");
g_pEventManager = std::make_unique<CEventManager>();
} break; } break;
case STAGE_BASICINIT: { case STAGE_BASICINIT: {
Debug::log(LOG, "Creating the CHyprOpenGLImpl!"); Debug::log(LOG, "Creating the CHyprOpenGLImpl!");
@ -471,9 +573,6 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the SessionLockManager!"); Debug::log(LOG, "Creating the SessionLockManager!");
g_pSessionLockManager = std::make_unique<CSessionLockManager>(); g_pSessionLockManager = std::make_unique<CSessionLockManager>();
Debug::log(LOG, "Creating the EventManager!");
g_pEventManager = std::make_unique<CEventManager>();
Debug::log(LOG, "Creating the HyprDebugOverlay!"); Debug::log(LOG, "Creating the HyprDebugOverlay!");
g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>(); g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>();
@ -516,26 +615,32 @@ void CCompositor::removeLockFile() {
void CCompositor::prepareFallbackOutput() { void CCompositor::prepareFallbackOutput() {
// create a backup monitor // create a backup monitor
wlr_backend* headless = nullptr; SP<Aquamarine::IBackendImplementation> headless;
wlr_multi_for_each_backend( for (auto& impl : m_pAqBackend->getImplementations()) {
m_sWLRBackend, if (impl->type() == Aquamarine::AQ_BACKEND_HEADLESS) {
[](wlr_backend* b, void* data) { headless = impl;
if (wlr_backend_is_headless(b)) break;
*((wlr_backend**)data) = b; }
}, }
&headless);
if (!headless) { if (!headless) {
Debug::log(WARN, "Unsafe state will be ineffective, no fallback output"); Debug::log(WARN, "No headless in prepareFallbackOutput?!");
return; return;
} }
wlr_headless_add_output(headless, 1920, 1080); headless->createOutput();
} }
void CCompositor::startCompositor() { void CCompositor::startCompositor(std::string socketName, int socketFd) {
initAllSignals(); if (!socketName.empty() && socketFd != -1) {
fcntl(socketFd, F_SETFD, FD_CLOEXEC);
const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
if (RETVAL >= 0) {
m_szWLDisplaySocket = socketName;
Debug::log(LOG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
} else
Debug::log(WARN, "wl_display_add_socket_fd for {} returned {}: skipping", socketName, RETVAL);
} else {
// get socket, avoid using 0 // get socket, avoid using 0
for (int candidate = 1; candidate <= 32; candidate++) { for (int candidate = 1; candidate <= 32; candidate++) {
const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate)); const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
@ -544,7 +649,7 @@ void CCompositor::startCompositor() {
m_szWLDisplaySocket = CANDIDATESTR; m_szWLDisplaySocket = CANDIDATESTR;
Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL); Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
break; break;
} else { } else
Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate); 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()) { if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_szWLDisplaySocket NULL!"); 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)"); 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("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
setenv("XDG_SESSION_TYPE", "wayland", 1);
signal(SIGPIPE, SIG_IGN); 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 = const auto CMD =
#ifdef USES_SYSTEMD #ifdef USES_SYSTEMD
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash " "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); 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(); prepareFallbackOutput();
g_pHyprRenderer->setCursorFromName("left_ptr"); 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}; 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) { for (auto& m : m_vMonitors) {
if (m->output == out) { if (m->output == out) {
return m.get(); return m.get();
@ -882,7 +980,7 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
return nullptr; return nullptr;
} }
CMonitor* CCompositor::getRealMonitorFromOutput(wlr_output* out) { CMonitor* CCompositor::getRealMonitorFromOutput(SP<Aquamarine::IOutput> out) {
for (auto& m : m_vRealMonitors) { for (auto& m : m_vRealMonitors) {
if (m->output == out) { if (m->output == out) {
return m.get(); return m.get();
@ -2230,8 +2328,12 @@ void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMod
g_pInputManager->recheckIdleInhibitorStatus(); g_pInputManager->recheckIdleInhibitorStatus();
// DMAbuf stuff for direct scanout // further updates require a monitor
g_pHyprRenderer->setWindowScanoutMode(pWindow); 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); g_pConfigManager->ensureVRR(PMONITOR);
} }
@ -2272,8 +2374,8 @@ void CCompositor::updateWorkspaceWindowData(const int& id) {
} }
} }
void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) { void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor, IOutput::scheduleFrameReason reason) {
if ((m_sWLRSession && !m_sWLRSession->active) || !m_bSessionActive) if ((m_pAqBackend->hasSession() && !m_pAqBackend->session->active) || !m_bSessionActive)
return; return;
if (!pMonitor->m_bEnabled) if (!pMonitor->m_bEnabled)
@ -2282,7 +2384,7 @@ void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
if (pMonitor->renderingActive) if (pMonitor->renderingActive)
pMonitor->pendingFrame = true; pMonitor->pendingFrame = true;
wlr_output_schedule_frame(pMonitor->output); pMonitor->output->scheduleFrame(reason);
} }
PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) { PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) {
@ -2798,3 +2900,84 @@ PHLWINDOW CCompositor::windowForCPointer(CWindow* pWindow) {
return {}; 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 "plugins/PluginSystem.hpp"
#include "helpers/Watchdog.hpp" #include "helpers/Watchdog.hpp"
#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/output/Output.hpp>
class CWLSurfaceResource; class CWLSurfaceResource;
enum eManagersInitStage { enum eManagersInitStage {
@ -43,22 +46,11 @@ class CCompositor {
CCompositor(); CCompositor();
~CCompositor(); ~CCompositor();
// ------------------ WLR BASICS ------------------ //
wl_display* m_sWLDisplay; wl_display* m_sWLDisplay;
wl_event_loop* m_sWLEventLoop; wl_event_loop* m_sWLEventLoop;
wlr_backend* m_sWLRBackend; int m_iDRMFD = -1;
wlr_session* m_sWLRSession; bool m_bInitialized = false;
wlr_renderer* m_sWLRRenderer; SP<Aquamarine::CBackend> m_pAqBackend;
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;
// ------------------------------------------------- //
std::string m_szHyprTempDataRoot = ""; std::string m_szHyprTempDataRoot = "";
@ -78,7 +70,7 @@ class CCompositor {
std::unordered_map<std::string, uint64_t> m_mMonitorIDMap; std::unordered_map<std::string, uint64_t> m_mMonitorIDMap;
void initServer(); void initServer();
void startCompositor(); void startCompositor(std::string socketName, int socketFd);
void cleanup(); void cleanup();
void createLockFile(); void createLockFile();
void removeLockFile(); void removeLockFile();
@ -95,9 +87,8 @@ class CCompositor {
bool m_bSessionActive = true; bool m_bSessionActive = true;
bool m_bDPMSStateON = true; bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors. bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bNextIsUnsafe = false; // because wlroots bool m_bNextIsUnsafe = false;
CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state CMonitor* m_pUnsafeOutput = nullptr; // fallback output for the unsafe state
bool m_bExitTriggered = false; // For exit dispatcher
bool m_bIsShuttingDown = false; bool m_bIsShuttingDown = false;
// ------------------------------------------------- // // ------------------------------------------------- //
@ -116,8 +107,8 @@ class CCompositor {
SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*); SP<CWLSurfaceResource> vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*);
SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl); SP<CWLSurfaceResource> vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl);
Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>); Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP<CWLSurfaceResource>);
CMonitor* getMonitorFromOutput(wlr_output*); CMonitor* getMonitorFromOutput(SP<Aquamarine::IOutput>);
CMonitor* getRealMonitorFromOutput(wlr_output*); CMonitor* getRealMonitorFromOutput(SP<Aquamarine::IOutput>);
PHLWINDOW getWindowFromSurface(SP<CWLSurfaceResource>); PHLWINDOW getWindowFromSurface(SP<CWLSurfaceResource>);
PHLWINDOW getWindowFromHandle(uint32_t); PHLWINDOW getWindowFromHandle(uint32_t);
bool isWorkspaceVisible(PHLWORKSPACE); bool isWorkspaceVisible(PHLWORKSPACE);
@ -157,7 +148,7 @@ class CCompositor {
void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID); void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID);
void updateFullscreenFadeOnWorkspace(PHLWORKSPACE); void updateFullscreenFadeOnWorkspace(PHLWORKSPACE);
PHLWINDOW getX11Parent(PHLWINDOW); PHLWINDOW getX11Parent(PHLWINDOW);
void scheduleFrameForMonitor(CMonitor*); void scheduleFrameForMonitor(CMonitor*, Aquamarine::IOutput::scheduleFrameReason reason = Aquamarine::IOutput::AQ_SCHEDULE_CLIENT_UNKNOWN);
void addToFadingOutSafe(PHLLS); void addToFadingOutSafe(PHLLS);
void removeFromFadingOutSafe(PHLLS); void removeFromFadingOutSafe(PHLLS);
void addToFadingOutSafe(PHLWINDOW); void addToFadingOutSafe(PHLWINDOW);
@ -182,6 +173,7 @@ class CCompositor {
void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform); void setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform);
void updateSuspendedStates(); void updateSuspendedStates();
PHLWINDOW windowForCPointer(CWindow*); PHLWINDOW windowForCPointer(CWindow*);
void onNewMonitor(SP<Aquamarine::IOutput> output);
std::string explicitConfigPath; std::string explicitConfigPath;

View file

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

View file

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

View file

@ -12,6 +12,8 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
#include <sys/poll.h> #include <sys/poll.h>
#include <filesystem>
#include <ranges>
#include <sstream> #include <sstream>
#include <string> #include <string>
@ -20,6 +22,7 @@
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String; using namespace Hyprutils::String;
#include <aquamarine/input/Input.hpp>
#include "../config/ConfigDataValues.hpp" #include "../config/ConfigDataValues.hpp"
#include "../config/ConfigValue.hpp" #include "../config/ConfigValue.hpp"
@ -53,19 +56,14 @@ static std::string formatToString(uint32_t drmFormat) {
static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFormat format) { static std::string availableModesForOutput(CMonitor* pMonitor, eHyprCtlOutputFormat format) {
std::string result; std::string result;
if (!wl_list_empty(&pMonitor->output->modes)) { for (auto& m : pMonitor->output->modes) {
wlr_output_mode* mode;
wl_list_for_each(mode, &pMonitor->output->modes, link) {
if (format == FORMAT_NORMAL) if (format == FORMAT_NORMAL)
result += std::format("{}x{}@{:.2f}Hz ", mode->width, mode->height, mode->refresh / 1000.0); result += std::format("{}x{}@{:.2f}Hz ", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
else else
result += std::format("\"{}x{}@{:.2f}Hz\",", mode->width, mode->height, mode->refresh / 1000.0); result += std::format("\"{}x{}@{:.2f}Hz\",", m->pixelSize.x, m->pixelSize.y, m->refreshRate / 1000.0);
} }
result.pop_back(); trimTrailingComma(result);
}
return result; return result;
} }
@ -75,6 +73,8 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
if (!m->output || m->ID == -1ull) if (!m->output || m->ID == -1ull)
return ""; return "";
if (format == eHyprCtlOutputFormat::FORMAT_JSON) {
result += std::format( result += std::format(
R"#({{ R"#({{
"id": {}, "id": {},
@ -107,14 +107,27 @@ std::string CHyprCtl::getMonitorData(Hyprutils::Memory::CSharedPointer<CMonitor>
"currentFormat": "{}", "currentFormat": "{}",
"availableModes": [{}] "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->ID, escapeJSONStrings(m->szName), escapeJSONStrings(m->szShortDescription), escapeJSONStrings(m->output->make), escapeJSONStrings(m->output->model),
m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : escapeJSONStrings(m->activeWorkspace->m_szName)), escapeJSONStrings(m->output->serial), (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y,
m->activeSpecialWorkspaceID(), escapeJSONStrings(m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x, 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, (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 == g_pCompositor->m_pLastMonitor ? "yes" : "no"), (int)m->dpmsStatus, m->output->state->state().adaptiveSync, m->tearingState.activelyTearing,
(m->output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED ? "true" : "false"), (m->tearingState.activelyTearing ? "true" : "false"), !m->m_bEnabled, formatToString(m->output->state->state().drmFormat), availableModesForOutput(m.get(), format));
(m->m_bEnabled ? "false" : "true"), formatToString(m->drmFormat), availableModesForOutput(m.get(), format)); }
return result; return result;
} }
@ -144,16 +157,16 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) {
if (!m->output || m->ID == -1ull) if (!m->output || m->ID == -1ull)
continue; continue;
result += std::format( result +=
"Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t" 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" "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", "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->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->output->make, m->output->model, m->output->serial, m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName),
(!m->activeWorkspace ? "" : m->activeWorkspace->m_szName), m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), (int)m->vecReservedTopLeft.x,
(int)m->vecReservedTopLeft.x, (int)m->vecReservedTopLeft.y, (int)m->vecReservedBottomRight.x, (int)m->vecReservedBottomRight.y, m->scale, (int)m->transform, (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 == 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->drmFormat), availableModesForOutput(m.get(), format)); 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} "defaultSpeed": {:.5f}
}},)#", }},)#",
(uintptr_t)m.get(), escapeJSONStrings(m->hlName), (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)) : m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f);
0.f);
} }
trimTrailingComma(result); trimTrailingComma(result);
@ -611,9 +623,8 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
R"#( {{ R"#( {{
"address": "0x{:x}", "address": "0x{:x}",
"type": "tabletTool", "type": "tabletTool",
"belongsTo": "0x{:x}"
}},)#", }},)#",
(uintptr_t)d.get(), d->wlr() ? (uintptr_t)d->wlr()->data : 0); (uintptr_t)d.get());
} }
trimTrailingComma(result); trimTrailingComma(result);
@ -641,7 +652,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
"address": "0x{:x}", "address": "0x{:x}",
"name": "{}" "name": "{}"
}},)#", }},)#",
(uintptr_t)&d, escapeJSONStrings(d.pWlrDevice ? d.pWlrDevice->name : "")); (uintptr_t)&d, escapeJSONStrings(d.pDevice ? d.pDevice->getName() : ""));
} }
trimTrailingComma(result); trimTrailingComma(result);
@ -654,9 +665,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
for (auto& m : g_pInputManager->m_vPointers) { 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, 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) ? (m->aq() && m->aq()->getLibinputHandle() ? libinput_device_config_accel_get_default_speed(m->aq()->getLibinputHandle()) : 0.f));
libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base)) :
0.f));
} }
result += "\n\nKeyboards:\n"; result += "\n\nKeyboards:\n";
@ -675,11 +684,11 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
} }
for (auto& d : g_pInputManager->m_vTablets) { 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) { 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"; result += "\n\nTouch:\n";
@ -691,7 +700,7 @@ std::string devicesRequest(eHyprCtlOutputFormat format, std::string request) {
result += "\n\nSwitches:\n"; result += "\n\nSwitches:\n";
for (auto& d : g_pInputManager->m_lSwitches) { 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()) if (PKEYBOARD == g_pInputManager->m_vKeyboards.end())
return "device not found"; return "device not found";
const auto PWLRKEYBOARD = (*PKEYBOARD)->wlr(); const auto KEEB = *PKEYBOARD;
const auto LAYOUTS = xkb_keymap_num_layouts(PWLRKEYBOARD->keymap);
const auto LAYOUTS = xkb_keymap_num_layouts(KEEB->xkbKeymap);
xkb_layout_index_t activeLayout = 0; xkb_layout_index_t activeLayout = 0;
while (activeLayout < LAYOUTS) { 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; break;
activeLayout++; activeLayout++;
} }
if (CMD == "next") { if (CMD == "next")
wlr_keyboard_notify_modifiers(PWLRKEYBOARD, PWLRKEYBOARD->modifiers.depressed, PWLRKEYBOARD->modifiers.latched, PWLRKEYBOARD->modifiers.locked, KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout > LAYOUTS ? 0 : activeLayout + 1);
activeLayout > LAYOUTS ? 0 : activeLayout + 1); else if (CMD == "prev")
} else if (CMD == "prev") { KEEB->updateModifiers(KEEB->modifiersState.depressed, KEEB->modifiersState.latched, KEEB->modifiersState.locked, activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
wlr_keyboard_notify_modifiers(PWLRKEYBOARD, PWLRKEYBOARD->modifiers.depressed, PWLRKEYBOARD->modifiers.latched, PWLRKEYBOARD->modifiers.locked, else {
activeLayout == 0 ? LAYOUTS - 1 : activeLayout - 1);
} else {
int requestedLayout = 0; int requestedLayout = 0;
try { try {
requestedLayout = std::stoi(CMD); 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); 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"; return "ok";
@ -1368,43 +1375,6 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request)
return result; 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) { std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
CVarList vars(request, 0, ' '); CVarList vars(request, 0, ' ');
@ -1413,15 +1383,36 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
const auto MODE = vars[1]; 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 (MODE == "create" || MODE == "add") {
if (g_pCompositor->getMonitorFromName(vars[3])) if (g_pCompositor->getMonitorFromName(vars[3]))
return "A real monitor already uses that name."; 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"; return "no backend replied to the request";
} else if (MODE == "destroy" || MODE == "remove") { } else if (MODE == "destroy" || MODE == "remove") {
@ -1433,7 +1424,7 @@ std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) {
if (!PMONITOR->createdByUser) if (!PMONITOR->createdByUser)
return "cannot remove a real display. Use the monitor keyword."; return "cannot remove a real display. Use the monitor keyword.";
wlr_output_destroy(PMONITOR->output); PMONITOR->output->destroy();
} }
return "ok"; return "ok";

View file

@ -10,30 +10,6 @@ void Debug::init(const std::string& IS) {
logFile = IS + (ISDEBUG ? "/hyprlandd.log" : "/hyprland.log"); 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) { void Debug::log(LogLevel level, std::string str) {
if (level == TRACE && !trace) if (level == TRACE && !trace)
return; return;

View file

@ -67,6 +67,4 @@ namespace Debug {
log(level, logMsg); 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) { 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); Debug::log(LOG, "New popup at {:x}", (uintptr_t)POPUP);
} }
@ -250,7 +250,8 @@ void CPopup::recheckTree() {
} }
void CPopup::recheckChildrenRecursive() { void CPopup::recheckChildrenRecursive() {
for (auto& c : m_vChildren) { auto cpy = m_vChildren;
for (auto& c : cpy) {
c->onCommit(true); c->onCommit(true);
c->recheckChildrenRecursive(); c->recheckChildrenRecursive();
} }

View file

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

View file

@ -1155,7 +1155,8 @@ int CWindow::getRealBorderSize() {
} }
bool CWindow::canBeTorn() { 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() { bool CWindow::shouldSendFullscreenState() {

View file

@ -2,7 +2,21 @@
#include "../defines.hpp" #include "../defines.hpp"
#include "../helpers/varlist/VarList.hpp" #include "../helpers/varlist/VarList.hpp"
#include "../managers/input/InputManager.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() { uint32_t IKeyboard::getCapabilities() {
return HID_INPUT_CAPABILITY_KEYBOARD; return HID_INPUT_CAPABILITY_KEYBOARD;
} }
@ -14,27 +28,149 @@ eHIDType IKeyboard::getType() {
IKeyboard::~IKeyboard() { IKeyboard::~IKeyboard() {
events.destroy.emit(); events.destroy.emit();
if (!xkbTranslationState) clearManuallyAllocd();
return; }
xkb_state_unref(xkbTranslationState); void IKeyboard::clearManuallyAllocd() {
xkbTranslationState = nullptr; 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) { void IKeyboard::updateXKBTranslationState(xkb_keymap* const keymap) {
if (xkbTranslationState) if (xkbState)
xkb_state_unref(xkbTranslationState); xkb_state_unref(xkbState);
xkbState = nullptr;
if (keymap) { if (keymap) {
Debug::log(LOG, "Updating keyboard {:x}'s translation state from a provided keymap", (uintptr_t)this); 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; return;
} }
const auto WLRKB = wlr(); const auto KEYMAP = xkbKeymap;
const auto KEYMAP = WLRKB->keymap; const auto STATE = xkbState;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
const auto PCONTEXT = xkb_context_new(XKB_CONTEXT_NO_FLAGS); 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); 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_keymap_unref(KEYMAP);
xkb_context_unref(PCONTEXT); 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); 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_keymap_unref(NEWKEYMAP);
xkb_context_unref(PCONTEXT); xkb_context_unref(PCONTEXT);
} }
std::string IKeyboard::getActiveLayout() { std::string IKeyboard::getActiveLayout() {
const auto WLRKB = wlr(); const auto KEYMAP = xkbKeymap;
const auto KEYMAP = WLRKB->keymap; const auto STATE = xkbState;
const auto STATE = WLRKB->xkb_state;
const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP); const auto LAYOUTSNUM = xkb_keymap_num_layouts(KEYMAP);
for (uint32_t i = 0; i < LAYOUTSNUM; ++i) { for (uint32_t i = 0; i < LAYOUTSNUM; ++i) {
@ -120,14 +255,12 @@ std::string IKeyboard::getActiveLayout() {
} }
void IKeyboard::updateLEDs() { void IKeyboard::updateLEDs() {
auto keyboard = wlr(); if (xkbState == nullptr)
if (!keyboard || keyboard->xkb_state == nullptr)
return; return;
uint32_t leds = 0; uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { for (uint32_t i = 0; i < LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i])) if (xkb_state_led_index_is_active(xkbState, ledIndexes.at(i)))
leds |= (1 << i); leds |= (1 << i);
} }
@ -135,13 +268,88 @@ void IKeyboard::updateLEDs() {
} }
void IKeyboard::updateLEDs(uint32_t leds) { void IKeyboard::updateLEDs(uint32_t leds) {
auto keyboard = wlr(); if (!xkbState)
if (!keyboard || keyboard->xkb_state == nullptr)
return; return;
if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock())) if (isVirtual() && g_pInputManager->shouldIgnoreVirtualKeyboard(self.lock()))
return; 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,7 +7,18 @@
#include <xkbcommon/xkbcommon.h> #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 { class IKeyboard : public IHID {
public: public:
@ -15,7 +26,7 @@ class IKeyboard : public IHID {
virtual uint32_t getCapabilities(); virtual uint32_t getCapabilities();
virtual eHIDType getType(); virtual eHIDType getType();
virtual bool isVirtual() = 0; virtual bool isVirtual() = 0;
virtual wlr_keyboard* wlr() = 0; virtual SP<Aquamarine::IKeyboard> aq() = 0;
struct SKeyEvent { struct SKeyEvent {
uint32_t timeMs = 0; uint32_t timeMs = 0;
@ -24,6 +35,17 @@ class IKeyboard : public IHID {
wl_keyboard_key_state state = WL_KEYBOARD_KEY_STATE_PRESSED; 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 { struct {
CSignal key; CSignal key;
CSignal modifiers; CSignal modifiers;
@ -39,19 +61,35 @@ class IKeyboard : public IHID {
std::string rules = ""; std::string rules = "";
}; };
void setKeymap(const SStringRuleNames& rules);
void updateXKBTranslationState(xkb_keymap* const keymap = nullptr); void updateXKBTranslationState(xkb_keymap* const keymap = nullptr);
std::string getActiveLayout(); std::string getActiveLayout();
void updateLEDs(); void updateLEDs();
void updateLEDs(uint32_t leds); 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 active = false;
bool enabled = true; bool enabled = true;
xkb_layout_index_t activeLayout = 0; xkb_layout_index_t activeLayout = 0;
xkb_state* xkbTranslationState = nullptr; xkb_state * xkbState = nullptr, *xkbStaticState /* Static state: never gets modifiers or layout changes sent, used for keybinds. */ = nullptr;
xkb_keymap* xkbKeymap = nullptr;
struct {
uint32_t depressed = 0, latched = 0, locked = 0, group = 0;
} modifiersState;
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;
std::string hlName = ""; std::string hlName = "";
std::string xkbFilePath = ""; std::string xkbFilePath = "";
std::string xkbKeymapString = "";
int xkbKeymapFD = -1;
SStringRuleNames currentRules; SStringRuleNames currentRules;
int repeatRate = 0; int repeatRate = 0;
@ -60,4 +98,9 @@ class IKeyboard : public IHID {
bool resolveBindsBySym = false; bool resolveBindsBySym = false;
WP<IKeyboard> self; WP<IKeyboard> self;
private:
void clearManuallyAllocd();
std::vector<uint32_t> pressedXKB;
}; };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,9 +6,9 @@
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"
struct wlr_tablet; AQUAMARINE_FORWARD(ITablet);
struct wlr_tablet_tool; AQUAMARINE_FORWARD(ITabletTool);
struct wlr_tablet_pad; AQUAMARINE_FORWARD(ITabletPad);
class CTabletTool; class CTabletTool;
class CTabletPad; class CTabletPad;
@ -21,13 +21,12 @@ class CWLSurfaceResource;
*/ */
class CTablet : public IHID { class CTablet : public IHID {
public: public:
static SP<CTablet> create(wlr_tablet* tablet); static SP<CTablet> create(SP<Aquamarine::ITablet> tablet);
static SP<CTablet> fromWlr(wlr_tablet* tablet);
~CTablet(); ~CTablet();
virtual uint32_t getCapabilities(); virtual uint32_t getCapabilities();
virtual eHIDType getType(); virtual eHIDType getType();
wlr_tablet* wlr(); SP<Aquamarine::ITablet> aq();
enum eTabletToolAxes { enum eTabletToolAxes {
HID_TABLET_TOOL_AXIS_X = (1 << 0), HID_TABLET_TOOL_AXIS_X = (1 << 0),
@ -42,7 +41,7 @@ class CTablet : public IHID {
}; };
struct SAxisEvent { struct SAxisEvent {
wlr_tablet_tool* tool; SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet; SP<CTablet> tablet;
uint32_t timeMs = 0; uint32_t timeMs = 0;
@ -58,7 +57,7 @@ class CTablet : public IHID {
}; };
struct SProximityEvent { struct SProximityEvent {
wlr_tablet_tool* tool; SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet; SP<CTablet> tablet;
uint32_t timeMs = 0; uint32_t timeMs = 0;
@ -67,7 +66,7 @@ class CTablet : public IHID {
}; };
struct STipEvent { struct STipEvent {
wlr_tablet_tool* tool; SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet; SP<CTablet> tablet;
uint32_t timeMs = 0; uint32_t timeMs = 0;
@ -76,7 +75,7 @@ class CTablet : public IHID {
}; };
struct SButtonEvent { struct SButtonEvent {
wlr_tablet_tool* tool; SP<Aquamarine::ITabletTool> tool;
SP<CTablet> tablet; SP<CTablet> tablet;
uint32_t timeMs = 0; uint32_t timeMs = 0;
@ -100,27 +99,27 @@ class CTablet : public IHID {
CBox boundBox; // output-local CBox boundBox; // output-local
private: private:
CTablet(wlr_tablet* tablet); CTablet(SP<Aquamarine::ITablet> tablet);
void disconnectCallbacks(); WP<Aquamarine::ITablet> tablet;
wlr_tablet* tablet = nullptr; struct {
CHyprSignalListener destroy;
DYNLISTENER(destroy); CHyprSignalListener axis;
DYNLISTENER(axis); CHyprSignalListener proximity;
DYNLISTENER(proximity); CHyprSignalListener tip;
DYNLISTENER(tip); CHyprSignalListener button;
DYNLISTENER(button); } listeners;
}; };
class CTabletPad : public IHID { class CTabletPad : public IHID {
public: public:
static SP<CTabletPad> create(wlr_tablet_pad* pad); static SP<CTabletPad> create(SP<Aquamarine::ITabletPad> pad);
~CTabletPad(); ~CTabletPad();
virtual uint32_t getCapabilities(); virtual uint32_t getCapabilities();
virtual eHIDType getType(); virtual eHIDType getType();
wlr_tablet_pad* wlr(); SP<Aquamarine::ITabletPad> aq();
struct SButtonEvent { struct SButtonEvent {
uint32_t timeMs = 0; uint32_t timeMs = 0;
@ -159,23 +158,22 @@ class CTabletPad : public IHID {
std::string hlName; std::string hlName;
private: private:
CTabletPad(wlr_tablet_pad* pad); CTabletPad(SP<Aquamarine::ITabletPad> pad);
void disconnectCallbacks(); WP<Aquamarine::ITabletPad> pad;
wlr_tablet_pad* pad = nullptr; struct {
CHyprSignalListener destroy;
DYNLISTENER(destroy); CHyprSignalListener ring;
DYNLISTENER(ring); CHyprSignalListener strip;
DYNLISTENER(strip); CHyprSignalListener button;
DYNLISTENER(button); CHyprSignalListener attach;
DYNLISTENER(attach); } listeners;
}; };
class CTabletTool : public IHID { class CTabletTool : public IHID {
public: public:
static SP<CTabletTool> create(wlr_tablet_tool* tool); static SP<CTabletTool> create(SP<Aquamarine::ITabletTool> tool);
static SP<CTabletTool> fromWlr(wlr_tablet_tool* tool);
~CTabletTool(); ~CTabletTool();
enum eTabletToolType { enum eTabletToolType {
@ -199,7 +197,7 @@ class CTabletTool : public IHID {
}; };
virtual uint32_t getCapabilities(); virtual uint32_t getCapabilities();
wlr_tablet_tool* wlr(); SP<Aquamarine::ITabletTool> aq();
virtual eHIDType getType(); virtual eHIDType getType();
SP<CWLSurfaceResource> getSurface(); SP<CWLSurfaceResource> getSurface();
void setSurface(SP<CWLSurfaceResource>); void setSurface(SP<CWLSurfaceResource>);
@ -216,17 +214,13 @@ class CTabletTool : public IHID {
std::string hlName; std::string hlName;
private: private:
CTabletTool(wlr_tablet_tool* tool); CTabletTool(SP<Aquamarine::ITabletTool> tool);
void disconnectCallbacks();
WP<CWLSurfaceResource> pSurface; WP<CWLSurfaceResource> pSurface;
WP<Aquamarine::ITabletTool> tool;
wlr_tablet_tool* tool = nullptr;
DYNLISTENER(destroy);
struct { struct {
CHyprSignalListener destroySurface; CHyprSignalListener destroySurface;
CHyprSignalListener destroyTool;
} listeners; } listeners;
}; };

View file

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

View file

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

View file

@ -14,58 +14,37 @@ CVirtualKeyboard::CVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keeb_) : keybo
if (!keeb_) if (!keeb_)
return; return;
auto keeb = keeb_->wlr(); listeners.destroy = keeb_->events.destroy.registerListener([this](std::any d) {
// clang-format off
hyprListener_destroy.initCallback(&keeb->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
keyboard.reset(); keyboard.reset();
events.destroy.emit(); events.destroy.emit();
}, this, "CVirtualKeyboard");
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,
}); });
}, this, "CVirtualKeyboard");
hyprListener_keymap.initCallback(&keeb->events.keymap, [this] (void* owner, void* data) { listeners.key = keeb_->events.key.registerListener([this](std::any d) { keyboardEvents.key.emit(d); });
keyboardEvents.keymap.emit(); listeners.modifiers = keeb_->events.modifiers.registerListener([this](std::any d) {
}, this, "CVirtualKeyboard"); 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,
});
});
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_modifiers.initCallback(&keeb->events.modifiers, [this] (void* owner, void* data) { deviceName = keeb_->name;
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";
} }
bool CVirtualKeyboard::isVirtual() { bool CVirtualKeyboard::isVirtual() {
return true; return true;
} }
wlr_keyboard* CVirtualKeyboard::wlr() { SP<Aquamarine::IKeyboard> CVirtualKeyboard::aq() {
if (keyboard.expired())
return nullptr; return nullptr;
return keyboard->wlr();
}
void CVirtualKeyboard::disconnectCallbacks() {
hyprListener_destroy.removeCallback();
hyprListener_key.removeCallback();
hyprListener_keymap.removeCallback();
hyprListener_repeatInfo.removeCallback();
hyprListener_modifiers.removeCallback();
} }
wl_client* CVirtualKeyboard::getClient() { wl_client* CVirtualKeyboard::getClient() {

View file

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

View file

@ -1,5 +1,6 @@
#include "VirtualPointer.hpp" #include "VirtualPointer.hpp"
#include "../protocols/VirtualPointer.hpp" #include "../protocols/VirtualPointer.hpp"
#include <aquamarine/input/Input.hpp>
SP<CVirtualPointer> CVirtualPointer::create(SP<CVirtualPointerV1Resource> resource) { SP<CVirtualPointer> CVirtualPointer::create(SP<CVirtualPointerV1Resource> resource) {
SP<CVirtualPointer> pPointer = SP<CVirtualPointer>(new CVirtualPointer(resource)); SP<CVirtualPointer> pPointer = SP<CVirtualPointer>(new CVirtualPointer(resource));
@ -13,165 +14,32 @@ CVirtualPointer::CVirtualPointer(SP<CVirtualPointerV1Resource> resource) : point
if (!resource->good()) if (!resource->good())
return; return;
auto mouse = resource->wlr(); listeners.destroy = pointer->events.destroy.registerListener([this](std::any d) {
pointer.reset();
// clang-format off
hyprListener_destroy.initCallback(&mouse->base.events.destroy, [this] (void* owner, void* data) {
disconnectCallbacks();
events.destroy.emit(); events.destroy.emit();
}, this, "CVirtualPointer");
hyprListener_motion.initCallback(&mouse->events.motion, [this] (void* owner, void* data) {
auto E = (wlr_pointer_motion_event*)data;
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) { listeners.motion = pointer->events.move.registerListener([this](std::any d) { pointerEvents.motion.emit(d); });
auto E = (wlr_pointer_motion_absolute_event*)data; 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.motionAbsolute.emit(SMotionAbsoluteEvent{ deviceName = pointer->name;
.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";
} }
bool CVirtualPointer::isVirtual() { bool CVirtualPointer::isVirtual() {
return true; return true;
} }
void CVirtualPointer::disconnectCallbacks() { SP<Aquamarine::IPointer> CVirtualPointer::aq() {
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 nullptr;
return pointer->wlr();
} }

View file

@ -9,30 +9,31 @@ class CVirtualPointer : public IPointer {
static SP<CVirtualPointer> create(SP<CVirtualPointerV1Resource> resource); static SP<CVirtualPointer> create(SP<CVirtualPointerV1Resource> resource);
virtual bool isVirtual(); virtual bool isVirtual();
virtual wlr_pointer* wlr(); virtual SP<Aquamarine::IPointer> aq();
private: private:
CVirtualPointer(SP<CVirtualPointerV1Resource>); CVirtualPointer(SP<CVirtualPointerV1Resource>);
WP<CVirtualPointerV1Resource> pointer; WP<CVirtualPointerV1Resource> pointer;
void disconnectCallbacks(); struct {
CHyprSignalListener destroy;
DYNLISTENER(destroy); CHyprSignalListener motion;
DYNLISTENER(motion); CHyprSignalListener motionAbsolute;
DYNLISTENER(motionAbsolute); CHyprSignalListener button;
DYNLISTENER(button); CHyprSignalListener axis;
DYNLISTENER(axis); CHyprSignalListener frame;
DYNLISTENER(frame);
DYNLISTENER(swipeBegin); CHyprSignalListener swipeBegin;
DYNLISTENER(swipeEnd); CHyprSignalListener swipeEnd;
DYNLISTENER(swipeUpdate); CHyprSignalListener swipeUpdate;
DYNLISTENER(pinchBegin); CHyprSignalListener pinchBegin;
DYNLISTENER(pinchEnd); CHyprSignalListener pinchEnd;
DYNLISTENER(pinchUpdate); CHyprSignalListener pinchUpdate;
DYNLISTENER(holdBegin); CHyprSignalListener holdBegin;
DYNLISTENER(holdEnd); 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 { namespace Events {
// Monitor events
LISTENER(change);
LISTENER(newOutput);
// DRM events
LISTENER(leaseRequest);
// Layer events
LISTENER(newLayerSurface);
// Window events // Window events
DYNLISTENFUNC(commitWindow); DYNLISTENFUNC(commitWindow);
DYNLISTENFUNC(mapWindow); DYNLISTENFUNC(mapWindow);
@ -35,15 +25,6 @@ namespace Events {
DYNLISTENFUNC(setOverrideRedirect); DYNLISTENFUNC(setOverrideRedirect);
DYNLISTENFUNC(ackConfigure); DYNLISTENFUNC(ackConfigure);
LISTENER(newInput);
// Virt Ptr
LISTENER(newVirtPtr);
// Various
LISTENER(requestSetSel);
LISTENER(requestSetPrimarySel);
// Monitor part 2 the sequel // Monitor part 2 the sequel
DYNLISTENFUNC(monitorFrame); DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy); DYNLISTENFUNC(monitorDestroy);
@ -52,16 +33,4 @@ namespace Events {
DYNLISTENFUNC(monitorNeedsFrame); DYNLISTENFUNC(monitorNeedsFrame);
DYNLISTENFUNC(monitorCommit); DYNLISTENFUNC(monitorCommit);
DYNLISTENFUNC(monitorBind); 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 "Events.hpp"
#include "../debug/HyprCtl.hpp" #include "../debug/HyprCtl.hpp"
#include "../config/ConfigValue.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) { 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; 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!"); 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) { 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) { void Events::listener_monitorDestroy(void* owner, void* data) {
const auto OUTPUT = (wlr_output*)data; CMonitor* pMonitor = (CMonitor*)owner;
CMonitor* pMonitor = nullptr;
for (auto& m : g_pCompositor->m_vRealMonitors) { for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->output == OUTPUT) { if (m->output == pMonitor->output) {
pMonitor = m.get(); pMonitor = m.get();
break; break;
} }
@ -188,9 +98,6 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
Debug::log(LOG, "Destroy called for monitor {}", pMonitor->output->name); 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->onDisconnect(true);
pMonitor->output = nullptr; 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; }); 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) { void Events::listener_monitorNeedsFrame(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner; 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) { void Events::listener_monitorCommit(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner; const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_commit*)data; if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR);
if (E->state->committed & WLR_OUTPUT_STATE_BUFFER) { g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR);
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
g_pProtocolManager->m_pToplevelExportProtocolManager->onOutputCommit(PMONITOR, E);
} }
} }

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 "../includes.hpp"
#include "debug/Log.hpp" #include "debug/Log.hpp"
#include "../macros.hpp" #include "../macros.hpp"
#include <xf86drm.h>
#include <drm_fourcc.h>
/* /*
DRM formats are LE, while OGL is BE. The two primary formats DRM formats are LE, while OGL is BE. The two primary formats
@ -309,3 +311,17 @@ uint32_t FormatUtils::glFormatToType(uint32_t gl) {
#endif #endif
GL_UNSIGNED_BYTE; 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 #pragma once
#include <cstdint> #include <cstdint>
#include <string>
#include "math/Math.hpp" #include "math/Math.hpp"
#include <aquamarine/backend/Misc.hpp>
typedef uint32_t DRMFormat; typedef uint32_t DRMFormat;
typedef uint32_t SHMFormat; typedef uint32_t SHMFormat;
@ -18,10 +20,7 @@ struct SPixelFormat {
Vector2D blockSize; Vector2D blockSize;
}; };
struct SDRMFormat { typedef Aquamarine::SDRMFormat SDRMFormat;
uint32_t format = 0;
std::vector<uint64_t> mods;
};
namespace FormatUtils { namespace FormatUtils {
SHMFormat drmToShm(DRMFormat drm); SHMFormat drmToShm(DRMFormat drm);
@ -34,4 +33,6 @@ namespace FormatUtils {
int minStride(const SPixelFormat* const fmt, int32_t width); int minStride(const SPixelFormat* const fmt, int32_t width);
uint32_t drmFormatToGL(DRMFormat drm); uint32_t drmFormatToGL(DRMFormat drm);
uint32_t glFormatToType(uint32_t gl); 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 "../Compositor.hpp"
#include "../managers/TokenManager.hpp" #include "../managers/TokenManager.hpp"
#include <optional> #include <optional>
#include <cstring>
#include <cmath>
#include <set> #include <set>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>

View file

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

View file

@ -1,12 +1,18 @@
#include "Monitor.hpp" #include "Monitor.hpp"
#include "MiscFunctions.hpp" #include "MiscFunctions.hpp"
#include "math/Math.hpp"
#include "../Compositor.hpp" #include "../Compositor.hpp"
#include "../config/ConfigValue.hpp" #include "../config/ConfigValue.hpp"
#include "../protocols/GammaControl.hpp" #include "../protocols/GammaControl.hpp"
#include "../devices/ITouch.hpp" #include "../devices/ITouch.hpp"
#include "../protocols/LayerShell.hpp" #include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp" #include "../protocols/PresentationTime.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/core/Output.hpp"
#include "../managers/PointerManager.hpp" #include "../managers/PointerManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "sync/SyncTimeline.hpp"
#include <aquamarine/output/Output.hpp>
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
using namespace Hyprutils::String; using namespace Hyprutils::String;
@ -21,62 +27,73 @@ CMonitor::CMonitor() : state(this) {
} }
CMonitor::~CMonitor() { 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(); events.destroy.emit();
} }
static void onPresented(void* owner, void* data) { void CMonitor::onConnect(bool noRule) {
const auto PMONITOR = (CMonitor*)owner;
auto E = (wlr_output_event_present*)data;
PROTO::presentation->onPresented(PMONITOR, E->when, E->refresh, E->seq, E->flags); if (output->supportsExplicit) {
inTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
outTimeline = CSyncTimeline::create(output->getBackend()->drmFD());
} }
void CMonitor::onConnect(bool noRule) { listeners.frame = output->events.frame.registerListener([this](std::any d) { Events::listener_monitorFrame(this, nullptr); });
hyprListener_monitorDestroy.removeCallback(); listeners.destroy = output->events.destroy.registerListener([this](std::any d) { Events::listener_monitorDestroy(this, nullptr); });
hyprListener_monitorFrame.removeCallback(); listeners.commit = output->events.commit.registerListener([this](std::any d) { Events::listener_monitorCommit(this, nullptr); });
hyprListener_monitorStateRequest.removeCallback(); listeners.needsFrame =
hyprListener_monitorDamage.removeCallback(); output->events.needsFrame.registerListener([this](std::any d) { g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); });
hyprListener_monitorNeedsFrame.removeCallback(); listeners.presented = output->events.present.registerListener([this](std::any d) {
hyprListener_monitorCommit.removeCallback(); auto E = std::any_cast<Aquamarine::IOutput::SPresentEvent>(d);
hyprListener_monitorBind.removeCallback(); PROTO::presentation->onPresented(this, E.when, E.refresh, E.seq, E.flags);
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 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) { if (m_bEnabled) {
wlr_output_state_set_enabled(state.wlr(), true); output->state->setEnabled(true);
state.commit(); state.commit();
return; return;
} }
szName = output->name; 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 // remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description
std::erase(szDescription, ','); std::erase(szDescription, ',');
// field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix // 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, ','); std::erase(szShortDescription, ',');
if (!wlr_backend_is_drm(output->backend)) if (output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM)
createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable createdByUser = true; // should be true. WL and Headless backends should be addable / removable
// get monitor rule that matches // get monitor rule that matches
SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this); SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this);
@ -84,54 +101,23 @@ void CMonitor::onConnect(bool noRule) {
// if it's disabled, disable and ignore // if it's disabled, disable and ignore
if (monitorRule.disabled) { if (monitorRule.disabled) {
wlr_output_state_set_scale(state.wlr(), 1); output->state->setEnabled(false);
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);
if (!state.commit()) if (!state.commit())
Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name); Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false; m_bEnabled = false;
hyprListener_monitorFrame.removeCallback(); listeners.frame.reset();
return; return;
} }
if (output->non_desktop) { if (output->nonDesktop) {
Debug::log(LOG, "Not configuring non-desktop output"); Debug::log(LOG, "Not configuring non-desktop output");
if (g_pCompositor->m_sWRLDRMLeaseMgr) { if (PROTO::lease)
wlr_drm_lease_v1_manager_offer_output(g_pCompositor->m_sWRLDRMLeaseMgr, output); PROTO::lease->offer(self.lock());
}
return;
}
if (!m_bRenderingInitPassed) { return;
output->allocator = nullptr;
output->renderer = nullptr;
wlr_output_init_render(output, g_pCompositor->m_sWLRAllocator, g_pCompositor->m_sWLRRenderer);
m_bRenderingInitPassed = true;
} }
SP<CMonitor>* thisWrapper = nullptr; SP<CMonitor>* thisWrapper = nullptr;
@ -151,14 +137,14 @@ void CMonitor::onConnect(bool noRule) {
m_bEnabled = true; m_bEnabled = true;
wlr_output_state_set_enabled(state.wlr(), 1); output->state->setEnabled(true);
// set mode, also applies // set mode, also applies
if (!noRule) if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true); g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
if (!state.commit()) 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); damage.setSize(vecTransformedSize);
@ -214,7 +200,7 @@ void CMonitor::onConnect(bool noRule) {
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this); 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); PROTO::gamma->applyGammaToState(this);
@ -261,12 +247,10 @@ void CMonitor::onDisconnect(bool destroy) {
g_pConfigManager->m_bWantsMonitorReload = true; g_pConfigManager->m_bWantsMonitorReload = true;
} }
hyprListener_monitorFrame.removeCallback(); listeners.frame.reset();
hyprListener_monitorPresented.removeCallback(); listeners.presented.reset();
hyprListener_monitorDamage.removeCallback(); listeners.needsFrame.reset();
hyprListener_monitorNeedsFrame.removeCallback(); listeners.commit.reset();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
for (size_t i = 0; i < 4; ++i) { for (size_t i = 0; i < 4; ++i) {
for (auto& ls : m_aLayerSurfaceLayers[i]) { for (auto& ls : m_aLayerSurfaceLayers[i]) {
@ -316,10 +300,10 @@ void CMonitor::onDisconnect(bool destroy) {
activeWorkspace->m_bVisible = false; activeWorkspace->m_bVisible = false;
activeWorkspace.reset(); activeWorkspace.reset();
wlr_output_state_set_enabled(state.wlr(), false); output->state->setEnabled(false);
if (!state.commit()) 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) if (g_pCompositor->m_pLastMonitor.get() == this)
g_pCompositor->setActiveMonitor(BACKUPMON ? BACKUPMON : g_pCompositor->m_pUnsafeOutput); 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"); static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
damage.damageEntire(); damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(this); g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
} else if (damage.damage(rg)) } else if (damage.damage(rg))
g_pCompositor->scheduleFrameForMonitor(this); g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
} }
void CMonitor::addDamage(const CRegion* rg) { 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"); static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) { if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
damage.damageEntire(); damage.damageEntire();
g_pCompositor->scheduleFrameForMonitor(this); g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
} }
if (damage.damage(*box)) if (damage.damage(*box))
g_pCompositor->scheduleFrameForMonitor(this); g_pCompositor->scheduleFrameForMonitor(this, Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
} }
bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() { bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
@ -369,8 +353,8 @@ bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate"); static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate");
// skip scheduling extra frames for fullsreen apps with vrr // 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 && bool shouldSkip =
activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL; *PNOBREAK && output->state->state().adaptiveSync && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow && activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL;
// keep requested minimum refresh rate // keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) { if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) {
@ -563,7 +547,7 @@ float CMonitor::getDefaultScale() {
static constexpr double MMPERINCH = 25.4; static constexpr double MMPERINCH = 25.4;
const auto DIAGONALPX = sqrt(pow(vecPixelSize.x, 2) + pow(vecPixelSize.y, 2)); 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; const auto PPI = DIAGONALPX / DIAGONALIN;
@ -767,11 +751,11 @@ Vector2D CMonitor::middle() {
} }
void CMonitor::updateMatrix() { void CMonitor::updateMatrix() {
wlr_matrix_identity(projMatrix.data()); matrixIdentity(projMatrix.data());
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) { if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
wlr_matrix_translate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0); matrixTranslate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
wlr_matrix_transform(projMatrix.data(), transform); matrixTransform(projMatrix.data(), wlTransformToHyprutils(transform));
wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0); matrixTranslate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
} }
} }
@ -787,31 +771,124 @@ CBox CMonitor::logicalBox() {
return {vecPosition, vecSize}; 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) { CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner; m_pOwner = owner;
wlr_output_state_init(&m_state);
} }
CMonitorState::~CMonitorState() { CMonitorState::~CMonitorState() {
wlr_output_state_finish(&m_state); ;
} }
wlr_output_state* CMonitorState::wlr() { void CMonitorState::ensureBufferPresent() {
return &m_state; if (!m_pOwner->output->state->state().enabled) {
Debug::log(TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled");
return;
} }
void CMonitorState::clear() { if (m_pOwner->output->state->state().buffer)
wlr_output_state_finish(&m_state); return;
m_state = {0};
wlr_output_state_init(&m_state); // 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 CMonitorState::commit() {
bool ret = wlr_output_commit_state(m_pOwner->output, &m_state); if (!updateSwapchain())
clear(); return false;
ensureBufferPresent();
bool ret = m_pOwner->output->commit();
return ret; return ret;
} }
bool CMonitorState::test() { 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 <optional>
#include "signal/Signal.hpp" #include "signal/Signal.hpp"
#include "DamageRing.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 for the different types of auto directions, e.g. auto-left, auto-up.
enum eAutoDirs { enum eAutoDirs {
@ -38,21 +40,20 @@ struct SMonitorRule {
}; };
class CMonitor; class CMonitor;
class CSyncTimeline;
// Class for wrapping the wlr state
class CMonitorState { class CMonitorState {
public: public:
CMonitorState(CMonitor* owner); CMonitorState(CMonitor* owner);
~CMonitorState(); ~CMonitorState();
wlr_output_state* wlr();
void clear();
// commit() will also clear()
bool commit(); bool commit();
bool test(); bool test();
bool updateSwapchain();
private: private:
wlr_output_state m_state = {0}; void ensureBufferPresent();
CMonitor* m_pOwner; CMonitor* m_pOwner;
}; };
@ -87,7 +88,7 @@ class CMonitor {
CMonitorState state; CMonitorState state;
CDamageRing damage; CDamageRing damage;
wlr_output* output = nullptr; SP<Aquamarine::IOutput> output;
float refreshRate = 60; float refreshRate = 60;
int framesToSkip = 0; int framesToSkip = 0;
int forceFullFrames = 0; int forceFullFrames = 0;
@ -97,13 +98,13 @@ class CMonitor {
float xwaylandScale = 1.f; float xwaylandScale = 1.f;
std::array<float, 9> projMatrix = {0}; std::array<float, 9> projMatrix = {0};
std::optional<Vector2D> forceSize; std::optional<Vector2D> forceSize;
wlr_output_mode* currentMode = nullptr; SP<Aquamarine::SOutputMode> currentMode;
SP<Aquamarine::CSwapchain> cursorSwapchain;
bool dpmsStatus = true; 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 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 enabled10bit = false; // as above, this can be TRUE even if 10 bit failed.
bool createdByUser = false; bool createdByUser = false;
uint32_t drmFormat = DRM_FORMAT_INVALID;
bool isUnsafeFallback = false; bool isUnsafeFallback = false;
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
@ -113,8 +114,16 @@ class CMonitor {
bool RATScheduled = false; bool RATScheduled = false;
CTimer lastPresentationTimer; CTimer lastPresentationTimer;
bool isBeingLeased = false;
SMonitorRule activeMonitorRule; SMonitorRule activeMonitorRule;
// explicit sync
SP<CSyncTimeline> inTimeline;
SP<CSyncTimeline> outTimeline;
uint64_t lastWaitPoint = 0;
uint64_t commitSeq = 0;
WP<CMonitor> self; WP<CMonitor> self;
// mirroring // mirroring
@ -124,6 +133,9 @@ class CMonitor {
// for tearing // for tearing
PHLWINDOWREF solitaryClient; PHLWINDOWREF solitaryClient;
// for direct scanout
PHLWINDOWREF lastScanout;
struct { struct {
bool canTear = false; bool canTear = false;
bool nextRenderTorn = false; bool nextRenderTorn = false;
@ -143,15 +155,6 @@ class CMonitor {
std::array<std::vector<PHLLSREF>, 4> m_aLayerSurfaceLayers; 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 // methods
void onConnect(bool noRule); void onConnect(bool noRule);
void onDisconnect(bool destroy = false); void onDisconnect(bool destroy = false);
@ -173,6 +176,8 @@ class CMonitor {
int64_t activeWorkspaceID(); int64_t activeWorkspaceID();
int64_t activeSpecialWorkspaceID(); int64_t activeSpecialWorkspaceID();
CBox logicalBox(); CBox logicalBox();
void scheduleDone();
bool attemptDirectScanout();
bool m_bEnabled = false; bool m_bEnabled = false;
bool m_bRenderingInitPassed = false; bool m_bRenderingInitPassed = false;
@ -186,4 +191,15 @@ class CMonitor {
private: private:
void setupDefaultWS(const SMonitorRule&); void setupDefaultWS(const SMonitorRule&);
int findAvailableDefaultWS(); 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 IKeyboard;
class CWLSurfaceResource; class CWLSurfaceResource;
AQUAMARINE_FORWARD(ISwitch);
struct SRenderData { struct SRenderData {
CMonitor* pMonitor; CMonitor* pMonitor;
timespec* when; timespec* when;
@ -70,14 +72,14 @@ struct SSwipeGesture {
}; };
struct SSwitchDevice { struct SSwitchDevice {
wlr_input_device* pWlrDevice = nullptr; WP<Aquamarine::ISwitch> pDevice;
int status = -1; // uninitialized struct {
CHyprSignalListener destroy;
DYNLISTENER(destroy); CHyprSignalListener fire;
DYNLISTENER(toggle); } listeners;
bool operator==(const SSwitchDevice& other) const { 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; return Hyprutils::Math::eTransform::HYPRUTILS_TRANSFORM_NORMAL;
} }
static void matrixIdentity(float mat[9]) { void matrixIdentity(float mat[9]) {
static const float identity[9] = { static const float identity[9] = {
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
}; };
memcpy(mat, identity, sizeof(identity)); 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]; float product[9];
product[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6]; 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)); 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] = { float transposition[9] = {
a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8], a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8],
}; };
memcpy(mat, transposition, sizeof(transposition)); 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] = { float translate[9] = {
1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f,
}; };
matrixMultiply(mat, mat, translate); 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] = { float scale[9] = {
x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f, x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f,
}; };
matrixMultiply(mat, mat, scale); matrixMultiply(mat, mat, scale);
} }
static void matrixRotate(float mat[9], float rad) { void matrixRotate(float mat[9], float rad) {
float rotate[9] = { float rotate[9] = {
cos(rad), -sin(rad), 0.0f, sin(rad), cos(rad), 0.0f, 0.0f, 0.0f, 1.0f, cos(rad), -sin(rad), 0.0f, sin(rad), cos(rad), 0.0f, 0.0f, 0.0f, 1.0f,
}; };
matrixMultiply(mat, mat, rotate); matrixMultiply(mat, mat, rotate);
} }
const std::unordered_map<eTransform, std::array<float, 9>>& getTransforms() {
static std::unordered_map<eTransform, std::array<float, 9>> transforms = { static std::unordered_map<eTransform, std::array<float, 9>> transforms = {
{HYPRUTILS_TRANSFORM_NORMAL, {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}},
1.0f, {HYPRUTILS_TRANSFORM_180, {-1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
0.0f, {HYPRUTILS_TRANSFORM_270, {0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
0.0f, {HYPRUTILS_TRANSFORM_FLIPPED, {-1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
0.0f, {HYPRUTILS_TRANSFORM_FLIPPED_90, {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.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}},
0.0f, {HYPRUTILS_TRANSFORM_FLIPPED_270, {0.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.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 matrixTransform(float mat[9], eTransform transform) {
matrixMultiply(mat, mat, transforms.at(transform).data());
} }
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); 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 x = 2.0f / width;
float y = 2.0f / height; 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); 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

@ -9,3 +9,12 @@ using namespace Hyprutils::Math;
eTransform wlTransformToHyprutils(wl_output_transform t); eTransform wlTransformToHyprutils(wl_output_transform t);
void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]); 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 <time.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-server-core.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 #ifdef LEGACY_RENDERER
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
@ -99,10 +27,6 @@ extern "C" {
#include <GLES3/gl3ext.h> #include <GLES3/gl3ext.h>
#endif #endif
#if !WLR_HAS_X11_BACKEND
#include "helpers/X11Stubs.hpp"
#endif
#ifdef NO_XWAYLAND #ifdef NO_XWAYLAND
#define XWAYLAND false #define XWAYLAND false
#else #else

View file

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

View file

@ -4,6 +4,7 @@
#include "config/ConfigManager.hpp" #include "config/ConfigManager.hpp"
#include "init/initHelpers.hpp" #include "init/initHelpers.hpp"
#include <fcntl.h>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <vector> #include <vector>
@ -16,6 +17,8 @@ void help() {
std::cout << "\nArguments:\n"; std::cout << "\nArguments:\n";
std::cout << " --help -h - Show this message again\n"; std::cout << " --help -h - Show this message again\n";
std::cout << " --config FILE -c FILE - Specify config file to use\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"; 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 // parse some args
std::string configPath; std::string configPath;
std::string socketName;
int socketFd = -1;
bool ignoreSudo = false; bool ignoreSudo = false;
std::vector<std::string> args{argv + 1, argv + argc}; 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"; std::cout << "[ WARNING ] Running Hyprland with superuser privileges might damage your system\n";
ignoreSudo = true; 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) { } else if (it->compare("-c") == 0 || it->compare("--config") == 0) {
if (std::next(it) == args.end()) { if (std::next(it) == args.end()) {
help(); 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"; 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"; std::cout << "Welcome to Hyprland!\n";
// let's init the compositor. // let's init the compositor.
@ -113,7 +155,7 @@ int main(int argc, char** argv) {
Debug::log(LOG, "Hyprland init finished."); Debug::log(LOG, "Hyprland init finished.");
// If all's good to go, start. // If all's good to go, start.
g_pCompositor->startCompositor(); g_pCompositor->startCompositor(socketName, socketFd);
g_pCompositor->m_bIsShuttingDown = true; g_pCompositor->m_bIsShuttingDown = true;

View file

@ -259,7 +259,7 @@ void CAnimationManager::tick() {
// manually schedule a frame // manually schedule a frame
if (PMONITOR) 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. // 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 "../config/ConfigValue.hpp"
#include "PointerManager.hpp" #include "PointerManager.hpp"
#include "../xwayland/XWayland.hpp" #include "../xwayland/XWayland.hpp"
#include <cstring>
#include "../helpers/CursorShapes.hpp"
extern "C" { extern "C" {
#include <wlr/interfaces/wlr_buffer.h> #include <X11/Xcursor/Xcursor.h>
#include <wlr/types/wlr_xcursor_manager.h>
} }
static int cursorAnimTimer(void* data) { static int cursorAnimTimer(void* data) {
@ -45,8 +46,7 @@ CCursorManager::CCursorManager() {
if (m_iSize == 0) if (m_iSize == 0)
m_iSize = 24; m_iSize = 24;
m_pWLRXCursorMgr = wlr_xcursor_manager_create(getenv("XCURSOR_THEME"), m_iSize); xcursor.loadTheme(getenv("XCURSOR_THEME") ? getenv("XCURSOR_THEME") : "", m_iSize * std::ceil(m_fCursorScale));
wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0);
m_pAnimationTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ::cursorAnimTimer, nullptr); m_pAnimationTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ::cursorAnimTimer, nullptr);
@ -56,9 +56,6 @@ CCursorManager::CCursorManager() {
} }
CCursorManager::~CCursorManager() { CCursorManager::~CCursorManager() {
if (m_pWLRXCursorMgr)
wlr_xcursor_manager_destroy(m_pWLRXCursorMgr);
if (m_pAnimationTimer) if (m_pAnimationTimer)
wl_event_source_remove(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; }); std::erase_if(m_vCursorBuffers, [ref](const auto& buf) { return buf.get() == ref; });
} }
static void cursorBufferDestroy(struct wlr_buffer* wlr_buffer) { CCursorManager::CCursorBuffer::CCursorBuffer(cairo_surface_t* surf, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base); surface = surf;
g_pCursorManager->dropBufferRef(buffer->parent); 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::CCursorBuffer(uint8_t* pixelData_, const Vector2D& size_, const Vector2D& hot_) : hotspot(hot_) {
CCursorManager::CCursorBuffer::SCursorWlrBuffer* buffer = wl_container_of(wlr_buffer, buffer, base); pixelData = pixelData_;
size = size_;
if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) stride = 4 * size_.x;
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() { CCursorManager::CCursorBuffer::~CCursorBuffer() {
; // will be freed in .destroy ;
} }
wlr_buffer* CCursorManager::getCursorBuffer() { Aquamarine::eBufferCapability CCursorManager::CCursorBuffer::caps() {
return !m_vCursorBuffers.empty() ? &m_vCursorBuffers.back()->wlrBuffer.base : nullptr; 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) { 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) { void CCursorManager::setXCursor(const std::string& name) {
if (!m_pWLRXCursorMgr) {
g_pPointerManager->resetCursorImage();
return;
}
float scale = std::ceil(m_fCursorScale); 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.themeLoaded) {
if (!xcursor) { Debug::log(ERR, "XCursor failed to find theme in setXCursor");
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.");
g_pPointerManager->resetCursorImage(); g_pPointerManager->resetCursorImage();
return; 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( m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>((uint8_t*)icon->pixels.data(), icon->size, icon->hotspot));
std::make_unique<CCursorBuffer>(image->buffer, Vector2D{(int)image->width, (int)image->height}, Vector2D{(double)image->hotspot_x, (double)image->hotspot_y}));
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) if (m_vCursorBuffers.size() > 1)
wlr_buffer_drop(&m_vCursorBuffers.front()->wlrBuffer.base); dropBufferRef(m_vCursorBuffers.at(0).get());
m_bOurBufferConnected = true; 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, 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].size, m_sCurrentCursorShapeData.images[0].size},
Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY})); 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, g_pPointerManager->setCursorBuffer(getCursorBuffer(), Vector2D{m_sCurrentCursorShapeData.images[0].hotspotX, m_sCurrentCursorShapeData.images[0].hotspotY} / m_fCursorScale,
m_fCursorScale); m_fCursorScale);
if (m_vCursorBuffers.size() > 1) if (m_vCursorBuffers.size() > 1)
wlr_buffer_drop(&m_vCursorBuffers.front()->wlrBuffer.base); dropBufferRef(m_vCursorBuffers.at(0).get());
m_bOurBufferConnected = true; m_bOurBufferConnected = true;
@ -225,7 +219,7 @@ void CCursorManager::tickAnimatedCursor() {
if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size()) if ((size_t)m_iCurrentAnimationFrame >= m_sCurrentCursorShapeData.images.size())
m_iCurrentAnimationFrame = 0; m_iCurrentAnimationFrame = 0;
m_vCursorBuffers.emplace_back(std::make_unique<CCursorBuffer>( m_vCursorBuffers.emplace_back(makeShared<CCursorBuffer>(
m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].surface, 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].size, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].size},
Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY})); Vector2D{m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotX, m_sCurrentCursorShapeData.images[m_iCurrentAnimationFrame].hotspotY}));
@ -256,10 +250,9 @@ void CCursorManager::setXWaylandCursor() {
if (CURSOR.surface) { if (CURSOR.surface) {
g_pXWayland->setCursor(cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), {CURSOR.size, CURSOR.size}, 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}); {CURSOR.hotspotX, CURSOR.hotspotY});
} else if (const auto XCURSOR = wlr_xcursor_manager_get_xcursor(m_pWLRXCursorMgr, "left_ptr", 1); XCURSOR) { } else if (xcursor.themeLoaded)
g_pXWayland->setCursor(XCURSOR->images[0]->buffer, XCURSOR->images[0]->width * 4, {(int)XCURSOR->images[0]->width, (int)XCURSOR->images[0]->height}, g_pXWayland->setCursor((uint8_t*)xcursor.defaultCursor->pixels.data(), xcursor.defaultCursor->size.x * 4, xcursor.defaultCursor->size, xcursor.defaultCursor->hotspot);
{(double)XCURSOR->images[0]->hotspot_x, (double)XCURSOR->images[0]->hotspot_y}); else
} else
Debug::log(ERR, "CursorManager: no valid cursor for xwayland"); Debug::log(ERR, "CursorManager: no valid cursor for xwayland");
} }
@ -284,7 +277,7 @@ void CCursorManager::updateTheme() {
for (auto& m : g_pCompositor->m_vMonitors) { for (auto& m : g_pCompositor->m_vMonitors) {
m->forceFullFrames = 5; 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); Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to X.", name);
if (m_pWLRXCursorMgr) xcursor.loadTheme(name, size);
wlr_xcursor_manager_destroy(m_pWLRXCursorMgr);
m_pWLRXCursorMgr = wlr_xcursor_manager_create(name.empty() ? "" : name.c_str(), size);
bool xSuccess = wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0) == 1;
// 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;
}
}
if (xSuccess && !diffTheme) {
m_szTheme = name; m_szTheme = name;
m_iSize = size; m_iSize = size;
updateTheme(); updateTheme();
return true; return true;
} }
Debug::log(ERR, "X also failed loading theme \"{}\", falling back to previous theme.", name); // 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
m_pHyprcursor = std::make_unique<Hyprcursor::CHyprcursorManager>(m_szTheme.c_str(), hcLogger); void CCursorManager::SXCursorManager::loadTheme(const std::string& name, int size) {
if (lastLoadSize == size && themeName == name)
return;
wlr_xcursor_manager_destroy(m_pWLRXCursorMgr); lastLoadSize = size;
m_pWLRXCursorMgr = wlr_xcursor_manager_create(m_szTheme.c_str(), m_iSize); themeLoaded = false;
wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0); themeName = name.empty() ? "default" : name;
updateTheme(); auto img = XcursorShapeLoadImage(2, themeName.c_str(), size);
return false;
if (!img) {
Debug::log(ERR, "XCursor failed finding theme \"{}\". Trying size 24.", themeName);
size = 24;
img = XcursorShapeLoadImage(2, themeName.c_str(), size);
if (!img) {
Debug::log(ERR, "XCursor failed finding theme \"{}\".", themeName);
return;
}
}
defaultCursor = makeShared<SXCursor>();
defaultCursor->size = {(int)img->width, (int)img->height};
defaultCursor->hotspot = {(int)img->xhot, (int)img->yhot};
defaultCursor->pixels.resize(img->width * img->height);
std::memcpy(defaultCursor->pixels.data(), img->pixels, img->width * img->height * sizeof(uint32_t));
themeLoaded = true;
XcursorImageDestroy(img);
// gather as many shapes as we can find.
cursors.clear();
for (auto& shape : CURSOR_SHAPE_NAMES) {
int id = -1;
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);
}
} }

View file

@ -6,17 +6,19 @@
#include "../includes.hpp" #include "../includes.hpp"
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"
#include "../helpers/memory/Memory.hpp" #include "../helpers/memory/Memory.hpp"
#include "../macros.hpp"
#include <aquamarine/buffer/Buffer.hpp>
struct wlr_buffer;
struct wlr_xcursor_manager;
class CWLSurface; class CWLSurface;
AQUAMARINE_FORWARD(IBuffer);
class CCursorManager { class CCursorManager {
public: public:
CCursorManager(); CCursorManager();
~CCursorManager(); ~CCursorManager();
wlr_buffer* getCursorBuffer(); SP<Aquamarine::IBuffer> getCursorBuffer();
void setCursorFromName(const std::string& name); void setCursorFromName(const std::string& name);
void setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot); void setCursorSurface(SP<CWLSurface> surf, const Vector2D& hotspot);
@ -29,24 +31,26 @@ class CCursorManager {
void tickAnimatedCursor(); void tickAnimatedCursor();
class CCursorBuffer { class CCursorBuffer : public Aquamarine::IBuffer {
public: public:
CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot); CCursorBuffer(cairo_surface_t* surf, const Vector2D& size, const Vector2D& hotspot);
CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot); CCursorBuffer(uint8_t* pixelData, const Vector2D& size, const Vector2D& hotspot);
~CCursorBuffer(); ~CCursorBuffer();
struct SCursorWlrBuffer { virtual Aquamarine::eBufferCapability caps();
wlr_buffer base; virtual Aquamarine::eBufferType type();
cairo_surface_t* surface = nullptr; virtual void update(const Hyprutils::Math::CRegion& damage);
bool dropped = false; virtual bool isSynchronous(); // whether the updates to this buffer are synchronous, aka happen over cpu
CCursorBuffer* parent = nullptr; virtual bool good();
uint8_t* pixelData = nullptr; virtual Aquamarine::SSHMAttrs shm();
size_t stride = 0; virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
} wlrBuffer; virtual void endDataPtr();
private: private:
Vector2D size;
Vector2D hotspot; Vector2D hotspot;
cairo_surface_t* surface = nullptr;
uint8_t* pixelData = nullptr;
size_t stride = 0;
friend class CCursorManager; friend class CCursorManager;
}; };
@ -56,7 +60,7 @@ class CCursorManager {
bool m_bOurBufferConnected = false; bool m_bOurBufferConnected = false;
private: private:
std::vector<std::unique_ptr<CCursorBuffer>> m_vCursorBuffers; std::vector<SP<CCursorBuffer>> m_vCursorBuffers;
std::unique_ptr<Hyprcursor::CHyprcursorManager> m_pHyprcursor; std::unique_ptr<Hyprcursor::CHyprcursorManager> m_pHyprcursor;
@ -70,8 +74,24 @@ class CCursorManager {
int m_iCurrentAnimationFrame = 0; int m_iCurrentAnimationFrame = 0;
Hyprcursor::SCursorShapeData m_sCurrentCursorShapeData; Hyprcursor::SCursorShapeData m_sCurrentCursorShapeData;
// xcursor fallback // gangsta bootleg XCursor impl. Whenever Hyprland has to use
wlr_xcursor_manager* m_pWLRXCursorMgr = nullptr; // 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; inline std::unique_ptr<CCursorManager> g_pCursorManager;

View file

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

View file

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

View file

@ -6,133 +6,8 @@
#include "../protocols/core/Compositor.hpp" #include "../protocols/core/Compositor.hpp"
#include "eventLoop/EventLoopManager.hpp" #include "eventLoop/EventLoopManager.hpp"
#include "SeatManager.hpp" #include "SeatManager.hpp"
#include <wlr/interfaces/wlr_output.h> #include <cstring>
#include <wlr/render/interface.h> #include <gbm.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;
}
CPointerManager::CPointerManager() { CPointerManager::CPointerManager() {
hooks.monitorAdded = g_pHookSystem->hookDynamic("newMonitor", [this](void* self, SCallbackInfo& info, std::any data) { 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(); updateCursorBackend();
} }
bool CPointerManager::softwareLockedFor(SP<CMonitor> mon) {
auto state = stateFor(mon);
return state->softwareLocks > 0 || state->hardwareFailed;
}
Vector2D CPointerManager::position() { Vector2D CPointerManager::position() {
return pointerPos; return pointerPos;
} }
@ -216,7 +96,7 @@ SP<CPointerManager::SMonitorPointerState> CPointerManager::stateFor(SP<CMonitor>
return *it; 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(); damageIfSoftware();
if (buf == currentCursorImage.pBuffer) { if (buf == currentCursorImage.pBuffer) {
if (hotspot != currentCursorImage.hotspot || scale != currentCursorImage.scale) { if (hotspot != currentCursorImage.hotspot || scale != currentCursorImage.scale) {
@ -232,10 +112,8 @@ void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot,
resetCursorImage(false); resetCursorImage(false);
if (buf) { if (buf) {
currentCursorImage.size = {buf->width, buf->height}; currentCursorImage.size = buf->size;
currentCursorImage.pBuffer = wlr_buffer_lock(buf); currentCursorImage.pBuffer = buf;
currentCursorImage.hyprListener_destroyBuffer.initCallback(&buf->events.destroy, [this](void* owner, void* data) { resetCursorImage(); }, this, "CPointerManager");
} }
currentCursorImage.hotspot = hotspot; currentCursorImage.hotspot = hotspot;
@ -317,8 +195,8 @@ void CPointerManager::recheckEnteredOutputs() {
// if we are using hw cursors, prevent // if we are using hw cursors, prevent
// the cursor from being stuck at the last point. // the cursor from being stuck at the last point.
// if we are leaving it, move it to narnia. // if we are leaving it, move it to narnia.
if (!s->hardwareFailed && s->monitor->output->impl->move_cursor) if (!s->hardwareFailed && (s->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER))
s->monitor->output->impl->move_cursor(s->monitor->output, -1337, -420); s->monitor->output->moveCursor({-1337, -420});
if (!currentCursorImage.surface) if (!currentCursorImage.surface)
continue; continue;
@ -339,16 +217,11 @@ void CPointerManager::resetCursorImage(bool apply) {
currentCursorImage.destroySurface.reset(); currentCursorImage.destroySurface.reset();
currentCursorImage.commitSurface.reset(); currentCursorImage.commitSurface.reset();
currentCursorImage.surface.reset(); currentCursorImage.surface.reset();
} else if (currentCursorImage.pBuffer) { } else if (currentCursorImage.pBuffer)
wlr_buffer_unlock(currentCursorImage.pBuffer);
currentCursorImage.hyprListener_destroyBuffer.removeCallback();
currentCursorImage.pBuffer = nullptr; currentCursorImage.pBuffer = nullptr;
}
if (currentCursorImage.pBufferTexture) { if (currentCursorImage.bufferTex)
wlr_texture_destroy(currentCursorImage.pBufferTexture); currentCursorImage.bufferTex = nullptr;
currentCursorImage.pBufferTexture = nullptr;
}
currentCursorImage.scale = 1.F; currentCursorImage.scale = 1.F;
currentCursorImage.hotspot = {0, 0}; currentCursorImage.hotspot = {0, 0};
@ -370,9 +243,8 @@ void CPointerManager::resetCursorImage(bool apply) {
} }
if (ms->cursorFrontBuffer) { if (ms->cursorFrontBuffer) {
if (ms->monitor->output->impl->set_cursor) if (ms->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER)
ms->monitor->output->impl->set_cursor(ms->monitor->output, nullptr, 0, 0); ms->monitor->output->setCursor(nullptr, {});
wlr_buffer_unlock(ms->cursorFrontBuffer);
ms->cursorFrontBuffer = nullptr; ms->cursorFrontBuffer = nullptr;
} }
} }
@ -418,18 +290,18 @@ void CPointerManager::onCursorMoved() {
continue; continue;
const auto CURSORPOS = getCursorPosForMonitor(m); 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) { bool CPointerManager::attemptHardwareCursor(SP<CPointerManager::SMonitorPointerState> state) {
auto output = state->monitor->output; auto output = state->monitor->output;
if (!output->impl->set_cursor) if (!(output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER))
return false; return false;
const auto CURSORPOS = getCursorPosForMonitor(state->monitor.lock()); 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(); auto texture = getCurrentCursorTexture();
@ -459,64 +331,62 @@ bool CPointerManager::attemptHardwareCursor(SP<CPointerManager::SMonitorPointerS
return success; return success;
} }
bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, wlr_buffer* buf) { bool CPointerManager::setHWCursorBuffer(SP<SMonitorPointerState> state, SP<Aquamarine::IBuffer> buf) {
if (!state->monitor->output->impl->set_cursor) if (!(state->monitor->output->getBackend()->capabilities() & Aquamarine::IBackendImplementation::eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER))
return false; return false;
const auto HOTSPOT = transformedHotspot(state->monitor.lock()); const auto HOTSPOT = transformedHotspot(state->monitor.lock());
Debug::log(TRACE, "[pointer] hw transformed hotspot for {}: {}", state->monitor->szName, HOTSPOT); 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; return false;
wlr_buffer_unlock(state->cursorFrontBuffer);
state->cursorFrontBuffer = buf; state->cursorFrontBuffer = buf;
g_pCompositor->scheduleFrameForMonitor(state->monitor.get()); g_pCompositor->scheduleFrameForMonitor(state->monitor.get(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_SHAPE);
if (buf)
wlr_buffer_lock(buf);
return true; 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; auto output = state->monitor->output;
int w = currentCursorImage.size.x, h = currentCursorImage.size.y; auto maxSize = output->cursorPlaneSize();
if (output->impl->get_cursor_size) { auto cursorSize = currentCursorImage.size;
output->impl->get_cursor_size(output, &w, &h);
if (w < currentCursorImage.size.x || h < currentCursorImage.size.y) { if (maxSize == Vector2D{})
Debug::log(TRACE, "hardware cursor too big! {} > {}x{}", currentCursorImage.size, w, h); return nullptr;
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;
if (!state->monitor->cursorSwapchain || maxSize != state->monitor->cursorSwapchain->currentOptions().size) {
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; return nullptr;
} }
} }
if (w <= 0 || h <= 0) { auto buf = state->monitor->cursorSwapchain->next(nullptr);
Debug::log(TRACE, "hw cursor for output {} failed the size checks ({}x{} is invalid)", state->monitor->szName, w, h);
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");
return nullptr;
}
wlr_swapchain_destroy(output->cursor_swapchain);
output->cursor_swapchain = wlr_swapchain_create(output->allocator, w, h, &fmt);
wlr_drm_format_finish(&fmt);
if (!output->cursor_swapchain) {
Debug::log(TRACE, "Failed to create cursor swapchain");
return nullptr;
}
}
wlr_buffer* buf = wlr_swapchain_acquire(output->cursor_swapchain, nullptr);
if (!buf) { if (!buf) {
Debug::log(TRACE, "Failed to acquire a buffer from the cursor swapchain"); Debug::log(TRACE, "Failed to acquire a buffer from the cursor swapchain");
return nullptr; return nullptr;
@ -525,16 +395,45 @@ wlr_buffer* CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPo
CRegion damage = {0, 0, INT16_MAX, INT16_MAX}; CRegion damage = {0, 0, INT16_MAX, INT16_MAX};
g_pHyprRenderer->makeEGLCurrent(); 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(); RBO->bind();
g_pHyprOpenGL->beginSimple(state->monitor.get(), damage, RBO); g_pHyprOpenGL->beginSimple(state->monitor.get(), damage, RBO);
g_pHyprOpenGL->clear(CColor{0.F, 0.F, 0.F, 0.F}); g_pHyprOpenGL->clear(CColor{0.F, 0.F, 0.F, 0.F});
CBox xbox = {{}, Vector2D{currentCursorImage.size / currentCursorImage.scale * state->monitor->scale}.round()}; 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()); currentCursorImage.scale, state->monitor->scale, xbox.size());
g_pHyprOpenGL->renderTexture(texture, &xbox, 1.F); g_pHyprOpenGL->renderTexture(texture, &xbox, 1.F);
@ -543,9 +442,7 @@ wlr_buffer* CPointerManager::renderHWCursorBuffer(SP<CPointerManager::SMonitorPo
glFlush(); glFlush();
g_pHyprOpenGL->m_RenderData.pMonitor = nullptr; g_pHyprOpenGL->m_RenderData.pMonitor = nullptr;
g_pHyprRenderer->onRenderbufferDestroy(RBO); g_pHyprRenderer->onRenderbufferDestroy(RBO.get());
wlr_buffer_unlock(buf);
return buf; return buf;
} }
@ -579,7 +476,7 @@ void CPointerManager::renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec*
box.x = std::round(box.x); box.x = std::round(box.x);
box.y = std::round(box.y); 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) if (currentCursorImage.surface)
currentCursorImage.surface->resource()->frame(now); currentCursorImage.surface->resource()->frame(now);
@ -587,17 +484,19 @@ void CPointerManager::renderSoftwareCursorsFor(SP<CMonitor> pMonitor, timespec*
Vector2D CPointerManager::getCursorPosForMonitor(SP<CMonitor> pMonitor) { Vector2D CPointerManager::getCursorPosForMonitor(SP<CMonitor> pMonitor) {
return CBox{pointerPos - pMonitor->vecPosition, {0, 0}} 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() * .pos() *
pMonitor->scale; pMonitor->scale;
} }
Vector2D CPointerManager::transformedHotspot(SP<CMonitor> pMonitor) { 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 {}; // doesn't matter, we have no hw cursor, and this is only for hw cursors
return CBox{currentCursorImage.hotspot * pMonitor->scale, {0, 0}} 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(); .pos();
} }
@ -799,10 +698,8 @@ SP<CTexture> CPointerManager::getCurrentCursorTexture() {
return nullptr; return nullptr;
if (currentCursorImage.pBuffer) { if (currentCursorImage.pBuffer) {
if (!currentCursorImage.pBufferTexture) { if (!currentCursorImage.bufferTex)
currentCursorImage.pBufferTexture = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, currentCursorImage.pBuffer); currentCursorImage.bufferTex = makeShared<CTexture>(currentCursorImage.pBuffer);
currentCursorImage.bufferTex = makeShared<CTexture>(currentCursorImage.pBufferTexture);
}
return currentCursorImage.bufferTex; return currentCursorImage.bufferTex;
} }

View file

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

View file

@ -1,5 +1,7 @@
#include "ProtocolManager.hpp" #include "ProtocolManager.hpp"
#include "../config/ConfigValue.hpp"
#include "../protocols/TearingControl.hpp" #include "../protocols/TearingControl.hpp"
#include "../protocols/FractionalScale.hpp" #include "../protocols/FractionalScale.hpp"
#include "../protocols/XDGOutput.hpp" #include "../protocols/XDGOutput.hpp"
@ -35,6 +37,8 @@
#include "../protocols/Viewporter.hpp" #include "../protocols/Viewporter.hpp"
#include "../protocols/MesaDRM.hpp" #include "../protocols/MesaDRM.hpp"
#include "../protocols/LinuxDMABUF.hpp" #include "../protocols/LinuxDMABUF.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Seat.hpp" #include "../protocols/core/Seat.hpp"
#include "../protocols/core/DataDevice.hpp" #include "../protocols/core/DataDevice.hpp"
@ -45,6 +49,10 @@
#include "../helpers/Monitor.hpp" #include "../helpers/Monitor.hpp"
#include "../render/Renderer.hpp" #include "../render/Renderer.hpp"
#include "../Compositor.hpp"
#include <aquamarine/buffer/Buffer.hpp>
#include <aquamarine/backend/Backend.hpp>
void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) { void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
const bool ISMIRROR = pMonitor->isMirror(); const bool ISMIRROR = pMonitor->isMirror();
@ -65,6 +73,8 @@ void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) {
CProtocolManager::CProtocolManager() { CProtocolManager::CProtocolManager() {
static const auto PENABLEEXPLICIT = CConfigValue<Hyprlang::INT>("experimental:explicit_sync");
// Outputs are a bit dumb, we have to agree. // Outputs are a bit dumb, we have to agree.
static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
auto M = std::any_cast<CMonitor*>(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::primarySelection = std::make_unique<CPrimarySelectionProtocol>(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection");
PROTO::xwaylandShell = std::make_unique<CXWaylandShellProtocol>(&xwayland_shell_v1_interface, 1, "XWaylandShell"); PROTO::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) { if (g_pHyprOpenGL->getDRMFormats().size() > 0) {
PROTO::mesaDRM = std::make_unique<CMesaDRMProtocol>(&wl_drm_interface, 2, "MesaDRM"); PROTO::mesaDRM = std::make_unique<CMesaDRMProtocol>(&wl_drm_interface, 2, "MesaDRM");
PROTO::linuxDma = std::make_unique<CLinuxDMABufV1Protocol>(&zwp_linux_dmabuf_v1_interface, 5, "LinuxDMABUF"); 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() { void CSeatManager::updateActiveKeyboardData() {
if (keyboard && keyboard->wlr()) if (keyboard)
PROTO::seat->updateRepeatInfo(keyboard->wlr()->repeat_info.rate, keyboard->wlr()->repeat_info.delay); PROTO::seat->updateRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay);
PROTO::seat->updateKeymap(); PROTO::seat->updateKeymap();
} }
@ -103,7 +103,7 @@ void CSeatManager::setKeyboardFocus(SP<CWLSurfaceResource> surf) {
if (state.keyboardFocus == surf) if (state.keyboardFocus == surf)
return; return;
if (!keyboard || !keyboard->wlr()) { if (!keyboard) {
Debug::log(ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set"); Debug::log(ERR, "BUG THIS: setKeyboardFocus without a valid keyboard set");
return; return;
} }
@ -144,7 +144,7 @@ void CSeatManager::setKeyboardFocus(SP<CWLSurfaceResource> surf) {
continue; continue;
k->sendEnter(surf); 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; return;
} }
if (!mouse || !mouse->wlr()) { if (!mouse) {
Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set"); Debug::log(ERR, "BUG THIS: setPointerFocus without a valid mouse set");
return; return;
} }
@ -333,9 +333,7 @@ void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double
if (source == 0) { if (source == 0) {
p->sendAxisValue120(axis, value120); p->sendAxisValue120(axis, value120);
p->sendAxisDiscrete(axis, discrete); p->sendAxisDiscrete(axis, discrete);
} } else if (value == 0)
if (value == 0)
p->sendAxisStop(timeMs, axis); 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(); const auto SIZEHINTS = pWindow->m_pXWaylandSurface->sizeHints.get();
if (SIZEHINTS && pWindow->m_iX11Type != 2) { if (SIZEHINTS && pWindow->m_iX11Type != 2) {
pbox->x = SIZEHINTS->x; // WM_SIZE_HINTS' x,y,w,h is deprecated it seems.
pbox->y = SIZEHINTS->y; // Source: https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#wm_normal_hints_property
pbox->width = SIZEHINTS->width; pbox->x = pWindow->m_pXWaylandSurface->geometry.x;
pbox->height = SIZEHINTS->height; 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 { } else {
*pbox = pWindow->m_pXWaylandSurface->geometry; 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) } else if (pWindow->m_pXDGSurface)
*pbox = pWindow->m_pXDGSurface->current.geometry; *pbox = pWindow->m_pXDGSurface->current.geometry;
} }

View file

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

View file

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

View file

@ -1,6 +1,6 @@
#include "InputManager.hpp" #include "InputManager.hpp"
#include "../../Compositor.hpp" #include "../../Compositor.hpp"
#include "wlr/types/wlr_switch.h" #include <aquamarine/output/Output.hpp>
#include <cstdint> #include <cstdint>
#include <ranges> #include <ranges>
#include "../../config/ConfigValue.hpp" #include "../../config/ConfigValue.hpp"
@ -28,6 +28,8 @@
#include "../../managers/PointerManager.hpp" #include "../../managers/PointerManager.hpp"
#include "../../managers/SeatManager.hpp" #include "../../managers/SeatManager.hpp"
#include <aquamarine/input/Input.hpp>
CInputManager::CInputManager() { CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) { m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
if (!cursorImageUnlocked()) if (!cursorImageUnlocked())
@ -189,8 +191,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
bool skipFrameSchedule = PMONITOR->shouldSkipScheduleFrameOnMouseEvent(); bool skipFrameSchedule = PMONITOR->shouldSkipScheduleFrameOnMouseEvent();
if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && PMONITOR->output->software_cursor_locks > 0 && !skipFrameSchedule) if (!PMONITOR->solitaryClient.lock() && g_pHyprRenderer->shouldRenderCursor() && g_pPointerManager->softwareLockedFor(PMONITOR->self.lock()) && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(PMONITOR); g_pCompositor->scheduleFrameForMonitor(PMONITOR, Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE);
PHLWINDOW forcedFocus = m_pForcedFocus.lock(); PHLWINDOW forcedFocus = m_pForcedFocus.lock();
@ -372,8 +374,8 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
foundSurface = foundSurface =
g_pCompositor->vectorToLayerSurface(mouseCoords, &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &surfaceCoords, &pFoundLayerSurface); 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) if (g_pPointerManager->softwareLockedFor(PMONITOR->self.lock()) > 0 && !skipFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get()); g_pCompositor->scheduleFrameForMonitor(g_pCompositor->m_pLastMonitor.get(), Aquamarine::IOutput::AQ_SCHEDULE_CURSOR_MOVE);
// grabs // grabs
if (g_pSeatManager->seatGrab && !g_pSeatManager->seatGrab->accepts(foundSurface)) { 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 POFFWINDOWAXIS = CConfigValue<Hyprlang::INT>("input:off_window_axis_events");
static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor"); static auto PINPUTSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:scroll_factor");
static auto PTOUCHPADSCROLLFACTOR = CConfigValue<Hyprlang::FLOAT>("input:touchpad: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); 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), double discrete = (e.deltaDiscrete != 0) ? (factor * e.deltaDiscrete / std::abs(e.deltaDiscrete)) : 0;
std::round(factor * e.deltaDiscrete), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); 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() { Vector2D CInputManager::getMouseCoordsInternal() {
return g_pPointerManager->position(); return g_pPointerManager->position();
} }
void CInputManager::newKeyboard(wlr_input_device* keyboard) { void CInputManager::newKeyboard(SP<Aquamarine::IKeyboard> keyboard) {
const auto PNEWKEYBOARD = m_vKeyboards.emplace_back(CKeyboard::create(wlr_keyboard_from_input_device(keyboard))); const auto PNEWKEYBOARD = m_vKeyboards.emplace_back(CKeyboard::create(keyboard));
setupKeyboard(PNEWKEYBOARD); setupKeyboard(PNEWKEYBOARD);
@ -820,14 +858,14 @@ void CInputManager::newVirtualKeyboard(SP<CVirtualKeyboardV1Resource> keyboard)
setupKeyboard(PNEWKEYBOARD); 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) { void CInputManager::setupKeyboard(SP<IKeyboard> keeb) {
m_vHIDs.push_back(keeb); m_vHIDs.push_back(keeb);
try { try {
keeb->hlName = getNameForNewDevice(keeb->wlr()->base.name); keeb->hlName = getNameForNewDevice(keeb->deviceName);
} catch (std::exception& e) { } catch (std::exception& e) {
Debug::log(ERR, "Keyboard had no name???"); // logic error 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 // we can ignore those and just apply
} }
wlr_keyboard_set_repeat_info(pKeyboard->wlr(), std::max(0, REPEATRATE), std::max(0, REPEATDELAY)); pKeyboard->repeatRate = std::max(0, REPEATRATE);
pKeyboard->repeatDelay = std::max(0, REPEATDELAY);
pKeyboard->repeatDelay = REPEATDELAY;
pKeyboard->repeatRate = REPEATRATE;
pKeyboard->numlockOn = NUMLOCKON; pKeyboard->numlockOn = NUMLOCKON;
pKeyboard->xkbFilePath = FILEPATH; 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->setKeymap(IKeyboard::SStringRuleNames{LAYOUT, MODEL, VARIANT, OPTIONS, RULES});
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);
const auto LAYOUTSTR = pKeyboard->getActiveLayout(); const auto LAYOUTSTR = pKeyboard->getActiveLayout();
@ -1017,11 +984,11 @@ void CInputManager::newVirtualMouse(SP<CVirtualPointerV1Resource> mouse) {
setupMouse(PMOUSE); 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) { void CInputManager::newMouse(SP<Aquamarine::IPointer> mouse) {
const auto PMOUSE = m_vPointers.emplace_back(CMouse::create(wlr_pointer_from_input_device(mouse))); const auto PMOUSE = m_vPointers.emplace_back(CMouse::create(mouse));
setupMouse(PMOUSE); setupMouse(PMOUSE);
@ -1032,13 +999,13 @@ void CInputManager::setupMouse(SP<IPointer> mauz) {
m_vHIDs.push_back(mauz); m_vHIDs.push_back(mauz);
try { try {
mauz->hlName = getNameForNewDevice(mauz->wlr()->base.name); mauz->hlName = getNameForNewDevice(mauz->deviceName);
} catch (std::exception& e) { } catch (std::exception& e) {
Debug::log(ERR, "Mouse had no name???"); // logic error Debug::log(ERR, "Mouse had no name???"); // logic error
} }
if (wlr_input_device_is_libinput(&mauz->wlr()->base)) { if (mauz->aq() && mauz->aq()->getLibinputHandle()) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&mauz->wlr()->base); 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), 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), 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)) { if (m->aq() && m->aq()->getLibinputHandle()) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&m->wlr()->base); const auto LIBINPUTDEV = m->aq()->getLibinputHandle();
double touchw = 0, touchh = 0; double touchw = 0, touchh = 0;
const auto ISTOUCHPAD = libinput_device_has_capability(LIBINPUTDEV, LIBINPUT_DEVICE_CAP_POINTER) && 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) { void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
if (pKeyboard->xkbTranslationState) Debug::log(LOG, "Keyboard at {:x} removed", (uintptr_t)pKeyboard.get());
xkb_state_unref(pKeyboard->xkbTranslationState);
pKeyboard->xkbTranslationState = nullptr;
std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; }); std::erase_if(m_vKeyboards, [pKeyboard](const auto& other) { return other == pKeyboard; });
if (m_vKeyboards.size() > 0) { if (m_vKeyboards.size() > 0) {
bool found = false; bool found = false;
for (auto& k : m_vKeyboards | std::views::reverse) { for (auto& k : m_vKeyboards | std::views::reverse) {
if (!k->wlr()) if (!k)
continue; continue;
g_pSeatManager->setKeyboard(k); g_pSeatManager->setKeyboard(k);
@ -1251,6 +1216,8 @@ void CInputManager::destroyKeyboard(SP<IKeyboard> pKeyboard) {
} }
void CInputManager::destroyPointer(SP<IPointer> mouse) { 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; }); std::erase_if(m_vPointers, [mouse](const auto& other) { return other == mouse; });
g_pSeatManager->setMouse(m_vPointers.size() > 0 ? m_vPointers.front() : nullptr); g_pSeatManager->setMouse(m_vPointers.size() > 0 ? m_vPointers.front() : nullptr);
@ -1297,20 +1264,7 @@ void CInputManager::updateKeyboardsLeds(SP<IKeyboard> pKeyboard) {
if (!pKeyboard) if (!pKeyboard)
return; return;
auto keyboard = pKeyboard->wlr(); pKeyboard->updateLEDs();
if (!keyboard || keyboard->xkb_state == nullptr)
return;
uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
if (xkb_state_led_index_is_active(keyboard->xkb_state, keyboard->led_indexes[i]))
leds |= (1 << i);
}
for (auto& k : m_vKeyboards) {
k->updateLEDs(leds);
}
} }
void CInputManager::onKeyboardKey(std::any event, SP<IKeyboard> pKeyboard) { 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(); const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) { if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(pKeyboard->wlr()); IME->setKeyboard(pKeyboard);
IME->sendKey(e.timeMs, e.keycode, e.state); IME->sendKey(e.timeMs, e.keycode, e.state);
} else { } else {
g_pSeatManager->setKeyboard(pKeyboard); g_pSeatManager->setKeyboard(pKeyboard);
@ -1356,15 +1310,14 @@ void CInputManager::onKeyboardMod(SP<IKeyboard> pKeyboard) {
const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard); const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto ALLMODS = accumulateModsFromAllKBs(); const auto ALLMODS = accumulateModsFromAllKBs();
const auto PWLRKB = pKeyboard->wlr();
auto MODS = PWLRKB->modifiers; auto MODS = pKeyboard->modifiersState;
MODS.depressed = ALLMODS; MODS.depressed = ALLMODS;
const auto IME = m_sIMERelay.m_pIME.lock(); const auto IME = m_sIMERelay.m_pIME.lock();
if (IME && IME->hasGrab() && !DISALLOWACTION) { if (IME && IME->hasGrab() && !DISALLOWACTION) {
IME->setKeyboard(PWLRKB); IME->setKeyboard(pKeyboard);
IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group); IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
} else { } else {
g_pSeatManager->setKeyboard(pKeyboard); g_pSeatManager->setKeyboard(pKeyboard);
@ -1373,12 +1326,12 @@ void CInputManager::onKeyboardMod(SP<IKeyboard> pKeyboard) {
updateKeyboardsLeds(pKeyboard); updateKeyboardsLeds(pKeyboard);
if (PWLRKB->modifiers.group != pKeyboard->activeLayout) { if (pKeyboard->modifiersState.group != pKeyboard->activeLayout) {
pKeyboard->activeLayout = PWLRKB->modifiers.group; pKeyboard->activeLayout = pKeyboard->modifiersState.group;
const auto LAYOUT = pKeyboard->getActiveLayout(); 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}); g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", pKeyboard->hlName + "," + LAYOUT});
EMIT_HOOK_EVENT("activeLayout", (std::vector<std::any>{pKeyboard, LAYOUT})); EMIT_HOOK_EVENT("activeLayout", (std::vector<std::any>{pKeyboard, LAYOUT}));
@ -1488,10 +1441,10 @@ uint32_t CInputManager::accumulateModsFromAllKBs() {
if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb)) if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb))
continue; continue;
if (!kb->enabled || !kb->wlr()) if (!kb->enabled)
continue; continue;
finalMask |= wlr_keyboard_get_modifiers(kb->wlr()); finalMask |= kb->getModifiers();
} }
return finalMask; return finalMask;
@ -1507,12 +1460,12 @@ void CInputManager::disableAllKeyboards(bool virt) {
} }
} }
void CInputManager::newTouchDevice(wlr_input_device* pDevice) { void CInputManager::newTouchDevice(SP<Aquamarine::ITouch> pDevice) {
const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(wlr_touch_from_input_device(pDevice))); const auto PNEWDEV = m_vTouches.emplace_back(CTouchDevice::create(pDevice));
m_vHIDs.push_back(PNEWDEV); m_vHIDs.push_back(PNEWDEV);
try { try {
PNEWDEV->hlName = getNameForNewDevice(pDevice->name); PNEWDEV->hlName = getNameForNewDevice(PNEWDEV->deviceName);
} catch (std::exception& e) { } catch (std::exception& e) {
Debug::log(ERR, "Touch Device had no name???"); // logic error 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) { void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
auto setConfig = [&](SP<ITouch> PTOUCHDEV) -> void { auto setConfig = [](SP<ITouch> PTOUCHDEV) -> void {
if (wlr_input_device_is_libinput(&PTOUCHDEV->wlr()->base)) { if (PTOUCHDEV->aq() && PTOUCHDEV->aq()->getLibinputHandle()) {
const auto LIBINPUTDEV = (libinput_device*)wlr_libinput_get_device_handle(&PTOUCHDEV->wlr()->base); const auto LIBINPUTDEV = PTOUCHDEV->aq()->getLibinputHandle();
const auto ENABLED = g_pConfigManager->getDeviceInt(PTOUCHDEV->hlName, "enabled", "input:touchdevice:enabled"); 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; 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; bool bound = !output.empty() && output != STRVAL_EMPTY;
const bool AUTODETECT = output == "[[Auto]]"; const bool AUTODETECT = output == "[[Auto]]";
if (!bound && AUTODETECT) { if (!bound && AUTODETECT) {
const auto DEFAULTOUTPUT = PTOUCHDEV->wlr()->output_name; // FIXME:
if (DEFAULTOUTPUT) { // const auto DEFAULTOUTPUT = PTOUCHDEV->wlr()->output_name;
output = DEFAULTOUTPUT; // if (DEFAULTOUTPUT) {
bound = true; // output = DEFAULTOUTPUT;
} // bound = true;
// }
} }
PTOUCHDEV->boundOutput = bound ? output : ""; PTOUCHDEV->boundOutput = bound ? output : "";
const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr; const auto PMONITOR = bound ? g_pCompositor->getMonitorFromName(output) : nullptr;
@ -1581,9 +1535,9 @@ void CInputManager::setTouchDeviceConfigs(SP<ITouch> dev) {
void CInputManager::setTabletConfigs() { void CInputManager::setTabletConfigs() {
for (auto& t : m_vTablets) { 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 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"); const auto RELINPUT = g_pConfigManager->getDeviceInt(NAME, "relative_input", "input:tablet:relative_input");
t->relativeInput = RELINPUT; 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_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"); 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) { 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, t->activeArea = CBox{ACTIVE_AREA_POS.x / t->aq()->physicalSize.x, ACTIVE_AREA_POS.y / t->aq()->physicalSize.y,
(ACTIVE_AREA_POS.y + ACTIVE_AREA_SIZE.y) / t->wlr()->height_mm}; (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) { void CInputManager::newSwitch(SP<Aquamarine::ISwitch> pDevice) {
const auto PNEWDEV = &m_lSwitches.emplace_back(); const auto PNEWDEV = &m_lSwitches.emplace_back();
PNEWDEV->pWlrDevice = pDevice; 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();
PNEWDEV->hyprListener_toggle.initCallback( const auto E = std::any_cast<Aquamarine::ISwitch::SFireEvent>(d);
&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;
if (PDEVICE->status != -1 && PDEVICE->status == E->switch_state)
return;
Debug::log(LOG, "Switch {} fired, triggering binds.", NAME); Debug::log(LOG, "Switch {} fired, triggering binds.", NAME);
g_pKeybindManager->onSwitchEvent(NAME); g_pKeybindManager->onSwitchEvent(NAME);
switch (E->switch_state) { if (E.enable) {
case WLR_SWITCH_STATE_ON:
Debug::log(LOG, "Switch {} turn on, triggering binds.", NAME); Debug::log(LOG, "Switch {} turn on, triggering binds.", NAME);
g_pKeybindManager->onSwitchOnEvent(NAME); g_pKeybindManager->onSwitchOnEvent(NAME);
break; } else {
case WLR_SWITCH_STATE_OFF:
Debug::log(LOG, "Switch {} turn off, triggering binds.", NAME); Debug::log(LOG, "Switch {} turn off, triggering binds.", NAME);
g_pKeybindManager->onSwitchOffEvent(NAME); g_pKeybindManager->onSwitchOffEvent(NAME);
break;
} }
});
PDEVICE->status = E->switch_state;
},
PNEWDEV, "SwitchDevice");
} }
void CInputManager::destroySwitch(SSwitchDevice* pDevice) { void CInputManager::destroySwitch(SSwitchDevice* pDevice) {

View file

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

View file

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

View file

@ -103,7 +103,7 @@ void CInputManager::onTouchMove(ITouch::SMotionEvent e) {
return; return;
const bool VERTANIMS = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle == "slidevert" || const bool VERTANIMS = m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle == "slidevert" ||
m_sActiveSwipe.pWorkspaceBegin->m_vRenderOffset.getConfig()->pValues->internalStyle.starts_with("slidefadevert"); 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"); static auto PSWIPEDIST = CConfigValue<Hyprlang::INT>("gestures:workspace_swipe_distance");
const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, (int64_t)1LL, (int64_t)UINT32_MAX); const auto SWIPEDISTANCE = std::clamp(*PSWIPEDIST, (int64_t)1LL, (int64_t)UINT32_MAX);
// Handle the workspace swipe if there is one // 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') src = globber.stdout().strip().split('\n')
executable('Hyprland', src, executable('Hyprland', src,
cpp_args: ['-DWLR_USE_UNSTABLE'],
link_args: '-rdynamic', link_args: '-rdynamic',
cpp_pch: 'pch/pch.hpp', cpp_pch: 'pch/pch.hpp',
dependencies: [ dependencies: [
server_protos, server_protos,
dependency('aquamarine'),
dependency('gbm'),
dependency('xcursor'),
dependency('wayland-server'), dependency('wayland-server'),
dependency('wayland-client'), dependency('wayland-client'),
wlroots.get_variable('wlroots'),
dependency('cairo'), dependency('cairo'),
dependency('hyprcursor', version: '>=0.1.7'), dependency('hyprcursor', version: '>=0.1.7'),
dependency('hyprlang', version: '>= 0.3.2'), dependency('hyprlang', version: '>= 0.3.2'),

View file

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

View file

@ -1,48 +1,9 @@
#include "CursorShape.hpp" #include "CursorShape.hpp"
#include <algorithm> #include <algorithm>
#include "../helpers/CursorShapes.hpp"
#define LOGM PROTO::cursorShape->protoLog #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) { 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) { 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"); pMgr->error(WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "The shape is invalid");
return; return;
} }
@ -90,7 +51,7 @@ void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t ser
SSetShapeEvent event; SSetShapeEvent event;
event.pMgr = pMgr; event.pMgr = pMgr;
event.shape = shape; event.shape = shape;
event.shapeName = SHAPE_NAMES[shape]; event.shapeName = CURSOR_SHAPE_NAMES.at(shape);
events.setShape.emit(event); 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) { if (gammaSize <= 0) {
LOGM(ERR, "Output {} doesn't support gamma", pMonitor->szName); LOGM(ERR, "Output {} doesn't support gamma", pMonitor->szName);
@ -81,6 +81,24 @@ CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> resource_, wl_resource* out
gammaTableSet = true; gammaTableSet = true;
close(fd); 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(); applyToMonitor();
}); });
@ -95,7 +113,7 @@ CGammaControl::~CGammaControl() {
return; return;
// reset the LUT if the client dies for whatever reason and doesn't unset the gamma // 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() { bool CGammaControl::good() {
@ -109,19 +127,15 @@ void CGammaControl::applyToMonitor() {
LOGM(LOG, "setting to monitor {}", pMonitor->szName); LOGM(LOG, "setting to monitor {}", pMonitor->szName);
if (!gammaTableSet) { if (!gammaTableSet) {
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr); pMonitor->output->state->setGammaLut({});
return; return;
} }
uint16_t* red = &gammaTable.at(0); pMonitor->output->state->setGammaLut(gammaTable);
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);
if (!pMonitor->state.test()) { if (!pMonitor->state.test()) {
LOGM(LOG, "setting to monitor {} failed", pMonitor->szName); 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); g_pHyprRenderer->damageMonitor(pMonitor);

View file

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

View file

@ -4,6 +4,7 @@
#include "../devices/IKeyboard.hpp" #include "../devices/IKeyboard.hpp"
#include <sys/mman.h> #include <sys/mman.h>
#include "core/Compositor.hpp" #include "core/Compositor.hpp"
#include <cstring>
#define LOGM PROTO::ime->protoLog #define LOGM PROTO::ime->protoLog
@ -19,7 +20,7 @@ CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboar
return; return;
} }
sendKeyboardData(g_pSeatManager->keyboard->wlr()); sendKeyboardData(g_pSeatManager->keyboard.lock());
} }
CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() { CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
@ -27,37 +28,36 @@ CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
std::erase_if(owner->grabs, [](const auto& g) { return g.expired(); }); 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) if (keyboard == pLastKeyboard)
return; return;
pLastKeyboard = keyboard; pLastKeyboard = keyboard;
int keymapFD = allocateSHMFile(keyboard->keymap_size); int keymapFD = allocateSHMFile(keyboard->xkbKeymapString.length() + 1);
if (keymapFD < 0) { if (keymapFD < 0) {
LOGM(ERR, "Failed to create a keymap file for keyboard grab"); LOGM(ERR, "Failed to create a keymap file for keyboard grab");
return; 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) { if (data == MAP_FAILED) {
LOGM(ERR, "Failed to mmap a keymap file for keyboard grab"); LOGM(ERR, "Failed to mmap a keymap file for keyboard grab");
close(keymapFD); close(keymapFD);
return; return;
} }
memcpy(data, keyboard->keymap_string, keyboard->keymap_size); memcpy(data, keyboard->xkbKeymapString.c_str(), keyboard->xkbKeymapString.length());
munmap(data, keyboard->keymap_size); 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); close(keymapFD);
const auto MODS = keyboard->modifiers; sendMods(keyboard->modifiersState.depressed, keyboard->modifiersState.latched, keyboard->modifiersState.locked, keyboard->modifiersState.group);
sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.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) { 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) { for (auto& gw : grabs) {
auto g = gw.lock(); auto g = gw.lock();

View file

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

View file

@ -6,6 +6,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <xf86drm.h> #include <xf86drm.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h>
#include "core/Compositor.hpp" #include "core/Compositor.hpp"
#include "types/DMABuffer.hpp" #include "types/DMABuffer.hpp"
#include "types/WLBuffer.hpp" #include "types/WLBuffer.hpp"
@ -22,21 +23,66 @@ static std::optional<dev_t> devIDFromFD(int fd) {
return stat.st_rdev; 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; std::set<std::pair<uint32_t, uint64_t>> formats;
for (auto& t : tranches_) {
for (auto& fmt : t.formats) { // insert formats into vec if they got inserted into set, meaning they're unique
for (auto& mod : fmt.mods) { size_t i = 0;
formats.insert(std::make_pair<>(fmt.format, mod));
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); 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());
}
}
}
}
tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry);
int fds[2] = {0}; int fds[2] = {0};
allocateSHMFilePair(tableLen, &fds[0], &fds[1]); allocateSHMFilePair(tableSize, &fds[0], &fds[1]);
auto arr = (SDMABUFFeedbackTableEntry*)mmap(nullptr, tableLen, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0); auto arr = (SDMABUFFormatTableEntry*)mmap(nullptr, tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
if (arr == MAP_FAILED) { if (arr == MAP_FAILED) {
LOGM(ERR, "mmap failed"); LOGM(ERR, "mmap failed");
@ -47,33 +93,18 @@ CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMAB
close(fds[0]); close(fds[0]);
std::vector<std::pair<uint32_t, uint64_t>> formatsVec; std::copy(formatsVec.begin(), formatsVec.end(), arr);
for (auto& f : formats) {
formatsVec.push_back(f);
}
size_t i = 0; munmap(arr, tableSize);
for (auto& [fmt, mod] : formatsVec) {
arr[i++] = SDMABUFFeedbackTableEntry{
.fmt = fmt,
.modifier = mod,
};
}
munmap(arr, tableLen);
mainDevice = device;
tableFD = fds[1]; 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.
} }
CCompiledDMABUFFeedback::~CCompiledDMABUFFeedback() { CDMABUFFormatTable::~CDMABUFFormatTable() {
close(tableFD); 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 = makeShared<CDMABuffer>(id, client, attrs);
buffer->resource->buffer = buffer; buffer->resource->buffer = buffer;
@ -103,7 +134,7 @@ CLinuxDMABBUFParamsResource::CLinuxDMABBUFParamsResource(SP<CZwpLinuxBufferParam
resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); });
resource->setDestroy([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; attrs->success = true;
@ -190,7 +221,7 @@ void CLinuxDMABBUFParamsResource::create(uint32_t id) {
return; 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) { 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]); 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->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
if (surface) auto& formatTable = PROTO::linuxDma->formatTable;
LOGM(ERR, "FIXME: surface feedback stub"); resource->sendFormatTable(formatTable->tableFD, formatTable->tableSize);
sendDefaultFeedback();
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();
} }
CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() { CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() {
@ -313,6 +321,41 @@ bool CLinuxDMABUFFeedbackResource::good() {
return resource->resource(); 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_) { CLinuxDMABUFResource::CLinuxDMABUFResource(SP<CZwpLinuxDmabufV1> resource_) : resource(resource_) {
if (!good()) if (!good())
return; return;
@ -361,48 +404,81 @@ bool CLinuxDMABUFResource::good() {
} }
void CLinuxDMABUFResource::sendMods() { void CLinuxDMABUFResource::sendMods() {
for (auto& [fmt, mod] : PROTO::linuxDma->defaultFeedback->tranches) { for (auto& fmt : PROTO::linuxDma->formatTable->rendererTranche.formats) {
for (auto& mod : fmt.modifiers) {
if (resource->version() < 3) { if (resource->version() < 3) {
if (mod == DRM_FORMAT_MOD_INVALID) if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
resource->sendFormat(fmt); resource->sendFormat(fmt.drmFormat);
continue; continue;
} }
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 // TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
resource->sendModifier(fmt, mod >> 32, mod & 0xFFFFFFFF); resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF);
}
} }
} }
CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { 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) { 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); auto dev = devIDFromFD(rendererFD);
if (!dev.has_value()) { if (!dev.has_value()) {
LOGM(ERR, "failed to get drm dev"); protoLog(ERR, "failed to get drm dev, disabling linux dmabuf");
PROTO::linuxDma.reset(); removeGlobal();
return; return;
} }
mainDevice = *dev; mainDevice = *dev;
auto fmts = g_pHyprOpenGL->getDRMFormats(); SDMABUFTranche eglTranche = {
.device = mainDevice,
SDMABufTranche tranche = { .flags = 0, // renderer isnt for ds so dont set flag.
.device = *dev, .formats = g_pHyprOpenGL->getDRMFormats(),
.formats = fmts,
}; };
std::vector<SDMABufTranche> tches; std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tches;
tches.push_back(tranche);
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; drmDevice* device = nullptr;
if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) { if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) {
LOGM(ERR, "failed to get drm dev"); protoLog(ERR, "failed to get drm dev, disabling linux dmabuf");
PROTO::linuxDma.reset(); removeGlobal();
return; return;
} }
@ -411,17 +487,51 @@ CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const
mainDeviceFD = open(name, O_RDWR | O_CLOEXEC); mainDeviceFD = open(name, O_RDWR | O_CLOEXEC);
drmFreeDevice(&device); drmFreeDevice(&device);
if (mainDeviceFD < 0) { if (mainDeviceFD < 0) {
LOGM(ERR, "failed to open drm dev"); protoLog(ERR, "failed to open drm dev, disabling linux dmabuf");
PROTO::linuxDma.reset(); removeGlobal();
return; return;
} }
} else { } 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); 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() { CLinuxDMABufV1Protocol::~CLinuxDMABufV1Protocol() {
if (mainDeviceFD >= 0) if (mainDeviceFD >= 0)
close(mainDeviceFD); close(mainDeviceFD);
@ -452,3 +562,52 @@ void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABBUFParamsResource* resour
void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) { void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) {
std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == 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 "wayland.hpp"
#include "linux-dmabuf-v1.hpp" #include "linux-dmabuf-v1.hpp"
#include "../helpers/signal/Signal.hpp" #include "../helpers/signal/Signal.hpp"
#include "../helpers/Format.hpp"
#include "../helpers/Monitor.hpp"
#include <aquamarine/buffer/Buffer.hpp>
class CDMABuffer; class CDMABuffer;
struct SDRMFormat;
struct SDMABUFAttrs;
class CWLSurfaceResource; class CWLSurfaceResource;
class CLinuxDMABuffer { class CLinuxDMABuffer {
public: public:
CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs); CLinuxDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs);
~CLinuxDMABuffer(); ~CLinuxDMABuffer();
bool good(); bool good();
@ -31,34 +32,29 @@ class CLinuxDMABuffer {
}; };
#pragma pack(push, 1) #pragma pack(push, 1)
struct SDMABUFFeedbackTableEntry { struct SDMABUFFormatTableEntry {
uint32_t fmt = 0; uint32_t fmt = 0;
char pad[4]; char pad[4];
uint64_t modifier = 0; uint64_t modifier = 0;
}; };
#pragma pack(pop) #pragma pack(pop)
class SCompiledDMABUFTranche { struct SDMABUFTranche {
dev_t device = 0;
uint32_t flags = 0;
std::vector<uint16_t> indices;
};
struct SDMABufTranche {
dev_t device = 0; dev_t device = 0;
uint32_t flags = 0; uint32_t flags = 0;
std::vector<SDRMFormat> formats; std::vector<SDRMFormat> formats;
std::vector<uint16_t> indicies;
}; };
class CCompiledDMABUFFeedback { class CDMABUFFormatTable {
public: public:
CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches); CDMABUFFormatTable(SDMABUFTranche rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches);
~CCompiledDMABUFFeedback(); ~CDMABUFFormatTable();
dev_t mainDevice = 0;
int tableFD = -1; int tableFD = -1;
size_t tableLen = 0; size_t tableSize = 0;
std::vector<std::pair<uint32_t, uint64_t>> tranches; SDMABUFTranche rendererTranche;
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> monitorTranches;
}; };
class CLinuxDMABBUFParamsResource { class CLinuxDMABBUFParamsResource {
@ -69,7 +65,7 @@ class CLinuxDMABBUFParamsResource {
bool good(); bool good();
void create(uint32_t id); // 0 means not immed void create(uint32_t id); // 0 means not immed
SP<SDMABUFAttrs> attrs; SP<Aquamarine::SDMABUFAttrs> attrs;
WP<CLinuxDMABuffer> createdBuffer; WP<CLinuxDMABuffer> createdBuffer;
bool used = false; bool used = false;
@ -86,11 +82,16 @@ class CLinuxDMABUFFeedbackResource {
~CLinuxDMABUFFeedbackResource(); ~CLinuxDMABUFFeedbackResource();
bool good(); bool good();
void sendDefaultFeedback();
void sendTranche(SDMABUFTranche& tranche);
SP<CWLSurfaceResource> surface; // optional, for surface feedbacks SP<CWLSurfaceResource> surface; // optional, for surface feedbacks
private: private:
SP<CZwpLinuxDmabufFeedbackV1> resource; SP<CZwpLinuxDmabufFeedbackV1> resource;
bool lastFeedbackWasScanout = false;
friend class CLinuxDMABufV1Protocol;
}; };
class CLinuxDMABUFResource { class CLinuxDMABUFResource {
@ -110,6 +111,7 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
~CLinuxDMABufV1Protocol(); ~CLinuxDMABufV1Protocol();
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
void updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor);
private: private:
void destroyResource(CLinuxDMABUFResource* resource); void destroyResource(CLinuxDMABUFResource* resource);
@ -117,13 +119,15 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
void destroyResource(CLinuxDMABBUFParamsResource* resource); void destroyResource(CLinuxDMABBUFParamsResource* resource);
void destroyResource(CLinuxDMABuffer* resource); void destroyResource(CLinuxDMABuffer* resource);
void resetFormatTable();
// //
std::vector<SP<CLinuxDMABUFResource>> m_vManagers; std::vector<SP<CLinuxDMABUFResource>> m_vManagers;
std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks; std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks;
std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams; std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams;
std::vector<SP<CLinuxDMABuffer>> m_vBuffers; std::vector<SP<CLinuxDMABuffer>> m_vBuffers;
UP<CCompiledDMABUFFeedback> defaultFeedback; UP<CDMABUFFormatTable> formatTable;
dev_t mainDevice; dev_t mainDevice;
int mainDeviceFD = -1; int mainDeviceFD = -1;

View file

@ -2,12 +2,11 @@
#include <algorithm> #include <algorithm>
#include <xf86drm.h> #include <xf86drm.h>
#include "../Compositor.hpp" #include "../Compositor.hpp"
#include <wlr/render/drm_format_set.h>
#include "types/WLBuffer.hpp" #include "types/WLBuffer.hpp"
#define LOGM PROTO::mesaDRM->protoLog #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); 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) { 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]); 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; 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.success = true;
attrs.size = {w, h}; attrs.size = {w, h};
attrs.modifier = DRM_FORMAT_MOD_INVALID; attrs.modifier = mod;
attrs.planes = 1; attrs.planes = 1;
attrs.offsets[0] = off0; attrs.offsets[0] = off0;
attrs.strides[0] = str0; attrs.strides[0] = str0;
@ -87,7 +103,7 @@ CMesaDRMResource::CMesaDRMResource(SP<CWlDrm> resource_) : resource(resource_) {
auto fmts = g_pHyprOpenGL->getDRMFormats(); auto fmts = g_pHyprOpenGL->getDRMFormats();
for (auto& fmt : fmts) { 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) { CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
drmDevice* dev = nullptr; 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) { if (drmGetDevice2(drmFD, 0, &dev) != 0) {
LOGM(ERR, "Failed to get device"); protoLog(ERR, "Failed to get device, disabling MesaDRM");
PROTO::mesaDRM.reset(); removeGlobal();
return; return;
} }
@ -108,7 +124,15 @@ CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, co
nodeName = dev->nodes[DRM_NODE_RENDER]; nodeName = dev->nodes[DRM_NODE_RENDER];
} else { } else {
ASSERT(dev->available_nodes & (1 << DRM_NODE_PRIMARY)); 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]; nodeName = dev->nodes[DRM_NODE_PRIMARY];
} }
drmFreeDevice(&dev); drmFreeDevice(&dev);

View file

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

View file

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

View file

@ -6,6 +6,7 @@
#include "WaylandProtocol.hpp" #include "WaylandProtocol.hpp"
#include "wlr-output-management-unstable-v1.hpp" #include "wlr-output-management-unstable-v1.hpp"
#include "../helpers/signal/Signal.hpp" #include "../helpers/signal/Signal.hpp"
#include <aquamarine/output/Output.hpp>
class CMonitor; class CMonitor;
@ -34,15 +35,15 @@ class COutputManager {
class COutputMode { class COutputMode {
public: public:
COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_); COutputMode(SP<CZwlrOutputModeV1> resource_, SP<Aquamarine::SOutputMode> mode_);
bool good(); bool good();
wlr_output_mode* getMode(); SP<Aquamarine::SOutputMode> getMode();
void sendAllData(); void sendAllData();
private: private:
SP<CZwlrOutputModeV1> resource; SP<CZwlrOutputModeV1> resource;
wlr_output_mode* mode = nullptr; WP<Aquamarine::SOutputMode> mode;
friend class COutputHead; friend class COutputHead;
friend class COutputManagementProtocol; friend class COutputManagementProtocol;
@ -61,7 +62,7 @@ class COutputHead {
SP<CZwlrOutputHeadV1> resource; SP<CZwlrOutputHeadV1> resource;
CMonitor* pMonitor = nullptr; CMonitor* pMonitor = nullptr;
void makeAndSendNewMode(wlr_output_mode* mode); void makeAndSendNewMode(SP<Aquamarine::SOutputMode> mode);
void sendCurrentMode(); void sendCurrentMode();
std::vector<WP<COutputMode>> modes; 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; 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()) if (!pMonitor->state.commit())
LOGM(ERR, "Couldn't set dpms to {} for {}", pMonitor->dpmsStatus, pMonitor->szName); LOGM(ERR, "Couldn't set dpms to {} for {}", pMonitor->dpmsStatus, pMonitor->szName);

View file

@ -3,6 +3,8 @@
#include "../helpers/Monitor.hpp" #include "../helpers/Monitor.hpp"
#include "../managers/HookSystemManager.hpp" #include "../managers/HookSystemManager.hpp"
#include "core/Compositor.hpp" #include "core/Compositor.hpp"
#include "core/Output.hpp"
#include <aquamarine/output/Output.hpp>
#define LOGM PROTO::presentation->protoLog #define LOGM PROTO::presentation->protoLog
@ -42,12 +44,10 @@ bool CPresentationFeedback::good() {
void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) { void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) {
auto client = resource->client(); auto client = resource->client();
wl_resource* res;
wl_resource_for_each(res, &data->pMonitor->output->resources) { if (PROTO::outputs.contains(data->pMonitor->szName)) {
if (client == wl_resource_get_client(res)) { if (auto outputResource = PROTO::outputs.at(data->pMonitor->szName)->outputResourceFrom(client); outputResource)
resource->sendSyncOutput(res); resource->sendSyncOutput(outputResource->getResource()->resource());
break;
}
} }
uint32_t flags = 0; uint32_t flags = 0;
@ -55,12 +55,12 @@ void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespe
flags |= WP_PRESENTATION_FEEDBACK_KIND_VSYNC; flags |= WP_PRESENTATION_FEEDBACK_KIND_VSYNC;
if (data->zeroCopy) if (data->zeroCopy)
flags |= WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY; 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; 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; 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), 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); (uint32_t)(seq & 0xFFFFFFFF), (wpPresentationFeedbackKind)flags);
else else

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