diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 08a9ad9c..285a15a1 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -9,11 +9,26 @@ body: --- + - type: dropdown + id: type + attributes: + label: Bug or Regression? + description: Is this a bug or a regression? + options: + - Bug + - Regression + validations: + required: true + - type: textarea id: ver attributes: - label: Hyprland Version - description: "Paste the output of `hyprctl systeminfo` here." + label: System Info and Version + description: | + Paste the output of `hyprctl systeminfo -c` here (If you are on a + version that shows you help menu, omit the `-c` and attach config files + to the issue). If you have configs outside of the main config shown + here, please attach. value: "
System/Version info @@ -29,17 +44,6 @@ body: validations: required: true - - type: dropdown - id: type - attributes: - label: Bug or Regression? - description: Is this a bug or a regression? - options: - - Bug - - Regression - validations: - required: true - - type: textarea id: desc attributes: diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..a0685fcf --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,83 @@ +assets: + - changed-files: + - any-glob-to-any-file: "assets/**" + +docs: + - changed-files: + - any-glob-to-any-file: "docs/**" + +hyprctl: + - changed-files: + - any-glob-to-any-file: "hyprctl/**" + +hyprpm: + - changed-files: + - any-glob-to-any-file: "hyprpm/**" + +nix: + - changed-files: + - any-glob-to-any-file: "nix/**" + +protocols: + - changed-files: + - any-glob-to-any-file: ["protocols/**", "src/protocols/**"] + +core: + - changed-files: + - any-glob-to-any-file: "src/**" + +config: + - changed-files: + - any-glob-to-any-file: "src/config/**" + +debug: + - changed-files: + - any-glob-to-any-file: "src/debug/**" + +desktop: + - changed-files: + - any-glob-to-any-file: "src/desktop/**" + +devices: + - changed-files: + - any-glob-to-any-file: "src/devices/**" + +events: + - changed-files: + - any-glob-to-any-file: "src/events/**" + +helpers: + - changed-files: + - any-glob-to-any-file: "src/helpers/**" + +hyprerror: + - changed-files: + - any-glob-to-any-file: "src/hyprerror/**" + +init: + - changed-files: + - any-glob-to-any-file: "src/init/**" + +layout: + - changed-files: + - any-glob-to-any-file: "src/layout/**" + +managers: + - changed-files: + - any-glob-to-any-file: "src/managers/**" + +pch: + - changed-files: + - any-glob-to-any-file: "src/pch/**" + +plugins: + - changed-files: + - any-glob-to-any-file: "src/plugins/**" + +render: + - changed-files: + - any-glob-to-any-file: "src/render/**" + +xwayland: + - changed-files: + - any-glob-to-any-file: "src/xwayland/**" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 00000000..52474c6a --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,12 @@ +name: "Pull Request Labeler" +on: + - pull_request_target + +jobs: + labeler: + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 diff --git a/CMakeLists.txt b/CMakeLists.txt index 40e0f32a..43cbb50c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,9 +113,11 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo pixman-1 libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm - hyprwayland-scanner>=0.3.8 hyprlang>=0.3.2 hyprcursor>=0.1.7 + hyprlang>=0.3.2 hyprcursor>=0.1.7 ) +find_package(hyprwayland-scanner 0.3.10 REQUIRED) + file(GLOB_RECURSE SRCFILES "src/*.cpp") set(TRACY_CPP_FILES "") @@ -190,12 +192,8 @@ if(NO_XWAYLAND) add_compile_definitions(NO_XWAYLAND) else() message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...") - pkg_check_modules(xdeps REQUIRED IMPORTED_TARGET xcb xwayland xcb-util xcb-render xcb-xfixes xcb-icccm xcb-composite xcb-res xcb-ewmh) - pkg_check_modules(xcb_errors IMPORTED_TARGET 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) - if(xcb_errors_FOUND) - target_link_libraries(Hyprland PkgConfig::xcb_errors) - endif() endif() if(NO_SYSTEMD) @@ -217,44 +215,54 @@ message(STATUS "Setting link libraries") target_link_libraries(Hyprland rt PkgConfig::deps) +# used by `make installheaders`, to ensure the headers are generated +add_custom_target(generate-protocol-headers) + function(protocol protoPath protoName external) if (external) - execute_process( - COMMAND ${WaylandScanner} server-header ${protoPath} protocols/${protoName}-protocol.h - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process( - COMMAND ${WaylandScanner} private-code ${protoPath} protocols/${protoName}-protocol.c - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c) + set(path ${CMAKE_SOURCE_DIR}/${protoPath}) else() - execute_process( - COMMAND ${WaylandScanner} server-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.h - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - execute_process( - COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.c - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c) + set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) endif() + + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h + COMMAND ${WaylandScanner} server-header ${path} protocols/${protoName}-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c + COMMAND ${WaylandScanner} private-code ${path} protocols/${protoName}-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + target_sources(Hyprland PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c) + target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h) endfunction() + function(protocolNew protoPath protoName external) if (external) - execute_process( - COMMAND hyprwayland-scanner ${protoPath} ${CMAKE_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) + set(path ${CMAKE_SOURCE_DIR}/${protoPath}) else() - execute_process( - COMMAND hyprwayland-scanner ${WAYLAND_PROTOCOLS_DIR}/${protoPath} ${CMAKE_SOURCE_DIR}/protocols/ - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) - target_sources(Hyprland PRIVATE protocols/${protoName}.cpp) + set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) endif() + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp + ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp + COMMAND hyprwayland-scanner ${path}/${protoName}.xml ${CMAKE_SOURCE_DIR}/protocols/ + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + target_sources(Hyprland PRIVATE protocols/${protoName}.cpp protocols/${protoName}.hpp) + target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp) endfunction() function(protocolWayland) - execute_process( + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp + ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp COMMAND hyprwayland-scanner --wayland-enums ${WAYLAND_SERVER_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) - target_sources(Hyprland PRIVATE protocols/wayland.cpp) + target_sources(Hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp) + target_sources(generate-protocol-headers PRIVATE ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp) endfunction() target_link_libraries(Hyprland @@ -269,40 +277,43 @@ target_link_libraries(Hyprland protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true) protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true) -protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false) protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false) -protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true) -protocolNew("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true) -protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true) -protocolNew("protocols/virtual-keyboard-unstable-v1.xml" "virtual-keyboard-unstable-v1" true) -protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer-unstable-v1" true) -protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true) -protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true) -protocolNew("protocols/kde-server-decoration.xml" "kde-server-decoration" true) -protocolNew("protocols/wlr-data-control-unstable-v1.xml" "wlr-data-control-unstable-v1" true) -protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true) -protocolNew("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true) -protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) -protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false) -protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false) -protocolNew("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false) -protocolNew("unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" "idle-inhibit-unstable-v1" false) -protocolNew("unstable/relative-pointer/relative-pointer-unstable-v1.xml" "relative-pointer-unstable-v1" false) -protocolNew("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" "xdg-decoration-unstable-v1" false) -protocolNew("staging/alpha-modifier/alpha-modifier-v1.xml" "alpha-modifier-v1" false) -protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml" "ext-foreign-toplevel-list-v1" false) -protocolNew("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml" "pointer-gestures-unstable-v1" false) -protocolNew("unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml" "keyboard-shortcuts-inhibit-unstable-v1" false) -protocolNew("unstable/text-input/text-input-unstable-v3.xml" "text-input-unstable-v3" false) -protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" false) -protocolNew("staging/xdg-activation/xdg-activation-v1.xml" "xdg-activation-v1" false) -protocolNew("staging/ext-idle-notify/ext-idle-notify-v1.xml" "ext-idle-notify-v1" false) -protocolNew("staging/ext-session-lock/ext-session-lock-v1.xml" "ext-session-lock-v1" false) -protocolNew("stable/tablet/tablet-v2.xml" "tablet-v2" false) -protocolNew("stable/presentation-time/presentation-time.xml" "presentation-time" false) -protocolNew("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false) -protocolNew("unstable/primary-selection/primary-selection-unstable-v1.xml" "primary-selection-unstable-v1" false) +protocolNew("protocols" "wlr-gamma-control-unstable-v1" true) +protocolNew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true) +protocolNew("protocols" "wlr-output-power-management-unstable-v1" true) +protocolNew("protocols" "virtual-keyboard-unstable-v1" true) +protocolNew("protocols" "wlr-virtual-pointer-unstable-v1" true) +protocolNew("protocols" "input-method-unstable-v2" true) +protocolNew("protocols" "wlr-output-management-unstable-v1" true) +protocolNew("protocols" "kde-server-decoration" true) +protocolNew("protocols" "wlr-data-control-unstable-v1" true) +protocolNew("subprojects/hyprland-protocols/protocols" "hyprland-focus-grab-v1" true) +protocolNew("protocols" "wlr-layer-shell-unstable-v1" true) +protocolNew("protocols" "wayland-drm" true) +protocolNew("staging/tearing-control" "tearing-control-v1" false) +protocolNew("staging/fractional-scale" "fractional-scale-v1" false) +protocolNew("unstable/xdg-output" "xdg-output-unstable-v1" false) +protocolNew("staging/cursor-shape" "cursor-shape-v1" false) +protocolNew("unstable/idle-inhibit" "idle-inhibit-unstable-v1" false) +protocolNew("unstable/relative-pointer" "relative-pointer-unstable-v1" false) +protocolNew("unstable/xdg-decoration" "xdg-decoration-unstable-v1" false) +protocolNew("staging/alpha-modifier" "alpha-modifier-v1" false) +protocolNew("staging/ext-foreign-toplevel-list" "ext-foreign-toplevel-list-v1" false) +protocolNew("unstable/pointer-gestures" "pointer-gestures-unstable-v1" false) +protocolNew("unstable/keyboard-shortcuts-inhibit" "keyboard-shortcuts-inhibit-unstable-v1" false) +protocolNew("unstable/text-input" "text-input-unstable-v3" false) +protocolNew("unstable/pointer-constraints" "pointer-constraints-unstable-v1" false) +protocolNew("staging/xdg-activation" "xdg-activation-v1" false) +protocolNew("staging/ext-idle-notify" "ext-idle-notify-v1" false) +protocolNew("staging/ext-session-lock" "ext-session-lock-v1" false) +protocolNew("stable/tablet" "tablet-v2" false) +protocolNew("stable/presentation-time" "presentation-time" false) +protocolNew("stable/xdg-shell" "xdg-shell" false) +protocolNew("unstable/primary-selection" "primary-selection-unstable-v1" false) +protocolNew("staging/xwayland-shell" "xwayland-shell-v1" false) +protocolNew("stable/viewporter" "viewporter" false) +protocolNew("stable/linux-dmabuf" "linux-dmabuf-v1" false) protocolWayland() diff --git a/Makefile b/Makefile index e7b9c1b6..a33f4cb7 100644 --- a/Makefile +++ b/Makefile @@ -53,6 +53,8 @@ installheaders: mkdir -p ${PREFIX}/include/hyprland/wlr mkdir -p ${PREFIX}/share/pkgconfig + cmake --build ./build --config Release --target generate-protocol-headers + find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland cd subprojects/wlroots-hyprland/include/wlr && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../.. cd subprojects/wlroots-hyprland/build/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlr && cd ../../../.. diff --git a/flake.lock b/flake.lock index eba1996a..ab886a15 100644 --- a/flake.lock +++ b/flake.lock @@ -13,11 +13,11 @@ ] }, "locked": { - "lastModified": 1715791817, - "narHash": "sha256-J069Uhv/gCMFLX1dSh2f+9ZTM09r1Nv3oUfocCnWKow=", + "lastModified": 1717181720, + "narHash": "sha256-yv+QZWsusu/NWjydkxixHC2g+tIJ9v+xkE2EiVpJj6g=", "owner": "hyprwm", "repo": "hyprcursor", - "rev": "7c3aa03dffb53921e583ade3d4ae3f487e390e7e", + "rev": "9e27a2c2ceb1e0b85bd55b0afefad196056fe87c", "type": "github" }, "original": { @@ -61,11 +61,11 @@ ] }, "locked": { - "lastModified": 1715791527, - "narHash": "sha256-HhQ4zvGHrRjR63ltySSeg+x+0jb0lepiutWdnFhLRoo=", + "lastModified": 1716473782, + "narHash": "sha256-+qLn4lsHU6iL3+HTo1gTQ1tWzet8K9h+IfVemzEQZj8=", "owner": "hyprwm", "repo": "hyprlang", - "rev": "969cb076e5b76f2e823aeca1937a3e1f159812ee", + "rev": "87d5d984109c839482b88b4795db073eb9ed446f", "type": "github" }, "original": { @@ -84,11 +84,11 @@ ] }, "locked": { - "lastModified": 1716058375, - "narHash": "sha256-CwjWoVnBZE5SBpRx9dgSQGCr4Goxyfcyv3zZbOhVqzk=", + "lastModified": 1717784906, + "narHash": "sha256-YxmfxHfWed1fosaa7fC1u7XoKp1anEZU+7Lh/ojRKoM=", "owner": "hyprwm", "repo": "hyprwayland-scanner", - "rev": "3afed4364790aebe0426077631af1e164a9650cc", + "rev": "0f30f9eca6e404130988554accbb64d1c9ec877d", "type": "github" }, "original": { @@ -99,11 +99,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1716137900, - "narHash": "sha256-sowPU+tLQv8GlqtVtsXioTKeaQvlMz/pefcdwg8MvfM=", + "lastModified": 1717602782, + "narHash": "sha256-pL9jeus5QpX5R+9rsp3hhZ+uplVHscNJh8n8VpqscM0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "6c0b7a92c30122196a761b440ac0d46d3d9954f1", + "rev": "e8057b67ebf307f01bdcc8fba94d94f75039d1f6", "type": "github" }, "original": { diff --git a/hyprctl/Strings.hpp b/hyprctl/Strings.hpp index 951c6a7f..76e87ecb 100644 --- a/hyprctl/Strings.hpp +++ b/hyprctl/Strings.hpp @@ -60,6 +60,7 @@ flags: --batch → Execute a batch of commands, separated by ';' --instance (-i) → use a specific instance. Can be either signature or index in hyprctl instances (0, 1, etc) + --quiet (-q) → Disable the output of hyprctl --help: Can be used to print command's arguments that did not fit into this page @@ -155,4 +156,4 @@ cmd: starting from 0 flags: - See 'hyprctl --help')#"; \ No newline at end of file + See 'hyprctl --help')#"; diff --git a/hyprctl/hyprctl.bash b/hyprctl/hyprctl.bash index 279ee518..8386cfbc 100644 --- a/hyprctl/hyprctl.bash +++ b/hyprctl/hyprctl.bash @@ -2,7 +2,7 @@ _hyprctl_cmd_2 () { hyprctl monitors | grep Monitor | awk '{ print $2 }' } -_hyprctl_cmd_1 () { +_hyprctl_cmd_3 () { hyprpm list | grep "Plugin" | awk '{print $4}' } @@ -10,7 +10,7 @@ _hyprctl_cmd_0 () { hyprctl clients | grep class | awk '{print $2}' } -_hyprctl_cmd_3 () { +_hyprctl_cmd_1 () { hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}' } @@ -23,25 +23,25 @@ _hyprctl () { local words cword _get_comp_words_by_ref -n "$COMP_WORDBREAKS" words cword - local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow") + local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "-q" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "--quiet" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "tagwindow" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "sendshortcut" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow") declare -A literal_transitions - literal_transitions[0]="([103]=1 [74]=2 [33]=3 [1]=2 [2]=2 [77]=2 [105]=4 [36]=2 [108]=5 [40]=2 [45]=2 [112]=2 [84]=6 [113]=8 [51]=2 [53]=2 [88]=9 [117]=2 [119]=2 [121]=2 [15]=2 [58]=10 [59]=2 [17]=11 [122]=12 [19]=2 [124]=2 [126]=2 [25]=13 [67]=2 [96]=5 [97]=2 [27]=2 [28]=14 [100]=2 [102]=5)" - literal_transitions[3]="([72]=18 [13]=2 [32]=18 [54]=18 [55]=18 [89]=18 [104]=2 [120]=2 [76]=1 [16]=2 [123]=18 [3]=1 [5]=2 [63]=18 [127]=2 [129]=18 [80]=18 [130]=18 [83]=18 [31]=18 [48]=2 [12]=2 [85]=18 [10]=18 [86]=18 [137]=18)" - literal_transitions[7]="([103]=1 [74]=2 [33]=3 [1]=2 [2]=2 [77]=2 [105]=4 [36]=2 [40]=2 [45]=2 [112]=2 [84]=6 [113]=8 [51]=2 [53]=2 [88]=9 [117]=2 [119]=2 [121]=2 [15]=2 [58]=10 [59]=2 [17]=11 [122]=12 [19]=2 [124]=2 [126]=2 [25]=13 [67]=2 [97]=2 [27]=2 [28]=14 [100]=2)" - literal_transitions[8]="([128]=2 [131]=2 [0]=2 [73]=2 [35]=2 [106]=2 [37]=2 [107]=2 [4]=2 [78]=2 [39]=2 [79]=2 [110]=2 [6]=2 [41]=2 [42]=2 [81]=2 [82]=2 [46]=2 [47]=2 [9]=2 [109]=2 [50]=2 [52]=2 [11]=2 [115]=2 [87]=2 [49]=2 [56]=2 [90]=2 [57]=2 [91]=2 [92]=2 [60]=2 [61]=2 [125]=2 [93]=2 [62]=2 [20]=2 [95]=2 [22]=2 [23]=2 [64]=2 [65]=2 [24]=2 [132]=2 [26]=2 [68]=2 [98]=2 [69]=2 [29]=2 [136]=2 [70]=2 [99]=2)" - literal_transitions[9]="([114]=15 [111]=16)" - literal_transitions[11]="([101]=2)" - literal_transitions[13]="([21]=1 [116]=1 [30]=1 [135]=1 [118]=1 [43]=1 [71]=1)" - literal_transitions[14]="([38]=2)" - literal_transitions[15]="([8]=2 [66]=2 [14]=2 [133]=2)" - literal_transitions[17]="([75]=19)" - literal_transitions[18]="([18]=2 [7]=2)" - literal_transitions[19]="([34]=5 [44]=5)" - literal_transitions[20]="([134]=2 [94]=2)" + literal_transitions[0]="([105]=1 [75]=2 [33]=3 [35]=4 [1]=2 [2]=2 [78]=2 [107]=5 [37]=2 [111]=4 [41]=2 [46]=2 [115]=2 [85]=6 [116]=8 [52]=2 [88]=4 [54]=2 [90]=9 [120]=2 [122]=2 [124]=2 [15]=2 [59]=10 [60]=2 [17]=11 [125]=12 [19]=2 [127]=2 [129]=2 [25]=13 [68]=2 [98]=4 [99]=2 [27]=2 [28]=14 [102]=2 [104]=4)" + literal_transitions[3]="([73]=17 [13]=2 [32]=17 [55]=17 [56]=17 [91]=17 [106]=2 [123]=2 [77]=1 [16]=2 [126]=17 [3]=1 [5]=2 [64]=17 [131]=2 [133]=17 [81]=17 [134]=17 [84]=17 [31]=17 [49]=2 [12]=2 [86]=17 [10]=17 [87]=17 [141]=17)" + literal_transitions[7]="([105]=1 [75]=2 [33]=3 [1]=2 [2]=2 [78]=2 [107]=5 [37]=2 [41]=2 [46]=2 [115]=2 [85]=6 [116]=8 [52]=2 [54]=2 [90]=9 [120]=2 [122]=2 [124]=2 [15]=2 [59]=10 [60]=2 [17]=11 [125]=12 [19]=2 [127]=2 [129]=2 [25]=13 [68]=2 [99]=2 [27]=2 [28]=14 [102]=2)" + literal_transitions[8]="([101]=2 [130]=2 [132]=2 [0]=2 [74]=2 [36]=2 [108]=2 [109]=2 [38]=2 [110]=2 [4]=2 [79]=2 [40]=2 [80]=2 [113]=2 [6]=2 [42]=2 [43]=2 [82]=2 [83]=2 [47]=2 [48]=2 [9]=2 [50]=2 [51]=2 [53]=2 [11]=2 [112]=2 [89]=2 [118]=2 [57]=2 [92]=2 [58]=2 [93]=2 [94]=2 [61]=2 [62]=2 [128]=2 [95]=2 [63]=2 [20]=2 [97]=2 [22]=2 [23]=2 [65]=2 [66]=2 [135]=2 [136]=2 [24]=2 [26]=2 [69]=2 [100]=2 [70]=2 [140]=2 [29]=2 [71]=2)" + literal_transitions[9]="([117]=20 [114]=16)" + literal_transitions[11]="([103]=2)" + literal_transitions[13]="([21]=1 [119]=1 [30]=1 [139]=1 [121]=1 [44]=1 [72]=1)" + literal_transitions[14]="([39]=2)" + literal_transitions[15]="([138]=2 [96]=2)" + literal_transitions[17]="([18]=2 [7]=2)" + literal_transitions[18]="([76]=19)" + literal_transitions[19]="([34]=4 [45]=4)" + literal_transitions[20]="([8]=2 [67]=2 [14]=2 [137]=2)" declare -A match_anything_transitions - match_anything_transitions=([1]=2 [0]=7 [6]=2 [20]=2 [10]=2 [2]=17 [7]=7 [12]=2 [14]=17 [16]=2 [4]=20 [11]=17) + match_anything_transitions=([1]=2 [0]=7 [6]=2 [15]=2 [10]=2 [5]=15 [14]=18 [7]=7 [2]=18 [16]=2 [12]=2 [11]=18) declare -A subword_transitions local state=0 @@ -108,7 +108,7 @@ _hyprctl () { done fi declare -A commands - commands=([16]=2 [4]=3 [12]=1 [10]=0) + commands=([5]=1 [16]=2 [12]=3 [10]=0) if [[ -v "commands[$state]" ]]; then local command_id=${commands[$state]} local completions=() diff --git a/hyprctl/hyprctl.fish b/hyprctl/hyprctl.fish index 5d8d12cb..2a75eb12 100644 --- a/hyprctl/hyprctl.fish +++ b/hyprctl/hyprctl.fish @@ -3,7 +3,7 @@ function _hyprctl_3 hyprctl monitors | grep Monitor | awk '{ print $2 }' end -function _hyprctl_2 +function _hyprctl_4 set 1 $argv[1] hyprpm list | grep "Plugin" | awk '{print $4}' end @@ -13,7 +13,7 @@ function _hyprctl_1 hyprctl clients | grep class | awk '{print $2}' end -function _hyprctl_4 +function _hyprctl_2 set 1 $argv[1] hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}' end @@ -29,7 +29,7 @@ function _hyprctl set COMP_CWORD (count $COMP_WORDS) end - set --local literals "cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow" + set --local literals "cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "-q" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "--quiet" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "tagwindow" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "sendshortcut" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow" set --local descriptions set descriptions[1] "Focus the next window on a workspace" @@ -54,100 +54,104 @@ function _hyprctl set descriptions[31] "CONFUSED" set descriptions[34] "Set a property of a window" set descriptions[35] "Specify the Hyprland instance" - set descriptions[36] "Toggle the current window's floating state" - set descriptions[37] "Get the list of defined workspace rules" - set descriptions[38] "Move the focused window to a workspace" - set descriptions[40] "Temporarily enable or disable binds:ignore_group_lock" - set descriptions[41] "List all workspaces with their properties" - set descriptions[42] "Swap the active window with the next or previous in a group" - set descriptions[43] "Close a specified window" - set descriptions[44] "WARNING" - set descriptions[45] "Specify the Hyprland instance" - set descriptions[46] "List all registered binds" - set descriptions[47] "Move the active window in a direction or to a monitor" - set descriptions[48] "Change the split ratio" - set descriptions[50] "Prohibit the active window from becoming or being inserted into group" - set descriptions[51] "Change the workspace" - set descriptions[52] "List all current config parsing errors" - set descriptions[53] "Toggle the current active window into a group" - set descriptions[54] "Get the config option status (values)" - set descriptions[57] "Close the active window" - set descriptions[58] "Pass the key to a specified window" - set descriptions[59] "List all decorations and their info" - set descriptions[60] "List all connected keyboards and mice" - set descriptions[61] "Switch focus from current to previously focused window" - set descriptions[62] "Change the current mapping group" - set descriptions[63] "Execute a Global Shortcut using the GlobalShortcuts portal" - set descriptions[65] "Force the renderer to reload all resources and outputs" - set descriptions[66] "Move a selected window" - set descriptions[68] "Print the Hyprland version: flags, commit and branch of build" - set descriptions[69] "Set all monitors' DPMS status" - set descriptions[70] "Resize the active window" - set descriptions[71] "Move the active window into a group" - set descriptions[72] "OK" - set descriptions[74] "Set the current window's floating state to true" - set descriptions[75] "Print tail of the log" - set descriptions[78] "List all layouts available (including plugin ones)" - set descriptions[79] "Move a workspace to a monitor" - set descriptions[80] "Execute a shell command" - set descriptions[82] "Modify the window stack order of the active or specified window" - set descriptions[83] "Toggle the focused window's internal fullscreen state" - set descriptions[85] "Issue a keyword to call a config keyword dynamically" - set descriptions[88] "Pin a window" - set descriptions[89] "Allows adding/removing fake outputs to a specific backend" - set descriptions[91] "Toggle a special workspace on/off" - set descriptions[92] "Toggle the focused window's fullscreen state" - set descriptions[93] "Toggle the current window to always be opaque" - set descriptions[94] "Focus the requested workspace" - set descriptions[96] "Switch to the next window in a group" - set descriptions[97] "Output in JSON format" - set descriptions[98] "List all running Hyprland instances and their info" - set descriptions[99] "Execute a raw shell command" - set descriptions[100] "Exit the compositor with no questions asked" - set descriptions[101] "List all windows with their properties" - set descriptions[103] "Execute a batch of commands separated by ;" - set descriptions[104] "Dismiss all or up to amount of notifications" - set descriptions[106] "Set the xkb layout index for a keyboard" - set descriptions[107] "Move window doesnt switch to the workspace" - set descriptions[108] "Behave as moveintogroup" - set descriptions[109] "Refresh state after issuing the command" - set descriptions[110] "Move the focus in a direction" - set descriptions[111] "Focus the urgent window or the last window" - set descriptions[113] "Get the active workspace name and its properties" - set descriptions[114] "Issue a dispatch to call a keybind dispatcher with an arg" - set descriptions[116] "Center the active window" - set descriptions[117] "HINT" - set descriptions[118] "Interact with hyprpaper if present" - set descriptions[119] "No Icon" - set descriptions[120] "Force reload the config" - set descriptions[122] "Print system info" - set descriptions[123] "Interact with a plugin" - set descriptions[125] "Get the active window name and its properties" - set descriptions[126] "Swap the active workspaces between two monitors" - set descriptions[127] "Print the current random splash" - set descriptions[129] "Lock the focused group" - set descriptions[132] "Lock the groups" - set descriptions[133] "Move the cursor to the corner of the active window" - set descriptions[136] "INFO" - set descriptions[137] "Resize a selected window" + set descriptions[36] "Disable output" + set descriptions[37] "Toggle the current window's floating state" + set descriptions[38] "Get the list of defined workspace rules" + set descriptions[39] "Move the focused window to a workspace" + set descriptions[41] "Temporarily enable or disable binds:ignore_group_lock" + set descriptions[42] "List all workspaces with their properties" + set descriptions[43] "Swap the active window with the next or previous in a group" + set descriptions[44] "Close a specified window" + set descriptions[45] "WARNING" + set descriptions[46] "Specify the Hyprland instance" + set descriptions[47] "List all registered binds" + set descriptions[48] "Move the active window in a direction or to a monitor" + set descriptions[49] "Change the split ratio" + set descriptions[51] "Prohibit the active window from becoming or being inserted into group" + set descriptions[52] "Change the workspace" + set descriptions[53] "List all current config parsing errors" + set descriptions[54] "Toggle the current active window into a group" + set descriptions[55] "Get the config option status (values)" + set descriptions[58] "Close the active window" + set descriptions[59] "Pass the key to a specified window" + set descriptions[60] "List all decorations and their info" + set descriptions[61] "List all connected keyboards and mice" + set descriptions[62] "Switch focus from current to previously focused window" + set descriptions[63] "Change the current mapping group" + set descriptions[64] "Execute a Global Shortcut using the GlobalShortcuts portal" + set descriptions[66] "Force the renderer to reload all resources and outputs" + set descriptions[67] "Move a selected window" + set descriptions[69] "Print the Hyprland version: flags, commit and branch of build" + set descriptions[70] "Set all monitors' DPMS status" + set descriptions[71] "Resize the active window" + set descriptions[72] "Move the active window into a group" + set descriptions[73] "OK" + set descriptions[75] "Set the current window's floating state to true" + set descriptions[76] "Print tail of the log" + set descriptions[79] "List all layouts available (including plugin ones)" + set descriptions[80] "Move a workspace to a monitor" + set descriptions[81] "Execute a shell command" + set descriptions[83] "Modify the window stack order of the active or specified window" + set descriptions[84] "Toggle the focused window's internal fullscreen state" + set descriptions[86] "Issue a keyword to call a config keyword dynamically" + set descriptions[89] "Disable output" + set descriptions[90] "Pin a window" + set descriptions[91] "Allows adding/removing fake outputs to a specific backend" + set descriptions[93] "Toggle a special workspace on/off" + set descriptions[94] "Toggle the focused window's fullscreen state" + set descriptions[95] "Toggle the current window to always be opaque" + set descriptions[96] "Focus the requested workspace" + set descriptions[98] "Switch to the next window in a group" + set descriptions[99] "Output in JSON format" + set descriptions[100] "List all running Hyprland instances and their info" + set descriptions[101] "Execute a raw shell command" + set descriptions[102] "Exit the compositor with no questions asked" + set descriptions[103] "List all windows with their properties" + set descriptions[105] "Execute a batch of commands separated by ;" + set descriptions[106] "Dismiss all or up to amount of notifications" + set descriptions[108] "Set the xkb layout index for a keyboard" + set descriptions[109] "Move window doesnt switch to the workspace" + set descriptions[110] "Apply a tag to the window" + set descriptions[111] "Behave as moveintogroup" + set descriptions[112] "Refresh state after issuing the command" + set descriptions[113] "Move the focus in a direction" + set descriptions[114] "Focus the urgent window or the last window" + set descriptions[116] "Get the active workspace name and its properties" + set descriptions[117] "Issue a dispatch to call a keybind dispatcher with an arg" + set descriptions[119] "Center the active window" + set descriptions[120] "HINT" + set descriptions[121] "Interact with hyprpaper if present" + set descriptions[122] "No Icon" + set descriptions[123] "Force reload the config" + set descriptions[125] "Print system info" + set descriptions[126] "Interact with a plugin" + set descriptions[128] "Get the active window name and its properties" + set descriptions[129] "Swap the active workspaces between two monitors" + set descriptions[130] "Print the current random splash" + set descriptions[131] "On shortcut X sends shortcut Y to a specified window" + set descriptions[133] "Lock the focused group" + set descriptions[136] "Lock the groups" + set descriptions[137] "Move the cursor to the corner of the active window" + set descriptions[140] "INFO" + set descriptions[141] "Resize a selected window" set --local literal_transitions - set literal_transitions[1] "set inputs 104 75 34 2 3 78 106 37 109 41 46 113 85 114 52 54 89 118 120 122 16 59 60 18 123 20 125 127 26 68 97 98 28 29 101 103; set tos 2 3 4 3 3 3 5 3 6 3 3 3 7 9 3 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 6 3 3 15 3 6" - set literal_transitions[4] "set inputs 73 14 33 55 56 90 105 121 77 17 124 4 6 64 128 130 81 131 84 32 49 13 86 11 87 138; set tos 19 3 19 19 19 19 3 3 2 3 19 2 3 19 3 19 19 19 19 19 3 3 19 19 19 19" - set literal_transitions[8] "set inputs 104 75 34 2 3 78 106 37 41 46 113 85 114 52 54 89 118 120 122 16 59 60 18 123 20 125 127 26 68 98 28 29 101; set tos 2 3 4 3 3 3 5 3 3 3 3 7 9 3 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 3 3 15 3" - set literal_transitions[9] "set inputs 129 132 1 74 36 107 38 108 5 79 40 80 111 7 42 43 82 83 47 48 10 110 51 53 12 116 88 50 57 91 58 92 93 61 62 126 94 63 21 96 23 24 65 66 25 133 27 69 99 70 30 137 71 100; set tos 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3" - set literal_transitions[10] "set inputs 115 112; set tos 16 17" - set literal_transitions[12] "set inputs 102; set tos 3" - set literal_transitions[14] "set inputs 22 117 31 136 119 44 72; set tos 2 2 2 2 2 2 2" - set literal_transitions[15] "set inputs 39; set tos 3" - set literal_transitions[16] "set inputs 9 67 15 134; set tos 3 3 3 3" - set literal_transitions[18] "set inputs 76; set tos 20" - set literal_transitions[19] "set inputs 19 8; set tos 3 3" - set literal_transitions[20] "set inputs 35 45; set tos 6 6" - set literal_transitions[21] "set inputs 135 95; set tos 3 3" + set literal_transitions[1] "set inputs 106 76 34 36 2 3 79 108 38 112 42 47 116 86 117 53 89 55 91 121 123 125 16 60 61 18 126 20 128 130 26 69 99 100 28 29 103 105; set tos 2 3 4 5 3 3 3 6 3 5 3 3 3 7 9 3 5 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 5 3 3 15 3 5" + set literal_transitions[4] "set inputs 74 14 33 56 57 92 107 124 78 17 127 4 6 65 132 134 82 135 85 32 50 13 87 11 88 142; set tos 18 3 18 18 18 18 3 3 2 3 18 2 3 18 3 18 18 18 18 18 3 3 18 18 18 18" + set literal_transitions[8] "set inputs 106 76 34 2 3 79 108 38 42 47 116 86 117 53 55 91 121 123 125 16 60 61 18 126 20 128 130 26 69 100 28 29 103; set tos 2 3 4 3 3 3 6 3 3 3 3 7 9 3 3 10 3 3 3 3 11 3 12 13 3 3 3 14 3 3 3 15 3" + set literal_transitions[9] "set inputs 102 131 133 1 75 37 109 110 39 111 5 80 41 81 114 7 43 44 83 84 48 49 10 51 52 54 12 113 90 119 58 93 59 94 95 62 63 129 96 64 21 98 23 24 66 67 136 137 25 27 70 101 71 141 30 72; set tos 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3" + set literal_transitions[10] "set inputs 118 115; set tos 21 17" + set literal_transitions[12] "set inputs 104; set tos 3" + set literal_transitions[14] "set inputs 22 120 31 140 122 45 73; set tos 2 2 2 2 2 2 2" + set literal_transitions[15] "set inputs 40; set tos 3" + set literal_transitions[16] "set inputs 139 97; set tos 3 3" + set literal_transitions[18] "set inputs 19 8; set tos 3 3" + set literal_transitions[19] "set inputs 77; set tos 20" + set literal_transitions[20] "set inputs 35 46; set tos 5 5" + set literal_transitions[21] "set inputs 9 68 15 138; set tos 3 3 3 3" - set --local match_anything_transitions_from 2 1 7 21 11 3 8 13 15 17 5 12 - set --local match_anything_transitions_to 3 8 3 3 3 18 8 3 18 3 21 18 + set --local match_anything_transitions_from 2 1 7 16 11 6 15 8 3 17 13 12 + set --local match_anything_transitions_to 3 8 3 3 3 16 19 8 19 3 3 19 set --local state 1 set --local word_index 2 @@ -199,8 +203,8 @@ function _hyprctl end end - set command_states 17 5 13 11 - set command_ids 3 4 2 1 + set command_states 6 17 13 11 + set command_ids 2 3 4 1 if contains $state $command_states set --local index (contains --index $state $command_states) set --local function_id $command_ids[$index] diff --git a/hyprctl/hyprctl.usage b/hyprctl/hyprctl.usage index a98541dd..64cbbb80 100644 --- a/hyprctl/hyprctl.usage +++ b/hyprctl/hyprctl.usage @@ -8,6 +8,7 @@ hyprctl []... | (-j) "Output in JSON format" | (-r) "Refresh state after issuing the command" | (--batch) "Execute a batch of commands separated by ;" + | (-q | --quiet) "Disable output" ; ::= {{{ hyprctl clients | grep class | awk '{print $2}' }}}; @@ -94,6 +95,7 @@ hyprctl []... ::= (exec) "Execute a shell command" | (execr) "Execute a raw shell command" | (pass) "Pass the key to a specified window" + | (sendshortcut) "On shortcut X sends shortcut Y to a specified window" | (killactive) "Close the active window" | (closewindow) "Close a specified window" | (workspace) "Change the workspace" @@ -116,6 +118,7 @@ hyprctl []... | (movewindowpixel) "Move a selected window" | (cyclenext) "Focus the next window on a workspace" | (swapnext) "Swap the focused window with the next window" + | (tagwindow) "Apply a tag to the window" | (focuswindow) "Focus the first window matching" | (focusmonitor) "Focus a monitor" | (splitratio) "Change the split ratio" diff --git a/hyprctl/hyprctl.zsh b/hyprctl/hyprctl.zsh index 5e2a475b..6babd835 100644 --- a/hyprctl/hyprctl.zsh +++ b/hyprctl/hyprctl.zsh @@ -1,10 +1,8 @@ -#compdef hyprctl - _hyprctl_cmd_2 () { hyprctl monitors | grep Monitor | awk '{ print $2 }' } -_hyprctl_cmd_1 () { +_hyprctl_cmd_3 () { hyprpm list | grep "Plugin" | awk '{print $4}' } @@ -12,12 +10,12 @@ _hyprctl_cmd_0 () { hyprctl clients | grep class | awk '{print $2}' } -_hyprctl_cmd_3 () { +_hyprctl_cmd_1 () { hyprctl devices | sed -n '/Keyboard at/{n; s/^\s\+//; p}' } _hyprctl () { - local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow") + local -a literals=("cyclenext" "globalshortcuts" "cursorpos" "bordersize" "renameworkspace" "animationstyle" "focuswindow" "0" "auto" "swapnext" "forceallowsinput" "moveactive" "activebordercolor" "alphafullscreen" "wayland" "layers" "minsize" "monitors" "1" "kill" "settiled" "3" "focusmonitor" "swapwindow" "moveoutofgroup" "notify" "movecursor" "setcursor" "seterror" "movecurrentworkspacetomonitor" "4" "nomaxsize" "forcenoanims" "setprop" "-i" "-q" "togglefloating" "workspacerules" "movetoworkspace" "disable" "setignoregrouplock" "workspaces" "movegroupwindow" "closewindow" "0" "--instance" "binds" "movewindow" "splitratio" "alpha" "denywindowfromgroup" "workspace" "configerrors" "togglegroup" "getoption" "forceopaque" "keepaspectratio" "killactive" "pass" "decorations" "devices" "focuscurrentorlast" "submap" "global" "alphafullscreenoverride" "forcerendererreload" "movewindowpixel" "headless" "version" "dpms" "resizeactive" "moveintogroup" "5" "alphaoverride" "setfloating" "rollinglog" "::=" "rounding" "layouts" "moveworkspacetomonitor" "exec" "alphainactiveoverride" "alterzorder" "fakefullscreen" "nofocus" "keyword" "forcenoborder" "forcenodim" "--quiet" "pin" "output" "forcenoblur" "togglespecialworkspace" "fullscreen" "toggleopaque" "focusworkspaceoncurrentmonitor" "next" "changegroupactive" "-j" "instances" "execr" "exit" "clients" "all" "--batch" "dismissnotify" "inactivebordercolor" "switchxkblayout" "movetoworkspacesilent" "tagwindow" "movewindoworgroup" "-r" "movefocus" "focusurgentorlast" "remove" "activeworkspace" "dispatch" "create" "centerwindow" "2" "hyprpaper" "-1" "reload" "alphainactive" "systeminfo" "plugin" "dimaround" "activewindow" "swapactiveworkspaces" "splash" "sendshortcut" "maxsize" "lockactivegroup" "windowdancecompat" "forceopaqueoverriden" "lockgroups" "movecursortocorner" "x11" "prev" "1" "resizewindowpixel" "forcenoshadow") local -A descriptions descriptions[1]="Focus the next window on a workspace" @@ -42,100 +40,104 @@ _hyprctl () { descriptions[31]="CONFUSED" descriptions[34]="Set a property of a window" descriptions[35]="Specify the Hyprland instance" - descriptions[36]="Toggle the current window's floating state" - descriptions[37]="Get the list of defined workspace rules" - descriptions[38]="Move the focused window to a workspace" - descriptions[40]="Temporarily enable or disable binds:ignore_group_lock" - descriptions[41]="List all workspaces with their properties" - descriptions[42]="Swap the active window with the next or previous in a group" - descriptions[43]="Close a specified window" - descriptions[44]="WARNING" - descriptions[45]="Specify the Hyprland instance" - descriptions[46]="List all registered binds" - descriptions[47]="Move the active window in a direction or to a monitor" - descriptions[48]="Change the split ratio" - descriptions[50]="Prohibit the active window from becoming or being inserted into group" - descriptions[51]="Change the workspace" - descriptions[52]="List all current config parsing errors" - descriptions[53]="Toggle the current active window into a group" - descriptions[54]="Get the config option status (values)" - descriptions[57]="Close the active window" - descriptions[58]="Pass the key to a specified window" - descriptions[59]="List all decorations and their info" - descriptions[60]="List all connected keyboards and mice" - descriptions[61]="Switch focus from current to previously focused window" - descriptions[62]="Change the current mapping group" - descriptions[63]="Execute a Global Shortcut using the GlobalShortcuts portal" - descriptions[65]="Force the renderer to reload all resources and outputs" - descriptions[66]="Move a selected window" - descriptions[68]="Print the Hyprland version: flags, commit and branch of build" - descriptions[69]="Set all monitors' DPMS status" - descriptions[70]="Resize the active window" - descriptions[71]="Move the active window into a group" - descriptions[72]="OK" - descriptions[74]="Set the current window's floating state to true" - descriptions[75]="Print tail of the log" - descriptions[78]="List all layouts available (including plugin ones)" - descriptions[79]="Move a workspace to a monitor" - descriptions[80]="Execute a shell command" - descriptions[82]="Modify the window stack order of the active or specified window" - descriptions[83]="Toggle the focused window's internal fullscreen state" - descriptions[85]="Issue a keyword to call a config keyword dynamically" - descriptions[88]="Pin a window" - descriptions[89]="Allows adding/removing fake outputs to a specific backend" - descriptions[91]="Toggle a special workspace on/off" - descriptions[92]="Toggle the focused window's fullscreen state" - descriptions[93]="Toggle the current window to always be opaque" - descriptions[94]="Focus the requested workspace" - descriptions[96]="Switch to the next window in a group" - descriptions[97]="Output in JSON format" - descriptions[98]="List all running Hyprland instances and their info" - descriptions[99]="Execute a raw shell command" - descriptions[100]="Exit the compositor with no questions asked" - descriptions[101]="List all windows with their properties" - descriptions[103]="Execute a batch of commands separated by ;" - descriptions[104]="Dismiss all or up to amount of notifications" - descriptions[106]="Set the xkb layout index for a keyboard" - descriptions[107]="Move window doesnt switch to the workspace" - descriptions[108]="Behave as moveintogroup" - descriptions[109]="Refresh state after issuing the command" - descriptions[110]="Move the focus in a direction" - descriptions[111]="Focus the urgent window or the last window" - descriptions[113]="Get the active workspace name and its properties" - descriptions[114]="Issue a dispatch to call a keybind dispatcher with an arg" - descriptions[116]="Center the active window" - descriptions[117]="HINT" - descriptions[118]="Interact with hyprpaper if present" - descriptions[119]="No Icon" - descriptions[120]="Force reload the config" - descriptions[122]="Print system info" - descriptions[123]="Interact with a plugin" - descriptions[125]="Get the active window name and its properties" - descriptions[126]="Swap the active workspaces between two monitors" - descriptions[127]="Print the current random splash" - descriptions[129]="Lock the focused group" - descriptions[132]="Lock the groups" - descriptions[133]="Move the cursor to the corner of the active window" - descriptions[136]="INFO" - descriptions[137]="Resize a selected window" + descriptions[36]="Disable output" + descriptions[37]="Toggle the current window's floating state" + descriptions[38]="Get the list of defined workspace rules" + descriptions[39]="Move the focused window to a workspace" + descriptions[41]="Temporarily enable or disable binds:ignore_group_lock" + descriptions[42]="List all workspaces with their properties" + descriptions[43]="Swap the active window with the next or previous in a group" + descriptions[44]="Close a specified window" + descriptions[45]="WARNING" + descriptions[46]="Specify the Hyprland instance" + descriptions[47]="List all registered binds" + descriptions[48]="Move the active window in a direction or to a monitor" + descriptions[49]="Change the split ratio" + descriptions[51]="Prohibit the active window from becoming or being inserted into group" + descriptions[52]="Change the workspace" + descriptions[53]="List all current config parsing errors" + descriptions[54]="Toggle the current active window into a group" + descriptions[55]="Get the config option status (values)" + descriptions[58]="Close the active window" + descriptions[59]="Pass the key to a specified window" + descriptions[60]="List all decorations and their info" + descriptions[61]="List all connected keyboards and mice" + descriptions[62]="Switch focus from current to previously focused window" + descriptions[63]="Change the current mapping group" + descriptions[64]="Execute a Global Shortcut using the GlobalShortcuts portal" + descriptions[66]="Force the renderer to reload all resources and outputs" + descriptions[67]="Move a selected window" + descriptions[69]="Print the Hyprland version: flags, commit and branch of build" + descriptions[70]="Set all monitors' DPMS status" + descriptions[71]="Resize the active window" + descriptions[72]="Move the active window into a group" + descriptions[73]="OK" + descriptions[75]="Set the current window's floating state to true" + descriptions[76]="Print tail of the log" + descriptions[79]="List all layouts available (including plugin ones)" + descriptions[80]="Move a workspace to a monitor" + descriptions[81]="Execute a shell command" + descriptions[83]="Modify the window stack order of the active or specified window" + descriptions[84]="Toggle the focused window's internal fullscreen state" + descriptions[86]="Issue a keyword to call a config keyword dynamically" + descriptions[89]="Disable output" + descriptions[90]="Pin a window" + descriptions[91]="Allows adding/removing fake outputs to a specific backend" + descriptions[93]="Toggle a special workspace on/off" + descriptions[94]="Toggle the focused window's fullscreen state" + descriptions[95]="Toggle the current window to always be opaque" + descriptions[96]="Focus the requested workspace" + descriptions[98]="Switch to the next window in a group" + descriptions[99]="Output in JSON format" + descriptions[100]="List all running Hyprland instances and their info" + descriptions[101]="Execute a raw shell command" + descriptions[102]="Exit the compositor with no questions asked" + descriptions[103]="List all windows with their properties" + descriptions[105]="Execute a batch of commands separated by ;" + descriptions[106]="Dismiss all or up to amount of notifications" + descriptions[108]="Set the xkb layout index for a keyboard" + descriptions[109]="Move window doesnt switch to the workspace" + descriptions[110]="Apply a tag to the window" + descriptions[111]="Behave as moveintogroup" + descriptions[112]="Refresh state after issuing the command" + descriptions[113]="Move the focus in a direction" + descriptions[114]="Focus the urgent window or the last window" + descriptions[116]="Get the active workspace name and its properties" + descriptions[117]="Issue a dispatch to call a keybind dispatcher with an arg" + descriptions[119]="Center the active window" + descriptions[120]="HINT" + descriptions[121]="Interact with hyprpaper if present" + descriptions[122]="No Icon" + descriptions[123]="Force reload the config" + descriptions[125]="Print system info" + descriptions[126]="Interact with a plugin" + descriptions[128]="Get the active window name and its properties" + descriptions[129]="Swap the active workspaces between two monitors" + descriptions[130]="Print the current random splash" + descriptions[131]="On shortcut X sends shortcut Y to a specified window" + descriptions[133]="Lock the focused group" + descriptions[136]="Lock the groups" + descriptions[137]="Move the cursor to the corner of the active window" + descriptions[140]="INFO" + descriptions[141]="Resize a selected window" local -A literal_transitions - literal_transitions[1]="([104]=2 [75]=3 [34]=4 [2]=3 [3]=3 [78]=3 [106]=5 [37]=3 [109]=6 [41]=3 [46]=3 [113]=3 [85]=7 [114]=9 [52]=3 [54]=3 [89]=10 [118]=3 [120]=3 [122]=3 [16]=3 [59]=11 [60]=3 [18]=12 [123]=13 [20]=3 [125]=3 [127]=3 [26]=14 [68]=3 [97]=6 [98]=3 [28]=3 [29]=15 [101]=3 [103]=6)" - literal_transitions[4]="([73]=19 [14]=3 [33]=19 [55]=19 [56]=19 [90]=19 [105]=3 [121]=3 [77]=2 [17]=3 [124]=19 [4]=2 [6]=3 [64]=19 [128]=3 [130]=19 [81]=19 [131]=19 [84]=19 [32]=19 [49]=3 [13]=3 [86]=19 [11]=19 [87]=19 [138]=19)" - literal_transitions[8]="([104]=2 [75]=3 [34]=4 [2]=3 [3]=3 [78]=3 [106]=5 [37]=3 [41]=3 [46]=3 [113]=3 [85]=7 [114]=9 [52]=3 [54]=3 [89]=10 [118]=3 [120]=3 [122]=3 [16]=3 [59]=11 [60]=3 [18]=12 [123]=13 [20]=3 [125]=3 [127]=3 [26]=14 [68]=3 [98]=3 [28]=3 [29]=15 [101]=3)" - literal_transitions[9]="([129]=3 [132]=3 [1]=3 [74]=3 [36]=3 [107]=3 [38]=3 [108]=3 [5]=3 [79]=3 [40]=3 [80]=3 [111]=3 [7]=3 [42]=3 [43]=3 [82]=3 [83]=3 [47]=3 [48]=3 [10]=3 [110]=3 [51]=3 [53]=3 [12]=3 [116]=3 [88]=3 [50]=3 [57]=3 [91]=3 [58]=3 [92]=3 [93]=3 [61]=3 [62]=3 [126]=3 [94]=3 [63]=3 [21]=3 [96]=3 [23]=3 [24]=3 [65]=3 [66]=3 [25]=3 [133]=3 [27]=3 [69]=3 [99]=3 [70]=3 [30]=3 [137]=3 [71]=3 [100]=3)" - literal_transitions[10]="([115]=16 [112]=17)" - literal_transitions[12]="([102]=3)" - literal_transitions[14]="([22]=2 [117]=2 [31]=2 [136]=2 [119]=2 [44]=2 [72]=2)" - literal_transitions[15]="([39]=3)" - literal_transitions[16]="([9]=3 [67]=3 [15]=3 [134]=3)" - literal_transitions[18]="([76]=20)" - literal_transitions[19]="([19]=3 [8]=3)" - literal_transitions[20]="([35]=6 [45]=6)" - literal_transitions[21]="([135]=3 [95]=3)" + literal_transitions[1]="([106]=2 [76]=3 [34]=4 [36]=5 [2]=3 [3]=3 [79]=3 [108]=6 [38]=3 [112]=5 [42]=3 [47]=3 [116]=3 [86]=7 [117]=9 [53]=3 [89]=5 [55]=3 [91]=10 [121]=3 [123]=3 [125]=3 [16]=3 [60]=11 [61]=3 [18]=12 [126]=13 [20]=3 [128]=3 [130]=3 [26]=14 [69]=3 [99]=5 [100]=3 [28]=3 [29]=15 [103]=3 [105]=5)" + literal_transitions[4]="([74]=18 [14]=3 [33]=18 [56]=18 [57]=18 [92]=18 [107]=3 [124]=3 [78]=2 [17]=3 [127]=18 [4]=2 [6]=3 [65]=18 [132]=3 [134]=18 [82]=18 [135]=18 [85]=18 [32]=18 [50]=3 [13]=3 [87]=18 [11]=18 [88]=18 [142]=18)" + literal_transitions[8]="([106]=2 [76]=3 [34]=4 [2]=3 [3]=3 [79]=3 [108]=6 [38]=3 [42]=3 [47]=3 [116]=3 [86]=7 [117]=9 [53]=3 [55]=3 [91]=10 [121]=3 [123]=3 [125]=3 [16]=3 [60]=11 [61]=3 [18]=12 [126]=13 [20]=3 [128]=3 [130]=3 [26]=14 [69]=3 [100]=3 [28]=3 [29]=15 [103]=3)" + literal_transitions[9]="([102]=3 [131]=3 [133]=3 [1]=3 [75]=3 [37]=3 [109]=3 [110]=3 [39]=3 [111]=3 [5]=3 [80]=3 [41]=3 [81]=3 [114]=3 [7]=3 [43]=3 [44]=3 [83]=3 [84]=3 [48]=3 [49]=3 [10]=3 [51]=3 [52]=3 [54]=3 [12]=3 [113]=3 [90]=3 [119]=3 [58]=3 [93]=3 [59]=3 [94]=3 [95]=3 [62]=3 [63]=3 [129]=3 [96]=3 [64]=3 [21]=3 [98]=3 [23]=3 [24]=3 [66]=3 [67]=3 [136]=3 [137]=3 [25]=3 [27]=3 [70]=3 [101]=3 [71]=3 [141]=3 [30]=3 [72]=3)" + literal_transitions[10]="([118]=21 [115]=17)" + literal_transitions[12]="([104]=3)" + literal_transitions[14]="([22]=2 [120]=2 [31]=2 [140]=2 [122]=2 [45]=2 [73]=2)" + literal_transitions[15]="([40]=3)" + literal_transitions[16]="([139]=3 [97]=3)" + literal_transitions[18]="([19]=3 [8]=3)" + literal_transitions[19]="([77]=20)" + literal_transitions[20]="([35]=5 [46]=5)" + literal_transitions[21]="([9]=3 [68]=3 [15]=3 [138]=3)" local -A match_anything_transitions - match_anything_transitions=([2]=3 [1]=8 [7]=3 [21]=3 [11]=3 [3]=18 [8]=8 [13]=3 [15]=18 [17]=3 [5]=21 [12]=18) + match_anything_transitions=([2]=3 [1]=8 [7]=3 [16]=3 [11]=3 [6]=16 [15]=19 [8]=8 [3]=19 [17]=3 [13]=3 [12]=19) declare -A subword_transitions @@ -195,7 +197,7 @@ _hyprctl () { fi done fi - local -A commands=([17]=2 [5]=3 [13]=1 [11]=0) + local -A commands=([6]=1 [17]=2 [13]=3 [11]=0) if [[ -v "commands[$state]" ]]; then local command_id=${commands[$state]} diff --git a/hyprctl/main.cpp b/hyprctl/main.cpp index e9d7583c..34abe46f 100644 --- a/hyprctl/main.cpp +++ b/hyprctl/main.cpp @@ -30,6 +30,7 @@ #define PAD std::string instanceSignature; +bool quiet = false; struct SInstanceData { std::string id; @@ -39,6 +40,13 @@ struct SInstanceData { bool valid = true; }; +void log(std::string str) { + if (quiet) + return; + + std::cout << str; +} + std::string getRuntimeDir() { const auto XDG = getenv("XDG_RUNTIME_DIR"); @@ -96,17 +104,17 @@ int request(std::string arg, int minArgs = 0) { const auto ARGS = std::count(arg.begin(), arg.end(), ' '); if (ARGS < minArgs) { - std::cout << "Not enough arguments, expected at least " << minArgs; + log("Not enough arguments, expected at least " + minArgs); return -1; } if (SERVERSOCKET < 0) { - std::cout << "Couldn't open a socket (1)"; + log("Couldn't open a socket (1)"); return 1; } if (instanceSignature.empty()) { - std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"; + log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); return 2; } @@ -120,14 +128,14 @@ int request(std::string arg, int minArgs = 0) { strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1); if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { - std::cout << "Couldn't connect to " << socketPath << ". (3)"; + log("Couldn't connect to " + socketPath + ". (3)"); return 3; } auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length()); if (sizeWritten < 0) { - std::cout << "Couldn't write (4)"; + log("Couldn't write (4)"); return 4; } @@ -137,7 +145,7 @@ int request(std::string arg, int minArgs = 0) { sizeWritten = read(SERVERSOCKET, buffer, 8192); if (sizeWritten < 0) { - std::cout << "Couldn't read (5)"; + log("Couldn't read (5)"); return 5; } @@ -146,7 +154,7 @@ int request(std::string arg, int minArgs = 0) { while (sizeWritten == 8192) { sizeWritten = read(SERVERSOCKET, buffer, 8192); if (sizeWritten < 0) { - std::cout << "Couldn't read (5)"; + log("Couldn't read (5)"); return 5; } reply += std::string(buffer, sizeWritten); @@ -154,7 +162,7 @@ int request(std::string arg, int minArgs = 0) { close(SERVERSOCKET); - std::cout << reply; + log(reply); return 0; } @@ -163,12 +171,12 @@ int requestHyprpaper(std::string arg) { const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0); if (SERVERSOCKET < 0) { - std::cout << "Couldn't open a socket (1)"; + log("Couldn't open a socket (1)"); return 1; } if (instanceSignature.empty()) { - std::cout << "HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"; + log("HYPRLAND_INSTANCE_SIGNATURE was not set! (Is Hyprland running?)"); return 2; } @@ -182,7 +190,7 @@ int requestHyprpaper(std::string arg) { strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1); if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) { - std::cout << "Couldn't connect to " << socketPath << ". (3)"; + log("Couldn't connect to " + socketPath + ". (3)"); return 3; } @@ -192,7 +200,7 @@ int requestHyprpaper(std::string arg) { auto sizeWritten = write(SERVERSOCKET, arg.c_str(), arg.length()); if (sizeWritten < 0) { - std::cout << "Couldn't write (4)"; + log("Couldn't write (4)"); return 4; } @@ -201,13 +209,13 @@ int requestHyprpaper(std::string arg) { sizeWritten = read(SERVERSOCKET, buffer, 8192); if (sizeWritten < 0) { - std::cout << "Couldn't read (5)"; + log("Couldn't read (5)"); return 5; } close(SERVERSOCKET); - std::cout << std::string(buffer); + log(std::string(buffer)); return 0; } @@ -250,7 +258,7 @@ void instancesRequest(bool json) { result += "\n]"; } - std::cout << result << "\n"; + log(result + "\n"); } std::deque splitArgs(int argc, char** argv) { @@ -297,6 +305,8 @@ int main(int argc, char** argv) { fullArgs += "r"; } else if (ARGS[i] == "-a" && !fullArgs.contains("a")) { fullArgs += "a"; + } else if ((ARGS[i] == "-c" || ARGS[i] == "--config") && !fullArgs.contains("c")) { + fullArgs += "c"; } else if (ARGS[i] == "--batch") { fullRequest = "--batch "; } else if (ARGS[i] == "--instance" || ARGS[i] == "-i") { @@ -308,6 +318,8 @@ int main(int argc, char** argv) { } overrideInstance = ARGS[i]; + } else if (ARGS[i] == "-q" || ARGS[i] == "--quiet") { + quiet = true; } else if (ARGS[i] == "--help") { const std::string& cmd = ARGS[0]; @@ -358,7 +370,7 @@ int main(int argc, char** argv) { instanceSignature = overrideInstance; else if (!overrideInstance.empty()) { if (!isNumber(overrideInstance, false)) { - std::cout << "instance invalid\n"; + log("instance invalid\n"); return 1; } @@ -367,7 +379,7 @@ int main(int argc, char** argv) { const auto INSTANCES = instances(); if (INSTANCENO < 0 || static_cast(INSTANCENO) >= INSTANCES.size()) { - std::cout << "no such instance\n"; + log("no such instance\n"); return 1; } @@ -376,7 +388,7 @@ int main(int argc, char** argv) { const auto ISIG = getenv("HYPRLAND_INSTANCE_SIGNATURE"); if (!ISIG) { - std::cout << "HYPRLAND_INSTANCE_SIGNATURE not set! (is hyprland running?)\n"; + log("HYPRLAND_INSTANCE_SIGNATURE not set! (is hyprland running?)\n"); return 1; } @@ -417,6 +429,6 @@ int main(int argc, char** argv) { exitStatus = request(fullRequest); } - std::cout << std::endl; + std::cout << std::flush; return exitStatus; } diff --git a/hyprpm/src/core/PluginManager.cpp b/hyprpm/src/core/PluginManager.cpp index dc3bc548..fb338d25 100644 --- a/hyprpm/src/core/PluginManager.cpp +++ b/hyprpm/src/core/PluginManager.cpp @@ -495,6 +495,16 @@ bool CPluginManager::updateHeaders(bool force) { if (m_bVerbose) progress.printMessageAbove(std::string{Colors::BLUE} + "[v] " + Colors::RESET + "cmake returned: " + ret); + if (ret.contains("required packages were not found")) { + // missing deps, let the user know. + std::string missing = ret.substr(ret.find("The following required packages were not found:")); + missing = missing.substr(0, missing.find("Call Stack")); + missing = missing.substr(0, missing.find_last_of('\n')); + + std::cerr << "\n" << Colors::RED << "✖" << Colors::RESET << " Could not configure the hyprland source, cmake complained:\n" << missing << "\n"; + 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) diff --git a/meson.build b/meson.build index b7b23470..49c48c6c 100644 --- a/meson.build +++ b/meson.build @@ -27,6 +27,12 @@ endif wlroots = subproject('wlroots-hyprland', default_options: ['examples=false', 'renderers=gles2']) have_xwlr = wlroots.get_variable('features').get('xwayland') xcb_dep = dependency('xcb', required: get_option('xwayland')) +xcb_composite_dep = dependency('xcb-composite', required: get_option('xwayland')) +xcb_errors_dep = dependency('xcb-errors', required: get_option('xwayland')) +xcb_icccm_dep = dependency('xcb-icccm', required: get_option('xwayland')) +xcb_render_dep = dependency('xcb-render', required: get_option('xwayland')) +xcb_res_dep = dependency('xcb-res', required: get_option('xwayland')) +xcb_xfixes_dep = dependency('xcb-xfixes', required: get_option('xwayland')) cmake = import('cmake') udis = cmake.subproject('udis86') diff --git a/props.json b/props.json index 72a6e5b6..d9bd7cea 100644 --- a/props.json +++ b/props.json @@ -1,3 +1,3 @@ { - "version": "0.40.0" + "version": "0.41.0" } \ No newline at end of file diff --git a/protocols/meson.build b/protocols/meson.build index 2c331e4b..f4978c23 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -24,7 +24,6 @@ hyprwayland_scanner = find_program( ) protocols = [ - [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], [wl_protocol_dir, 'unstable/text-input/text-input-unstable-v1.xml'], ['wlr-screencopy-unstable-v1.xml'], [hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'], @@ -41,6 +40,7 @@ new_protocols = [ ['wlr-output-management-unstable-v1.xml'], ['kde-server-decoration.xml'], ['wlr-layer-shell-unstable-v1.xml'], + ['wayland-drm.xml'], ['wlr-data-control-unstable-v1.xml'], [hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'], [wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], @@ -63,6 +63,9 @@ new_protocols = [ [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'], [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], [wl_protocol_dir, 'unstable/primary-selection/primary-selection-unstable-v1.xml'], + [wl_protocol_dir, 'staging/xwayland-shell/xwayland-shell-v1.xml'], + [wl_protocol_dir, 'stable/viewporter/viewporter.xml'], + [wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'], ] wl_protos_src = [] diff --git a/protocols/wayland-drm.xml b/protocols/wayland-drm.xml new file mode 100644 index 00000000..eaf2654a --- /dev/null +++ b/protocols/wayland-drm.xml @@ -0,0 +1,189 @@ + + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2011 Intel Corporation + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that\n the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bitmask of capabilities. + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 5c9cfbad..5c0b8eaa 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -19,7 +19,10 @@ #include "protocols/PointerConstraints.hpp" #include "protocols/LayerShell.hpp" #include "protocols/XDGShell.hpp" +#include "protocols/core/Compositor.hpp" +#include "protocols/core/Subcompositor.hpp" #include "desktop/LayerSurface.hpp" +#include "xwayland/XWayland.hpp" #include #include @@ -146,7 +149,7 @@ void CCompositor::initServer() { m_sWLEventLoop = wl_display_get_event_loop(m_sWLDisplay); // register crit signal handler - wl_event_loop_add_signal(m_sWLEventLoop, SIGTERM, handleCritSignal, nullptr); + m_critSigSource = wl_event_loop_add_signal(m_sWLEventLoop, SIGTERM, handleCritSignal, nullptr); if (!envEnabled("HYPRLAND_NO_CRASHREPORTER")) { signal(SIGSEGV, handleUnrecoverableSignal); @@ -183,7 +186,7 @@ void CCompositor::initServer() { &isHeadlessOnly); if (isHeadlessOnly) { - m_sWLRRenderer = wlr_renderer_autocreate(m_sWLRBackend); + m_sWLRRenderer = wlr_renderer_autocreate(m_sWLRBackend); // TODO: remove this, it's barely needed now. } else { m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend); if (m_iDRMFD < 0) { @@ -199,15 +202,6 @@ void CCompositor::initServer() { throwError("wlr_gles2_renderer_create_with_drm_fd() failed!"); } - wlr_renderer_init_wl_shm(m_sWLRRenderer, m_sWLDisplay); - - if (wlr_renderer_get_dmabuf_texture_formats(m_sWLRRenderer)) { - if (wlr_renderer_get_drm_fd(m_sWLRRenderer) >= 0) - wlr_drm_create(m_sWLDisplay, m_sWLRRenderer); - - m_sWLRLinuxDMABuf = wlr_linux_dmabuf_v1_create_with_renderer(m_sWLDisplay, 4, m_sWLRRenderer); - } - m_sWLRAllocator = wlr_allocator_autocreate(m_sWLRBackend, m_sWLRRenderer); if (!m_sWLRAllocator) { @@ -222,13 +216,7 @@ void CCompositor::initServer() { throwError("wlr_gles2_renderer_get_egl() failed!"); } - m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, 6, m_sWLRRenderer); - m_sWLRSubCompositor = wlr_subcompositor_create(m_sWLDisplay); - // m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay); - - // wlr_data_control_manager_v1_create(m_sWLDisplay); - // wlr_primary_selection_v1_device_manager_create(m_sWLDisplay); - wlr_viewporter_create(m_sWLDisplay); + initManagers(STAGE_BASICINIT); m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend); if (!m_sWRLDRMLeaseMgr) { @@ -243,8 +231,6 @@ void CCompositor::initServer() { throwError("wlr_headless_backend_create() failed!"); } - wlr_single_pixel_buffer_manager_v1_create(m_sWLDisplay); - wlr_multi_backend_add(m_sWLRBackend, m_sWLRHeadlessBackend); initManagers(STAGE_LATE); @@ -319,7 +305,7 @@ void CCompositor::cleanup() { // still in a normal working state. g_pPluginSystem->unloadAllPlugins(); - m_pLastFocus = nullptr; + m_pLastFocus.reset(); m_pLastWindow.reset(); m_vWorkspaces.clear(); @@ -332,12 +318,9 @@ void CCompositor::cleanup() { m->state.commit(); } - m_vMonitors.clear(); + g_pXWayland.reset(); - if (g_pXWaylandManager->m_sWLRXWayland) { - wlr_xwayland_destroy(g_pXWaylandManager->m_sWLRXWayland); - g_pXWaylandManager->m_sWLRXWayland = nullptr; - } + m_vMonitors.clear(); wl_display_destroy_clients(g_pCompositor->m_sWLDisplay); removeAllSignals(); @@ -375,6 +358,9 @@ void CCompositor::cleanup() { if (m_sWLRBackend) wlr_backend_destroy(m_sWLRBackend); + if (m_critSigSource) + wl_event_source_remove(m_critSigSource); + wl_display_terminate(m_sWLDisplay); m_sWLDisplay = nullptr; @@ -392,12 +378,6 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the HookSystem!"); g_pHookSystem = std::make_unique(); - Debug::log(LOG, "Creating the ProtocolManager!"); - g_pProtocolManager = std::make_unique(); - - Debug::log(LOG, "Creating the SeatManager!"); - g_pSeatManager = std::make_unique(); - Debug::log(LOG, "Creating the KeybindManager!"); g_pKeybindManager = std::make_unique(); @@ -422,6 +402,16 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the PointerManager!"); g_pPointerManager = std::make_unique(); } break; + case STAGE_BASICINIT: { + Debug::log(LOG, "Creating the CHyprOpenGLImpl!"); + g_pHyprOpenGL = std::make_unique(); + + Debug::log(LOG, "Creating the ProtocolManager!"); + g_pProtocolManager = std::make_unique(); + + Debug::log(LOG, "Creating the SeatManager!"); + g_pSeatManager = std::make_unique(); + } break; case STAGE_LATE: { Debug::log(LOG, "Creating the ThreadManager!"); g_pThreadManager = std::make_unique(); @@ -432,9 +422,6 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the InputManager!"); g_pInputManager = std::make_unique(); - Debug::log(LOG, "Creating the CHyprOpenGLImpl!"); - g_pHyprOpenGL = std::make_unique(); - Debug::log(LOG, "Creating the HyprRenderer!"); g_pHyprRenderer = std::make_unique(); @@ -462,6 +449,9 @@ void CCompositor::initManagers(eManagersInitStage stage) { Debug::log(LOG, "Creating the CursorManager!"); g_pCursorManager = std::make_unique(); + + Debug::log(LOG, "Starting XWayland"); + g_pXWayland = std::make_unique(); } break; default: UNREACHABLE(); } @@ -570,6 +560,8 @@ void CCompositor::startCompositor() { createLockFile(); + EMIT_HOOK_EVENT("ready", nullptr); + // This blocks until we are done. Debug::log(LOG, "Hyprland is ready, running the event loop!"); g_pEventLoopManager->enterLoop(m_sWLDisplay, m_sWLEventLoop); @@ -669,7 +661,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper if (properties & ALLOW_FLOATING) { for (auto& w : m_vWindows | std::views::reverse) { const auto BB = w->getWindowBoxUnified(properties); - CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA}; + CBox box = BB.copy().expand(w->m_iX11Type == 2 ? BORDER_GRAB_AREA : 0); if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_sAdditionalConfigData.noFocus && w != pIgnoreWindow) { if (box.containsPoint(g_pPointerManager->position())) return w; @@ -698,7 +690,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper BB.x + BB.width <= PWINDOWMONITOR->vecPosition.x + PWINDOWMONITOR->vecSize.x && BB.y + BB.height <= PWINDOWMONITOR->vecPosition.y + PWINDOWMONITOR->vecSize.y) continue; - CBox box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA}; + CBox box = BB.copy().expand(w->m_iX11Type == 2 ? BORDER_GRAB_AREA : 0); if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_pWorkspace) && !w->isHidden() && !w->m_bPinned && !w->m_sAdditionalConfigData.noFocus && w != pIgnoreWindow && (!aboveFullscreen || w->m_bCreatedOverFullscreen)) { // OR windows should add focus to parent @@ -707,7 +699,7 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper if (box.containsPoint(g_pPointerManager->position())) { - if (w->m_bIsX11 && w->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(w->m_uSurface.xwayland)) { + if (w->m_bIsX11 && w->m_iX11Type == 2 && !w->m_pXWaylandSurface->wantsFocus()) { // Override Redirect return g_pCompositor->m_pLastWindow.lock(); // we kinda trick everything here. // TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases. @@ -785,47 +777,32 @@ PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t proper return windowForWorkspace(false); } -wlr_surface* CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW pWindow, Vector2D& sl) { +SP CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW pWindow, Vector2D& sl) { if (!validMapped(pWindow)) return nullptr; RASSERT(!pWindow->m_bIsX11, "Cannot call vectorWindowToSurface on an X11 window!"); - double subx, suby; - - CBox geom = pWindow->m_pXDGSurface->current.geometry; - // try popups first - const auto PPOPUP = pWindow->m_pPopupHead->at(pos); + const auto PPOPUP = pWindow->m_pPopupHead->at(pos); - wlr_surface* found = PPOPUP ? PPOPUP->m_sWLSurface.wlr() : nullptr; - - if (!PPOPUP) - found = wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), pos.x - pWindow->m_vRealPosition.value().x + geom.x, pos.y - pWindow->m_vRealPosition.value().y + geom.y, &subx, - &suby); - else { + if (PPOPUP) { const auto OFF = PPOPUP->coordsRelativeToParent(); - subx = pos.x - OFF.x + geom.x - pWindow->m_vRealPosition.goal().x; - suby = pos.y - OFF.y + geom.y - pWindow->m_vRealPosition.goal().y; + sl = pos - pWindow->m_vRealPosition.goal() - OFF; + return PPOPUP->m_pWLSurface->resource(); } - if (found) { - sl.x = subx; - sl.y = suby; - return found; + auto [surf, local] = pWindow->m_pWLSurface->resource()->at(pos - pWindow->m_vRealPosition.goal(), true); + if (surf) { + sl = local; + return surf; } - sl.x = pos.x - pWindow->m_vRealPosition.value().x; - sl.y = pos.y - pWindow->m_vRealPosition.value().y; - - sl.x += geom.x; - sl.y += geom.y; - - return pWindow->m_pWLSurface.wlr(); + return nullptr; } -Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindow, wlr_surface* pSurface) { +Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindow, SP pSurface) { if (!validMapped(pWindow)) return {}; @@ -836,25 +813,22 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo if (PPOPUP) return vec - PPOPUP->coordsGlobal(); - std::tuple iterData = {pSurface, -1337, -1337}; + std::tuple, Vector2D> iterData = {pSurface, {-1337, -1337}}; - wlr_surface_for_each_surface( - pWindow->m_pWLSurface.wlr(), - [](wlr_surface* surf, int x, int y, void* data) { - const auto PDATA = (std::tuple*)data; - if (surf == std::get<0>(*PDATA)) { - std::get<1>(*PDATA) = x; - std::get<2>(*PDATA) = y; - } + pWindow->m_pWLSurface->resource()->breadthfirst( + [](SP surf, const Vector2D& offset, void* data) { + const auto PDATA = (std::tuple, Vector2D>*)data; + if (surf == std::get<0>(*PDATA)) + std::get<1>(*PDATA) = offset; }, &iterData); CBox geom = pWindow->m_pXDGSurface->current.geometry; - if (std::get<1>(iterData) == -1337 && std::get<2>(iterData) == -1337) + if (std::get<1>(iterData) == Vector2D{-1337, -1337}) return vec - pWindow->m_vRealPosition.goal(); - return vec - pWindow->m_vRealPosition.goal() - Vector2D{std::get<1>(iterData), std::get<2>(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) { @@ -877,7 +851,7 @@ CMonitor* CCompositor::getRealMonitorFromOutput(wlr_output* out) { return nullptr; } -void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { +void CCompositor::focusWindow(PHLWINDOW pWindow, SP pSurface) { static auto PFOLLOWMOUSE = CConfigValue("input:follow_mouse"); static auto PSPECIALFALLTHROUGH = CConfigValue("input:special_fallthrough"); @@ -892,7 +866,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { return; } - if (pWindow && pWindow->m_bIsX11 && pWindow->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(pWindow->m_uSurface.xwayland)) + if (pWindow && pWindow->m_bIsX11 && pWindow->m_iX11Type == 2 && !pWindow->m_pXWaylandSurface->wantsFocus()) return; g_pLayoutManager->getCurrentLayout()->bringWindowToTop(pWindow); @@ -920,7 +894,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(nullptr); - m_pLastFocus = nullptr; + m_pLastFocus.reset(); g_pInputManager->recheckIdleInhibitorStatus(); return; @@ -972,7 +946,7 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { m_pLastWindow = PLASTWINDOW; - const auto PWINDOWSURFACE = pSurface ? pSurface : pWindow->m_pWLSurface.wlr(); + const auto PWINDOWSURFACE = pSurface ? pSurface : pWindow->m_pWLSurface->resource(); focusSurface(PWINDOWSURFACE, pWindow); @@ -1007,9 +981,9 @@ void CCompositor::focusWindow(PHLWINDOW pWindow, wlr_surface* pSurface) { g_pInputManager->sendMotionEventsToFocused(); } -void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { +void CCompositor::focusSurface(SP pSurface, PHLWINDOW pWindowOwner) { - if (g_pSeatManager->state.keyboardFocus == pSurface || (pWindowOwner && g_pSeatManager->state.keyboardFocus == pWindowOwner->m_pWLSurface.wlr())) + if (g_pSeatManager->state.keyboardFocus == pSurface || (pWindowOwner && g_pSeatManager->state.keyboardFocus == pWindowOwner->m_pWLSurface->resource())) return; // Don't focus when already focused on this. if (g_pSessionLockManager->isSessionLocked() && !g_pSessionLockManager->isSurfaceSessionLock(pSurface)) @@ -1020,18 +994,18 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { return; } - const auto PLASTSURF = m_pLastFocus; + const auto PLASTSURF = m_pLastFocus.lock(); // Unfocus last surface if should if (m_pLastFocus && !pWindowOwner) - g_pXWaylandManager->activateSurface(m_pLastFocus, false); + g_pXWaylandManager->activateSurface(m_pLastFocus.lock(), false); if (!pSurface) { g_pSeatManager->setKeyboardFocus(nullptr); g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","}); // unfocused g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ""}); - EMIT_HOOK_EVENT("keyboardFocus", (wlr_surface*)nullptr); - m_pLastFocus = nullptr; + EMIT_HOOK_EVENT("keyboardFocus", (SP)nullptr); + m_pLastFocus.reset(); return; } @@ -1048,8 +1022,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { EMIT_HOOK_EVENT("keyboardFocus", pSurface); - const auto SURF = CWLSurface::surfaceFromWlr(pSurface); - const auto OLDSURF = CWLSurface::surfaceFromWlr(PLASTSURF); + const auto SURF = CWLSurface::fromResource(pSurface); + const auto OLDSURF = CWLSurface::fromResource(PLASTSURF); if (OLDSURF && OLDSURF->constraint()) OLDSURF->constraint()->deactivate(); @@ -1058,7 +1032,7 @@ void CCompositor::focusSurface(wlr_surface* pSurface, PHLWINDOW pWindowOwner) { SURF->constraint()->activate(); } -wlr_surface* CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, CMonitor* monitor, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { +SP CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, CMonitor* monitor, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { for (auto& lsl : monitor->m_aLayerSurfaceLayers | std::views::reverse) { for (auto& ls : lsl | std::views::reverse) { if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->mapped) || ls->alpha.value() == 0.f) @@ -1069,7 +1043,7 @@ wlr_surface* CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, CMonito if (SURFACEAT) { *ppLayerSurfaceFound = ls.lock(); *sCoords = pos - SURFACEAT->coordsGlobal(); - return SURFACEAT->m_sWLSurface.wlr(); + return SURFACEAT->m_pWLSurface->resource(); } } } @@ -1077,31 +1051,34 @@ wlr_surface* CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, CMonito return nullptr; } -wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { +SP CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) { for (auto& ls : *layerSurfaces | std::views::reverse) { if (ls->fadingOut || !ls->layerSurface || (ls->layerSurface && !ls->layerSurface->surface->mapped) || ls->alpha.value() == 0.f) continue; - auto SURFACEAT = wlr_surface_surface_at(ls->layerSurface->surface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y); + auto [surf, local] = ls->layerSurface->surface->at(pos - ls->geometry.pos()); - if (SURFACEAT) { - if (!pixman_region32_not_empty(&SURFACEAT->input_region)) + if (surf) { + if (surf->current.input.empty()) continue; *ppLayerSurfaceFound = ls.lock(); - return SURFACEAT; + + *sCoords = local; + + return surf; } } return nullptr; } -PHLWINDOW CCompositor::getWindowFromSurface(wlr_surface* pSurface) { +PHLWINDOW CCompositor::getWindowFromSurface(SP pSurface) { for (auto& w : m_vWindows) { if (!w->m_bIsMapped || w->m_bFadingOut) continue; - if (w->m_pWLSurface.wlr() == pSurface) + if (w->m_pWLSurface->resource() == pSurface) return w; } @@ -1240,7 +1217,7 @@ bool CCompositor::isWindowActive(PHLWINDOW pWindow) { if (!pWindow->m_bIsMapped) return false; - const auto PSURFACE = pWindow->m_pWLSurface.wlr(); + const auto PSURFACE = pWindow->m_pWLSurface->resource(); return PSURFACE == m_pLastFocus || pWindow == m_pLastWindow.lock(); } @@ -1273,10 +1250,9 @@ void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { if (top) pWindow->m_bCreatedOverFullscreen = true; - if (!pWindow->m_bIsX11) { + if (!pWindow->m_bIsX11) moveToZ(pWindow, top); - return; - } else { + else { // move X11 window stack std::deque toMove; @@ -1288,7 +1264,7 @@ void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) { toMove.emplace_front(pw); for (auto& w : m_vWindows) { - if (w->m_bIsMapped && !w->isHidden() && w->m_bIsX11 && w->X11TransientFor() == pw) { + if (w->m_bIsMapped && !w->isHidden() && w->m_bIsX11 && w->X11TransientFor() == pw && w != pw && std::find(toMove.begin(), toMove.end(), w) == toMove.end()) { x11Stack(w, top, x11Stack); } } @@ -1643,11 +1619,6 @@ bool CCompositor::isPointOnReservedArea(const Vector2D& point, const CMonitor* p return !VECINRECT(point, XY1.x, XY1.y, XY2.x, XY2.y); } -void checkFocusSurfaceIter(wlr_surface* pSurface, int x, int y, void* data) { - auto pair = (std::pair*)data; - pair->second = pair->second || pSurface == pair->first; -} - CMonitor* CCompositor::getMonitorInDirection(const char& dir) { return this->getMonitorInDirection(m_pLastMonitor.get(), dir); } @@ -2226,7 +2197,7 @@ PHLWINDOW CCompositor::getX11Parent(PHLWINDOW pWindow) { if (!w->m_bIsX11) continue; - if (w->m_uSurface.xwayland == pWindow->m_uSurface.xwayland->parent) + if (w->m_pXWaylandSurface == pWindow->m_pXWaylandSurface->parent) return w; } @@ -2366,8 +2337,12 @@ void CCompositor::warpCursorTo(const Vector2D& pos, bool force) { static auto PNOWARPS = CConfigValue("cursor:no_warps"); - if (*PNOWARPS && !force) + if (*PNOWARPS && !force) { + const auto PMONITORNEW = getMonitorFromVector(pos); + if (PMONITORNEW != m_pLastMonitor.get()) + setActiveMonitor(PMONITORNEW); return; + } g_pPointerManager->warpTo(pos); @@ -2382,24 +2357,24 @@ void CCompositor::closeWindow(PHLWINDOW pWindow) { } } -PHLLS CCompositor::getLayerSurfaceFromSurface(wlr_surface* pSurface) { - std::pair result = {pSurface, false}; +PHLLS CCompositor::getLayerSurfaceFromSurface(SP pSurface) { + std::pair, bool> result = {pSurface, false}; for (auto& ls : m_vLayers) { if (ls->layerSurface && ls->layerSurface->surface == pSurface) return ls; - static auto iter = [](wlr_surface* surf, int x, int y, void* data) -> void { - if (surf == ((std::pair*)data)->first) { - *(bool*)data = true; - return; - } - }; - if (!ls->layerSurface || !ls->mapped) continue; - wlr_surface_for_each_surface(ls->layerSurface->surface, iter, &result); + ls->layerSurface->surface->breadthfirst( + [](SP surf, const Vector2D& offset, void* data) { + if (surf == ((std::pair, bool>*)data)->first) { + *(bool*)data = true; + return; + } + }, + &result); if (result.second) return ls; @@ -2666,6 +2641,7 @@ void CCompositor::arrangeMonitors() { maxXOffsetLeft = newPosition.x; break; case eAutoDirs::DIR_AUTO_RIGHT: + case eAutoDirs::DIR_AUTO_NONE: newPosition.x = maxXOffsetRight; maxXOffsetRight += m->vecSize.x; break; @@ -2730,13 +2706,13 @@ void CCompositor::leaveUnsafeState() { } } -void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scale) { +void CCompositor::setPreferredScaleForSurface(SP pSurface, double scale) { PROTO::fractional->sendScale(pSurface, scale); - wlr_surface_set_preferred_buffer_scale(pSurface, static_cast(std::ceil(scale))); + pSurface->sendPreferredScale(std::ceil(scale)); - const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface); + const auto PSURFACE = CWLSurface::fromResource(pSurface); if (!PSURFACE) { - Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface); + Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", (uintptr_t)pSurface); return; } @@ -2744,12 +2720,12 @@ void CCompositor::setPreferredScaleForSurface(wlr_surface* pSurface, double scal PSURFACE->m_iLastScale = static_cast(std::ceil(scale)); } -void CCompositor::setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform) { - wlr_surface_set_preferred_buffer_transform(pSurface, transform); +void CCompositor::setPreferredTransformForSurface(SP pSurface, wl_output_transform transform) { + pSurface->sendPreferredTransform(transform); - const auto PSURFACE = CWLSurface::surfaceFromWlr(pSurface); + const auto PSURFACE = CWLSurface::fromResource(pSurface); if (!PSURFACE) { - Debug::log(WARN, "Orphaned wlr_surface {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface); + Debug::log(WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", (uintptr_t)pSurface); return; } diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 574889bc..793899ee 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -29,8 +29,11 @@ #include "plugins/PluginSystem.hpp" #include "helpers/Watchdog.hpp" +class CWLSurfaceResource; + enum eManagersInitStage { STAGE_PRIORITY = 0, + STAGE_BASICINIT, STAGE_LATE }; @@ -79,7 +82,7 @@ class CCompositor { void createLockFile(); void removeLockFile(); - wlr_surface* m_pLastFocus = nullptr; + WP m_pLastFocus; PHLWINDOWREF m_pLastWindow; WP m_pLastMonitor; @@ -96,114 +99,97 @@ class CCompositor { // ------------------------------------------------- // - CMonitor* getMonitorFromID(const int&); - CMonitor* getMonitorFromName(const std::string&); - CMonitor* getMonitorFromDesc(const std::string&); - CMonitor* getMonitorFromCursor(); - CMonitor* getMonitorFromVector(const Vector2D&); - void removeWindowFromVectorSafe(PHLWINDOW); - void focusWindow(PHLWINDOW, wlr_surface* pSurface = nullptr); - void focusSurface(wlr_surface*, PHLWINDOW pWindowOwner = nullptr); - bool monitorExists(CMonitor*); - PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr); - wlr_surface* vectorToLayerSurface(const Vector2D&, std::vector*, Vector2D*, PHLLS*); - wlr_surface* vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*); - wlr_surface* vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl); - Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, wlr_surface*); - CMonitor* getMonitorFromOutput(wlr_output*); - CMonitor* getRealMonitorFromOutput(wlr_output*); - PHLWINDOW getWindowFromSurface(wlr_surface*); - PHLWINDOW getWindowFromHandle(uint32_t); - bool isWorkspaceVisible(PHLWORKSPACE); - PHLWORKSPACE getWorkspaceByID(const int&); - PHLWORKSPACE getWorkspaceByName(const std::string&); - PHLWORKSPACE getWorkspaceByString(const std::string&); - void sanityCheckWorkspaces(); - void updateWorkspaceWindowDecos(const int&); - void updateWorkspaceSpecialRenderData(const int&); - int getWindowsOnWorkspace(const int& id, std::optional onlyTiled = {}, std::optional onlyVisible = {}); - int getGroupsOnWorkspace(const int& id, std::optional onlyTiled = {}, std::optional onlyVisible = {}); - PHLWINDOW getUrgentWindow(); - bool hasUrgentWindowOnWorkspace(const int&); - PHLWINDOW getFirstWindowOnWorkspace(const int&); - PHLWINDOW getTopLeftWindowOnWorkspace(const int&); - PHLWINDOW getFullscreenWindowOnWorkspace(const int&); - bool isWindowActive(PHLWINDOW); - void changeWindowZOrder(PHLWINDOW, bool); - void cleanupFadingOut(const int& monid); - PHLWINDOW getWindowInDirection(PHLWINDOW, char); - PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional floating = {}); - PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional floating = {}); - int getNextAvailableNamedWorkspace(); - bool isPointOnAnyMonitor(const Vector2D&); - bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr); - CMonitor* getMonitorInDirection(const char&); - CMonitor* getMonitorInDirection(CMonitor*, const char&); - void updateAllWindowsAnimatedDecorationValues(); - void updateWorkspaceWindows(const int64_t& id); - void updateWindowAnimatedDecorationValues(PHLWINDOW); - int getNextAvailableMonitorID(std::string const& name); - void moveWorkspaceToMonitor(PHLWORKSPACE, CMonitor*, bool noWarpCursor = false); - void swapActiveWorkspaces(CMonitor*, CMonitor*); - CMonitor* getMonitorFromString(const std::string&); - bool workspaceIDOutOfBounds(const int64_t&); - void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID); - void updateFullscreenFadeOnWorkspace(PHLWORKSPACE); - PHLWINDOW getX11Parent(PHLWINDOW); - void scheduleFrameForMonitor(CMonitor*); - void addToFadingOutSafe(PHLLS); - void addToFadingOutSafe(PHLWINDOW); - PHLWINDOW getWindowByRegex(const std::string&); - void warpCursorTo(const Vector2D&, bool force = false); - PHLLS getLayerSurfaceFromSurface(wlr_surface*); - void closeWindow(PHLWINDOW); - Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); - void forceReportSizesToWindowsOnWorkspace(const int&); - PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = "", bool isEmtpy = true); // will be deleted next frame if left empty and unfocused! - void renameWorkspace(const int&, const std::string& name = ""); - void setActiveMonitor(CMonitor*); - bool isWorkspaceSpecial(const int&); - int getNewSpecialID(); - void performUserChecks(); - void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); - PHLWINDOW getForceFocus(); - void arrangeMonitors(); - void enterUnsafeState(); - void leaveUnsafeState(); - void setPreferredScaleForSurface(wlr_surface* pSurface, double scale); - void setPreferredTransformForSurface(wlr_surface* pSurface, wl_output_transform transform); - void updateSuspendedStates(); - PHLWINDOW windowForCPointer(CWindow*); + CMonitor* getMonitorFromID(const int&); + CMonitor* getMonitorFromName(const std::string&); + CMonitor* getMonitorFromDesc(const std::string&); + CMonitor* getMonitorFromCursor(); + CMonitor* getMonitorFromVector(const Vector2D&); + void removeWindowFromVectorSafe(PHLWINDOW); + void focusWindow(PHLWINDOW, SP pSurface = nullptr); + void focusSurface(SP, PHLWINDOW pWindowOwner = nullptr); + bool monitorExists(CMonitor*); + PHLWINDOW vectorToWindowUnified(const Vector2D&, uint8_t properties, PHLWINDOW pIgnoreWindow = nullptr); + SP vectorToLayerSurface(const Vector2D&, std::vector*, Vector2D*, PHLLS*); + SP vectorToLayerPopupSurface(const Vector2D&, CMonitor* monitor, Vector2D*, PHLLS*); + SP vectorWindowToSurface(const Vector2D&, PHLWINDOW, Vector2D& sl); + Vector2D vectorToSurfaceLocal(const Vector2D&, PHLWINDOW, SP); + CMonitor* getMonitorFromOutput(wlr_output*); + CMonitor* getRealMonitorFromOutput(wlr_output*); + PHLWINDOW getWindowFromSurface(SP); + PHLWINDOW getWindowFromHandle(uint32_t); + bool isWorkspaceVisible(PHLWORKSPACE); + PHLWORKSPACE getWorkspaceByID(const int&); + PHLWORKSPACE getWorkspaceByName(const std::string&); + PHLWORKSPACE getWorkspaceByString(const std::string&); + void sanityCheckWorkspaces(); + void updateWorkspaceWindowDecos(const int&); + void updateWorkspaceSpecialRenderData(const int&); + int getWindowsOnWorkspace(const int& id, std::optional onlyTiled = {}, std::optional onlyVisible = {}); + int getGroupsOnWorkspace(const int& id, std::optional onlyTiled = {}, std::optional onlyVisible = {}); + PHLWINDOW getUrgentWindow(); + bool hasUrgentWindowOnWorkspace(const int&); + PHLWINDOW getFirstWindowOnWorkspace(const int&); + PHLWINDOW getTopLeftWindowOnWorkspace(const int&); + PHLWINDOW getFullscreenWindowOnWorkspace(const int&); + bool isWindowActive(PHLWINDOW); + void changeWindowZOrder(PHLWINDOW, bool); + void cleanupFadingOut(const int& monid); + PHLWINDOW getWindowInDirection(PHLWINDOW, char); + PHLWINDOW getNextWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional floating = {}); + PHLWINDOW getPrevWindowOnWorkspace(PHLWINDOW, bool focusableOnly = false, std::optional floating = {}); + int getNextAvailableNamedWorkspace(); + bool isPointOnAnyMonitor(const Vector2D&); + bool isPointOnReservedArea(const Vector2D& point, const CMonitor* monitor = nullptr); + CMonitor* getMonitorInDirection(const char&); + CMonitor* getMonitorInDirection(CMonitor*, const char&); + void updateAllWindowsAnimatedDecorationValues(); + void updateWorkspaceWindows(const int64_t& id); + void updateWindowAnimatedDecorationValues(PHLWINDOW); + int getNextAvailableMonitorID(std::string const& name); + void moveWorkspaceToMonitor(PHLWORKSPACE, CMonitor*, bool noWarpCursor = false); + void swapActiveWorkspaces(CMonitor*, CMonitor*); + CMonitor* getMonitorFromString(const std::string&); + bool workspaceIDOutOfBounds(const int64_t&); + void setWindowFullscreen(PHLWINDOW, bool, eFullscreenMode mode = FULLSCREEN_INVALID); + void updateFullscreenFadeOnWorkspace(PHLWORKSPACE); + PHLWINDOW getX11Parent(PHLWINDOW); + void scheduleFrameForMonitor(CMonitor*); + void addToFadingOutSafe(PHLLS); + void addToFadingOutSafe(PHLWINDOW); + PHLWINDOW getWindowByRegex(const std::string&); + void warpCursorTo(const Vector2D&, bool force = false); + PHLLS getLayerSurfaceFromSurface(SP); + void closeWindow(PHLWINDOW); + Vector2D parseWindowVectorArgsRelative(const std::string&, const Vector2D&); + void forceReportSizesToWindowsOnWorkspace(const int&); + PHLWORKSPACE createNewWorkspace(const int&, const int&, const std::string& name = "", bool isEmtpy = true); // will be deleted next frame if left empty and unfocused! + void renameWorkspace(const int&, const std::string& name = ""); + void setActiveMonitor(CMonitor*); + bool isWorkspaceSpecial(const int&); + int getNewSpecialID(); + void performUserChecks(); + void moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace); + PHLWINDOW getForceFocus(); + void arrangeMonitors(); + void enterUnsafeState(); + void leaveUnsafeState(); + void setPreferredScaleForSurface(SP pSurface, double scale); + void setPreferredTransformForSurface(SP pSurface, wl_output_transform transform); + void updateSuspendedStates(); + PHLWINDOW windowForCPointer(CWindow*); - std::string explicitConfigPath; + std::string explicitConfigPath; private: - void initAllSignals(); - void removeAllSignals(); - void cleanEnvironment(); - void setRandomSplash(); - void initManagers(eManagersInitStage stage); - void prepareFallbackOutput(); + void initAllSignals(); + void removeAllSignals(); + void cleanEnvironment(); + void setRandomSplash(); + void initManagers(eManagersInitStage stage); + void prepareFallbackOutput(); - uint64_t m_iHyprlandPID = 0; + uint64_t m_iHyprlandPID = 0; + wl_event_source* m_critSigSource = nullptr; }; inline std::unique_ptr g_pCompositor; - -// For XWayland -inline std::map HYPRATOMS = {HYPRATOM("_NET_WM_WINDOW_TYPE"), - HYPRATOM("_NET_WM_WINDOW_TYPE_NORMAL"), - HYPRATOM("_NET_WM_WINDOW_TYPE_DOCK"), - HYPRATOM("_NET_WM_WINDOW_TYPE_DIALOG"), - HYPRATOM("_NET_WM_WINDOW_TYPE_UTILITY"), - HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLBAR"), - HYPRATOM("_NET_WM_WINDOW_TYPE_SPLASH"), - HYPRATOM("_NET_WM_WINDOW_TYPE_MENU"), - HYPRATOM("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"), - HYPRATOM("_NET_WM_WINDOW_TYPE_POPUP_MENU"), - HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLTIP"), - HYPRATOM("_NET_WM_WINDOW_TYPE_NOTIFICATION"), - HYPRATOM("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"), - HYPRATOM("_NET_SUPPORTING_WM_CHECK"), - HYPRATOM("_NET_WM_NAME"), - HYPRATOM("UTF8_STRING")}; diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index cc6e2df2..12f18145 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include extern "C" char** environ; @@ -499,6 +500,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("gestures:workspace_swipe", Hyprlang::INT{0}); m_pConfig->addConfigValue("gestures:workspace_swipe_fingers", Hyprlang::INT{3}); + m_pConfig->addConfigValue("gestures:workspace_swipe_min_fingers", Hyprlang::INT{0}); m_pConfig->addConfigValue("gestures:workspace_swipe_distance", Hyprlang::INT{300}); m_pConfig->addConfigValue("gestures:workspace_swipe_invert", Hyprlang::INT{1}); m_pConfig->addConfigValue("gestures:workspace_swipe_min_speed_to_force", Hyprlang::INT{30}); @@ -520,6 +522,7 @@ CConfigManager::CConfigManager() { m_pConfig->addConfigValue("cursor:hotspot_padding", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:inactive_timeout", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:no_warps", Hyprlang::INT{0}); + m_pConfig->addConfigValue("cursor:persistent_warps", Hyprlang::INT{0}); m_pConfig->addConfigValue("cursor:default_monitor", {STRVAL_EMPTY}); m_pConfig->addConfigValue("cursor:zoom_factor", {1.f}); m_pConfig->addConfigValue("cursor:zoom_rigid", Hyprlang::INT{0}); @@ -633,6 +636,25 @@ std::string CConfigManager::getMainConfigPath() { return getConfigDir() + "/hypr/" + (ISDEBUG ? "hyprlandd.conf" : "hyprland.conf"); } +const std::string CConfigManager::getConfigString() { + std::string configString; + std::string currFileContent; + + for (auto path : configPaths) { + std::ifstream configFile(path); + configString += ("\n\nConfig File: " + path + ": "); + if (!configFile.is_open()) { + Debug::log(LOG, "Config file not readable/found!"); + configString += "Read Failed\n"; + continue; + } + configString += "Read Succeeded\n"; + currFileContent.assign(std::istreambuf_iterator(configFile), std::istreambuf_iterator()); + configString.append(currFileContent); + } + return configString; +} + std::string CConfigManager::getErrors() { return m_szConfigErrors; } @@ -975,7 +997,7 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const CMonitor& PMONITOR) { Debug::log(WARN, "No rule found for {}, trying to use the first.", PMONITOR.szName); for (auto& r : m_dMonitorRules) { - if (r.name == "") { + if (r.name.empty()) { return r; } } @@ -1061,12 +1083,17 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo bool hasFloating = pWindow->m_bIsFloating; bool hasFullscreen = pWindow->m_bIsFullscreen; + // local tags for dynamic tag rule match + auto tags = pWindow->m_tags; + for (auto& rule : m_dWindowRules) { // check if we have a matching rule if (!rule.v2) { try { + if (rule.szValue.starts_with("tag:") && !tags.isTagged(rule.szValue.substr(4))) + continue; + if (rule.szValue.starts_with("title:")) { - // we have a title rule. std::regex RULECHECK(rule.szValue.substr(6)); if (!std::regex_search(title, RULECHECK)) @@ -1083,28 +1110,31 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo } } else { try { - if (rule.szClass != "") { + if (!rule.szTag.empty() && !tags.isTagged(rule.szTag)) + continue; + + if (!rule.szClass.empty()) { std::regex RULECHECK(rule.szClass); if (!std::regex_search(appidclass, RULECHECK)) continue; } - if (rule.szTitle != "") { + if (!rule.szTitle.empty()) { std::regex RULECHECK(rule.szTitle); if (!std::regex_search(title, RULECHECK)) continue; } - if (rule.szInitialTitle != "") { + if (!rule.szInitialTitle.empty()) { std::regex RULECHECK(rule.szInitialTitle); if (!std::regex_search(pWindow->m_szInitialTitle, RULECHECK)) continue; } - if (rule.szInitialClass != "") { + if (!rule.szInitialClass.empty()) { std::regex RULECHECK(rule.szInitialClass); if (!std::regex_search(pWindow->m_szInitialClass, RULECHECK)) @@ -1173,6 +1203,13 @@ std::vector CConfigManager::getMatchingRules(PHLWINDOW pWindow, boo returns.push_back(rule); + // apply tag with local tags + if (rule.szRule.starts_with("tag")) { + CVarList vars{rule.szRule, 0, 's', true}; + if (vars.size() == 2 && vars[0] == "tag") + tags.applyTag(vars[1], true); + } + if (dynamic) continue; @@ -1264,6 +1301,18 @@ void CConfigManager::appendMonitorRule(const SMonitorRule& r) { m_dMonitorRules.emplace_back(r); } +bool CConfigManager::replaceMonitorRule(const SMonitorRule& newrule) { + // Looks for an existing monitor rule (compared by name). + // If the rule exists, it is replaced with the input rule. + for (auto& r : m_dMonitorRules) { + if (r.name == newrule.name) { + r = newrule; + return true; + } + } + return false; +} + void CConfigManager::performMonitorReload() { bool overAgain = false; @@ -1665,7 +1714,7 @@ std::optional CConfigManager::handleMonitor(const std::string& comm newrule.resolution = Vector2D(-1, -2); } else if (parseModeLine(ARGS[1], newrule.drmMode)) { newrule.resolution = Vector2D(newrule.drmMode.hdisplay, newrule.drmMode.vdisplay); - newrule.refreshRate = newrule.drmMode.vrefresh / 1000; + newrule.refreshRate = float(newrule.drmMode.vrefresh) / 1000; } else { if (!ARGS[1].contains("x")) { @@ -1992,7 +2041,7 @@ std::optional CConfigManager::handleBind(const std::string& command if ((KEY != "") || multiKey) { SParsedKey parsedKey = parseKey(KEY); - if (parsedKey.catchAll && m_szCurrentSubmap == "") { + if (parsedKey.catchAll && m_szCurrentSubmap.empty()) { Debug::log(ERR, "Catchall not allowed outside of submap!"); return "Invalid catchall, catchall keybinds are only allowed in submaps."; } @@ -2017,18 +2066,24 @@ std::optional CConfigManager::handleUnbind(const std::string& comma } bool windowRuleValid(const std::string& RULE) { - return RULE == "float" || RULE == "tile" || RULE.starts_with("opacity") || RULE.starts_with("move") || RULE.starts_with("size") || RULE.starts_with("minsize") || - RULE.starts_with("maxsize") || RULE.starts_with("pseudo") || RULE.starts_with("monitor") || RULE.starts_with("idleinhibit") || RULE == "nofocus" || RULE == "noblur" || - RULE == "noshadow" || RULE == "nodim" || RULE == "noborder" || RULE == "opaque" || RULE == "forceinput" || RULE == "fullscreen" || RULE == "fakefullscreen" || - RULE == "nomaxsize" || RULE == "pin" || RULE == "noanim" || RULE == "dimaround" || RULE == "windowdance" || RULE == "maximize" || RULE == "keepaspectratio" || - RULE == "focusonactivate" || RULE.starts_with("animation") || RULE.starts_with("rounding") || RULE.starts_with("workspace") || RULE.starts_with("bordercolor") || - RULE == "forcergbx" || RULE == "noinitialfocus" || RULE == "stayfocused" || RULE.starts_with("bordersize") || RULE.starts_with("xray") || RULE.starts_with("center") || - RULE.starts_with("group") || RULE == "immediate" || RULE == "nearestneighbor" || RULE.starts_with("suppressevent") || RULE.starts_with("plugin:"); + static const auto rules = std::unordered_set{ + "dimaround", "fakefullscreen", "float", "focusonactivate", "forceinput", "forcergbx", "fullscreen", "immediate", + "keepaspectratio", "maximize", "nearestneighbor", "noanim", "noblur", "noborder", "nodim", "nofocus", + "noinitialfocus", "nomaxsize", "noshadow", "opaque", "pin", "stayfocused", "tile", "windowdance", + }; + static const auto rulesPrefix = std::vector{ + "animation", "bordercolor", "bordersize", "center", "group", "idleinhibit", "maxsize", "minsize", "monitor", "move", + "opacity", "plugin:", "pseudo", "rounding", "size", "suppressevent", "tag", "workspace", "xray", + }; + + return rules.contains(RULE) || std::any_of(rulesPrefix.begin(), rulesPrefix.end(), [&RULE](auto prefix) { return RULE.starts_with(prefix); }); } bool layerRuleValid(const std::string& RULE) { - return RULE == "noanim" || RULE == "blur" || RULE == "blurpopups" || RULE.starts_with("ignorealpha") || RULE.starts_with("ignorezero") || RULE == "dimaround" || - RULE.starts_with("xray") || RULE.starts_with("animation"); + static const auto rules = std::unordered_set{"noanim", "blur", "blurpopups", "dimaround"}; + static const auto rulesPrefix = std::vector{"ignorealpha", "ignorezero", "xray", "animation"}; + + return rules.contains(RULE) || std::any_of(rulesPrefix.begin(), rulesPrefix.end(), [&RULE](auto prefix) { return RULE.starts_with(prefix); }); } std::optional CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { @@ -2036,7 +2091,7 @@ std::optional CConfigManager::handleWindowRule(const std::string& c const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); // check rule and value - if (RULE == "" || VALUE == "") + if (RULE.empty() || VALUE.empty()) return "empty rule?"; if (RULE == "unset") { @@ -2063,7 +2118,7 @@ std::optional CConfigManager::handleLayerRule(const std::string& co const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1)); // check rule and value - if (RULE == "" || VALUE == "") + if (RULE.empty() || VALUE.empty()) return "empty rule?"; if (RULE == "unset") { @@ -2101,6 +2156,7 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& rule.szRule = RULE; rule.szValue = VALUE; + const auto TAGPOS = VALUE.find("tag:"); const auto TITLEPOS = VALUE.find("title:"); const auto CLASSPOS = VALUE.find("class:"); const auto INITIALTITLEPOS = VALUE.find("initialTitle:"); @@ -2123,9 +2179,10 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& currentPos = VALUE.find("workspace:", currentPos + 1); } - if (TITLEPOS == std::string::npos && CLASSPOS == std::string::npos && INITIALTITLEPOS == std::string::npos && INITIALCLASSPOS == std::string::npos && - X11POS == std::string::npos && FLOATPOS == std::string::npos && FULLSCREENPOS == std::string::npos && PINNEDPOS == std::string::npos && WORKSPACEPOS == std::string::npos && - FOCUSPOS == std::string::npos && ONWORKSPACEPOS == std::string::npos) { + const auto checkPos = std::unordered_set{ + TAGPOS, TITLEPOS, CLASSPOS, INITIALTITLEPOS, INITIALCLASSPOS, X11POS, FLOATPOS, FULLSCREENPOS, PINNEDPOS, WORKSPACEPOS, FOCUSPOS, ONWORKSPACEPOS, + }; + if (checkPos.size() == 1 && checkPos.contains(std::string::npos)) { Debug::log(ERR, "Invalid rulev2 syntax: {}", VALUE); return "Invalid rulev2 syntax: " + VALUE; } @@ -2135,6 +2192,8 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& result = VALUE.substr(pos); size_t min = 999999; + if (TAGPOS > pos && TAGPOS < min) + min = TAGPOS; if (TITLEPOS > pos && TITLEPOS < min) min = TITLEPOS; if (CLASSPOS > pos && CLASSPOS < min) @@ -2168,6 +2227,9 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& return result; }; + if (TAGPOS != std::string::npos) + rule.szTag = extract(TAGPOS + 4); + if (CLASSPOS != std::string::npos) rule.szClass = extract(CLASSPOS + 6); @@ -2206,6 +2268,9 @@ std::optional CConfigManager::handleWindowRuleV2(const std::string& if (!other.v2) { return other.szClass == rule.szClass && !rule.szClass.empty(); } else { + if (!rule.szTag.empty() && rule.szTag != other.szTag) + return false; + if (!rule.szClass.empty() && rule.szClass != other.szClass) return false; diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 7fc132c6..ca22904d 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -103,6 +103,7 @@ class CConfigManager { void onPluginLoadUnload(const std::string& name, bool load); static std::string getConfigDir(); static std::string getMainConfigPath(); + const std::string getConfigString(); SMonitorRule getMonitorRuleFor(const CMonitor&); SWorkspaceRule getWorkspaceRuleFor(PHLWORKSPACE workspace); @@ -128,6 +129,7 @@ class CConfigManager { void performMonitorReload(); void appendMonitorRule(const SMonitorRule&); + bool replaceMonitorRule(const SMonitorRule&); bool m_bWantsMonitorReload = false; bool m_bForceReload = false; bool m_bNoMonitorReload = false; diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 51071533..bdb12e58 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "../config/ConfigDataValues.hpp" #include "../config/ConfigValue.hpp" @@ -23,6 +24,7 @@ #include "../devices/IKeyboard.hpp" #include "../devices/ITouch.hpp" #include "../devices/Tablet.hpp" +#include "config/ConfigManager.hpp" static void trimTrailingComma(std::string& str) { if (!str.empty() && str.back() == ',') @@ -130,10 +132,9 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) { continue; result += std::format( - "Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\tspecial " - "workspace: {} ({})\n\treserved: {} " - "{} {} {}\n\tscale: {:.2f}\n\ttransform: " - "{}\n\tfocused: {}\n\tdpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n", + "Monitor {} (ID {}):\n\t{}x{}@{:.5f} at {}x{}\n\tdescription: {}\n\tmake: {}\n\tmodel: {}\n\tserial: {}\n\tactive workspace: {} ({})\n\t" + "special workspace: {} ({})\n\treserved: {} {} {} {}\n\tscale: {:.2f}\n\ttransform: {}\n\tfocused: {}\n\t" + "dpmsStatus: {}\n\tvrr: {}\n\tactivelyTearing: {}\n\tdisabled: {}\n\tcurrentFormat: {}\n\tavailableModes: {}\n\n", m->szName, m->ID, (int)m->vecPixelSize.x, (int)m->vecPixelSize.y, m->refreshRate, (int)m->vecPosition.x, (int)m->vecPosition.y, m->szShortDescription, (m->output->make ? m->output->make : ""), (m->output->model ? m->output->model : ""), (m->output->serial ? m->output->serial : ""), m->activeWorkspaceID(), (!m->activeWorkspace ? "" : m->activeWorkspace->m_szName), m->activeSpecialWorkspaceID(), (m->activeSpecialWorkspace ? m->activeSpecialWorkspace->m_szName : ""), @@ -146,6 +147,16 @@ std::string monitorsRequest(eHyprCtlOutputFormat format, std::string request) { return result; } +static std::string getTagsData(PHLWINDOW w, eHyprCtlOutputFormat format) { + const auto tags = w->m_tags.getTags(); + + if (format == eHyprCtlOutputFormat::FORMAT_JSON) + return std::accumulate(tags.begin(), tags.end(), std::string(), + [](const std::string& a, const std::string& b) { return a.empty() ? std::format("\"{}\"", b) : std::format("{}, \"{}\"", a, b); }); + else + return std::accumulate(tags.begin(), tags.end(), std::string(), [](const std::string& a, const std::string& b) { return a.empty() ? b : a + ", " + b; }); +} + static std::string getGroupedData(PHLWINDOW w, eHyprCtlOutputFormat format) { const bool isJson = format == eHyprCtlOutputFormat::FORMAT_JSON; if (w->m_sGroupData.pNextWindow.expired()) @@ -204,6 +215,7 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { "fullscreenMode": {}, "fakeFullscreen": {}, "grouped": [{}], + "tags": [{}], "swallowing": "0x{:x}", "focusHistoryID": {} }},)#", @@ -213,18 +225,18 @@ static std::string getWindowData(PHLWINDOW w, eHyprCtlOutputFormat format) { escapeJSONStrings(w->m_szClass), escapeJSONStrings(w->m_szTitle), escapeJSONStrings(w->m_szInitialClass), escapeJSONStrings(w->m_szInitialTitle), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"), (w->m_bIsFullscreen ? (w->m_pWorkspace ? (int)w->m_pWorkspace->m_efFullscreenMode : 0) : 0), w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format), - (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); + getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } else { return std::format("Window {:x} -> {}:\n\tmapped: {}\n\thidden: {}\n\tat: {},{}\n\tsize: {},{}\n\tworkspace: {} ({})\n\tfloating: {}\n\tmonitor: {}\n\tclass: {}\n\ttitle: " "{}\n\tinitialClass: {}\n\tinitialTitle: {}\n\tpid: " "{}\n\txwayland: {}\n\tpinned: " - "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n", + "{}\n\tfullscreen: {}\n\tfullscreenmode: {}\n\tfakefullscreen: {}\n\tgrouped: {}\n\ttags: {}\n\tswallowing: {:x}\n\tfocusHistoryID: {}\n\n", (uintptr_t)w.get(), w->m_szTitle, (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goal().x, (int)w->m_vRealPosition.goal().y, (int)w->m_vRealSize.goal().x, (int)w->m_vRealSize.goal().y, w->m_pWorkspace ? w->workspaceID() : WORKSPACE_INVALID, (!w->m_pWorkspace ? "" : w->m_pWorkspace->m_szName), (int)w->m_bIsFloating, (int64_t)w->m_iMonitorID, w->m_szClass, w->m_szTitle, w->m_szInitialClass, w->m_szInitialTitle, w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen, (w->m_bIsFullscreen ? (w->m_pWorkspace ? w->m_pWorkspace->m_efFullscreenMode : 0) : 0), (int)w->m_bFakeFullscreenState, getGroupedData(w, format), - (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); + getTagsData(w, format), (uintptr_t)w->m_pSwallowed.lock().get(), getFocusHistoryID(w)); } } @@ -883,6 +895,8 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) #if defined(__DragonFly__) || defined(__FreeBSD__) const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga"); +#elif defined(__arm__) || defined(__aarch64__) + const std::string GPUINFO = execAndGet("cat /proc/device-tree/soc*/gpu*/compatible"); #else const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA"); #endif @@ -895,6 +909,12 @@ std::string systemInfoRequest(eHyprCtlOutputFormat format, std::string request) result += std::format(" {} by {} ver {}\n", pl->name, pl->author, pl->version); } + if (g_pHyprCtl->m_sCurrentRequestParams.sysInfoConfig) { + result += "\n======Config-Start======\n"; + result += g_pConfigManager->getConfigString(); + result += "\n======Config-End========\n"; + } + return result; } @@ -1036,13 +1056,15 @@ std::string dispatchBatch(eHyprCtlOutputFormat format, std::string request) { nextItem(); + const std::string DELIMITER = "\n\n\n"; + while (curitem != "" || request != "") { - reply += g_pHyprCtl->getReply(curitem); + reply += g_pHyprCtl->getReply(curitem) + DELIMITER; nextItem(); } - return reply; + return reply.substr(0, std::max(static_cast(reply.size() - DELIMITER.size()), 0)); } std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) { @@ -1063,7 +1085,8 @@ std::string dispatchSetCursor(eHyprCtlOutputFormat format, std::string request) if (size <= 0) return "size not positive"; - g_pCursorManager->changeTheme(theme, size); + if (!g_pCursorManager->changeTheme(theme, size)) + return "failed to set cursor"; return "ok"; } @@ -1351,73 +1374,64 @@ std::string decorationRequest(eHyprCtlOutputFormat format, std::string request) return result; } -void createOutputIter(wlr_backend* backend, void* data) { - const auto DATA = (std::pair*)data; +static bool addOutput(wlr_backend* backend, const std::string& type, const std::string& name) { + wlr_output* output = nullptr; - if (DATA->second) + 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(data); + + if (DATA->added) return; - if (DATA->first.empty() || DATA->first == "auto") { - if (wlr_backend_is_wl(backend)) { - wlr_wl_output_create(backend); - DATA->second = true; - } else if (wlr_backend_is_x11(backend)) { - wlr_x11_output_create(backend); - DATA->second = true; - } else if (wlr_backend_is_headless(backend)) { - wlr_headless_add_output(backend, 1920, 1080); - DATA->second = true; - } - } else { - if (wlr_backend_is_wl(backend) && DATA->first == "wayland") { - wlr_wl_output_create(backend); - DATA->second = true; - } else if (wlr_backend_is_x11(backend) && DATA->first == "x11") { - wlr_x11_output_create(backend); - DATA->second = true; - } else if (wlr_backend_is_headless(backend) && DATA->first == "headless") { - wlr_headless_add_output(backend, 1920, 1080); - DATA->second = true; - } - } + if (addOutput(backend, DATA->type, DATA->name)) + DATA->added = true; } std::string dispatchOutput(eHyprCtlOutputFormat format, std::string request) { - std::string curitem = ""; + CVarList vars(request, 0, ' '); - auto nextItem = [&]() { - auto idx = request.find_first_of(' '); + if (vars.size() < 2) + return "not enough args"; - if (idx != std::string::npos) { - curitem = request.substr(0, idx); - request = request.substr(idx + 1); - } else { - curitem = request; - request = ""; - } - - curitem = removeBeginEndSpacesTabs(curitem); - }; - - nextItem(); - nextItem(); - - const auto MODE = curitem; - - nextItem(); - - const auto NAME = curitem; + const auto MODE = vars[1]; if (MODE == "create" || MODE == "add") { - std::pair result = {NAME, false}; + if (g_pCompositor->getMonitorFromName(vars[3])) + return "A real monitor already uses that name."; + + outputData result{vars[2], vars[3], false}; wlr_multi_for_each_backend(g_pCompositor->m_sWLRBackend, createOutputIter, &result); - if (!result.second) + if (!result.added) return "no backend replied to the request"; } else if (MODE == "destroy" || MODE == "remove") { - const auto PMONITOR = g_pCompositor->getMonitorFromName(NAME); + const auto PMONITOR = g_pCompositor->getMonitorFromName(vars[2]); if (!PMONITOR) return "output not found"; @@ -1602,6 +1616,11 @@ CHyprCtl::CHyprCtl() { startHyprCtlSocket(); } +CHyprCtl::~CHyprCtl() { + if (m_eventSource) + wl_event_source_remove(m_eventSource); +} + SP CHyprCtl::registerCommand(SHyprCtlCommand cmd) { return m_vCommands.emplace_back(makeShared(cmd)); } @@ -1638,6 +1657,8 @@ std::string CHyprCtl::getReply(std::string request) { reloadAll = true; else if (c == 'a') m_sCurrentRequestParams.all = true; + else if (c == 'c') + m_sCurrentRequestParams.sysInfoConfig = true; } if (sepIndex < request.size()) @@ -1780,5 +1801,5 @@ void CHyprCtl::startHyprCtlSocket() { Debug::log(LOG, "Hypr socket started at {}", socketPath); - wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr); + m_eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, m_iSocketFD, WL_EVENT_READABLE, hyprCtlFDTick, nullptr); } diff --git a/src/debug/HyprCtl.hpp b/src/debug/HyprCtl.hpp index ebcb87cf..21a2c5f8 100644 --- a/src/debug/HyprCtl.hpp +++ b/src/debug/HyprCtl.hpp @@ -8,6 +8,7 @@ class CHyprCtl { public: CHyprCtl(); + ~CHyprCtl(); std::string makeDynamicCall(const std::string& input); SP registerCommand(SHyprCtlCommand cmd); @@ -17,13 +18,15 @@ class CHyprCtl { int m_iSocketFD = -1; struct { - bool all = false; + bool all = false; + bool sysInfoConfig = false; } m_sCurrentRequestParams; private: void startHyprCtlSocket(); std::vector> m_vCommands; + wl_event_source* m_eventSource = nullptr; }; -inline std::unique_ptr g_pHyprCtl; \ No newline at end of file +inline std::unique_ptr g_pHyprCtl; diff --git a/src/debug/HyprDebugOverlay.cpp b/src/debug/HyprDebugOverlay.cpp index 6d3ec907..889be8ea 100644 --- a/src/debug/HyprDebugOverlay.cpp +++ b/src/debug/HyprDebugOverlay.cpp @@ -3,6 +3,10 @@ #include "config/ConfigValue.hpp" #include "../Compositor.hpp" +CHyprDebugOverlay::CHyprDebugOverlay() { + m_pTexture = makeShared(); +} + void CHyprMonitorDebugOverlay::renderData(CMonitor* pMonitor, float µs) { m_dLastRenderTimes.push_back(µs / 1000.f); @@ -222,8 +226,8 @@ void CHyprDebugOverlay::draw() { // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(m_pCairoSurface); - m_tTexture.allocate(); - glBindTexture(GL_TEXTURE_2D, m_tTexture.m_iTexID); + m_pTexture->allocate(); + glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -235,5 +239,5 @@ void CHyprDebugOverlay::draw() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); CBox pMonBox = {0, 0, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y}; - g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f); + g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f); } diff --git a/src/debug/HyprDebugOverlay.hpp b/src/debug/HyprDebugOverlay.hpp index f3beab45..a6063ee9 100644 --- a/src/debug/HyprDebugOverlay.hpp +++ b/src/debug/HyprDebugOverlay.hpp @@ -31,6 +31,7 @@ class CHyprMonitorDebugOverlay { class CHyprDebugOverlay { public: + CHyprDebugOverlay(); void draw(); void renderData(CMonitor*, float µs); void renderDataNoOverlay(CMonitor*, float µs); @@ -42,7 +43,7 @@ class CHyprDebugOverlay { cairo_surface_t* m_pCairoSurface = nullptr; cairo_t* m_pCairo = nullptr; - CTexture m_tTexture; + SP m_pTexture; friend class CHyprMonitorDebugOverlay; friend class CHyprRenderer; diff --git a/src/debug/HyprNotificationOverlay.cpp b/src/debug/HyprNotificationOverlay.cpp index e1fc810b..aec3853e 100644 --- a/src/debug/HyprNotificationOverlay.cpp +++ b/src/debug/HyprNotificationOverlay.cpp @@ -23,6 +23,8 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() { g_pHyprRenderer->damageBox(&m_bLastDamage); }); + + m_pTexture = makeShared(); } CHyprNotificationOverlay::~CHyprNotificationOverlay() { @@ -227,8 +229,8 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) { // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(m_pCairoSurface); - m_tTexture.allocate(); - glBindTexture(GL_TEXTURE_2D, m_tTexture.m_iTexID); + m_pTexture->allocate(); + glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -240,7 +242,7 @@ void CHyprNotificationOverlay::draw(CMonitor* pMonitor) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, MONSIZE.x, MONSIZE.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); CBox pMonBox = {0, 0, MONSIZE.x, MONSIZE.y}; - g_pHyprOpenGL->renderTexture(m_tTexture, &pMonBox, 1.f); + g_pHyprOpenGL->renderTexture(m_pTexture, &pMonBox, 1.f); } bool CHyprNotificationOverlay::hasAny() { diff --git a/src/debug/HyprNotificationOverlay.hpp b/src/debug/HyprNotificationOverlay.hpp index 5c978089..352c44c9 100644 --- a/src/debug/HyprNotificationOverlay.hpp +++ b/src/debug/HyprNotificationOverlay.hpp @@ -58,7 +58,7 @@ class CHyprNotificationOverlay { CMonitor* m_pLastMonitor = nullptr; Vector2D m_vecLastSize = Vector2D(-1, -1); - CTexture m_tTexture; + SP m_pTexture; }; inline std::unique_ptr g_pHyprNotificationOverlay; diff --git a/src/debug/Log.hpp b/src/debug/Log.hpp index abf74065..e8cd80cf 100644 --- a/src/debug/Log.hpp +++ b/src/debug/Log.hpp @@ -49,11 +49,13 @@ namespace Debug { // print date and time to the ofs if (disableTime && !**disableTime) { #ifndef _LIBCPP_VERSION - logMsg += std::format("[{:%T}] ", std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}); + const auto zt = std::chrono::zoned_time{std::chrono::current_zone(), std::chrono::system_clock::now()}; + const auto hms = std::chrono::hh_mm_ss{zt.get_local_time() - std::chrono::floor(zt.get_local_time())}; #else - auto c = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}; - logMsg += std::format("{:%H}:{:%M}:{:%S}", c.hours(), c.minutes(), c.subseconds()); + // TODO: current clang 17 does not support `zoned_time`, remove this once clang 19 is ready + const auto hms = std::chrono::hh_mm_ss{std::chrono::system_clock::now() - std::chrono::floor(std::chrono::system_clock::now())}; #endif + logMsg += std::format("[{}] ", hms); } // no need for try {} catch {} because std::format_string ensures that vformat never throw std::format_error diff --git a/src/desktop/LayerSurface.cpp b/src/desktop/LayerSurface.cpp index 759eb09f..62cae8f6 100644 --- a/src/desktop/LayerSurface.cpp +++ b/src/desktop/LayerSurface.cpp @@ -2,6 +2,7 @@ #include "../Compositor.hpp" #include "../events/Events.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/core/Compositor.hpp" #include "../managers/SeatManager.hpp" PHLLS CLayerSurface::create(SP resource) { @@ -9,6 +10,8 @@ PHLLS CLayerSurface::create(SP resource) { CMonitor* pMonitor = resource->monitor.empty() ? g_pCompositor->getMonitorFromCursor() : g_pCompositor->getMonitorFromName(resource->monitor); + pLS->surface->assign(resource->surface.lock(), pLS); + if (!pMonitor) { Debug::log(ERR, "New LS has no monitor??"); return pLS; @@ -39,8 +42,6 @@ PHLLS CLayerSurface::create(SP resource) { pLS->alpha.setValueAndWarp(0.f); - pLS->surface.assign(resource->surface, pLS); - Debug::log(LOG, "LayerSurface {:x} (namespace {} layer {}) created on monitor {}", (uintptr_t)resource.get(), resource->layerNamespace, (int)pLS->layer, pMonitor->szName); return pLS; @@ -58,13 +59,16 @@ CLayerSurface::CLayerSurface(SP resource_) : layerSurface(r listeners.map = layerSurface->events.map.registerListener([this](std::any d) { onMap(); }); listeners.unmap = layerSurface->events.unmap.registerListener([this](std::any d) { onUnmap(); }); listeners.destroy = layerSurface->events.destroy.registerListener([this](std::any d) { onDestroy(); }); + + surface = CWLSurface::create(); } CLayerSurface::~CLayerSurface() { if (!g_pHyprOpenGL) return; - surface.unassign(); + if (surface) + surface->unassign(); g_pHyprRenderer->makeEGLCurrent(); std::erase_if(g_pHyprOpenGL->m_mLayerFramebuffers, [&](const auto& other) { return other.first.expired() || other.first.lock() == self.lock(); }); } @@ -105,7 +109,8 @@ void CLayerSurface::onDestroy() { readyToDelete = true; layerSurface.reset(); - surface.unassign(); + if (surface) + surface->unassign(); } void CLayerSurface::onMap() { @@ -126,7 +131,7 @@ void CLayerSurface::onMap() { g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID); - wlr_surface_send_enter(surface.wlr(), PMONITOR->output); + surface->resource()->enter(PMONITOR->self.lock()); if (layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) g_pInputManager->m_dExclusiveLSes.push_back(self); @@ -139,10 +144,10 @@ void CLayerSurface::onMap() { // TODO: use the new superb really very cool grab g_pSeatManager->setGrab(nullptr); g_pInputManager->releaseAllMouseButtons(); - g_pCompositor->focusSurface(surface.wlr()); + g_pCompositor->focusSurface(surface->resource()); const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y); - g_pSeatManager->setPointerFocus(surface.wlr(), LOCAL); + g_pSeatManager->setPointerFocus(surface->resource(), LOCAL); g_pInputManager->m_bEmptyFocusCursorSet = false; } @@ -160,8 +165,8 @@ void CLayerSurface::onMap() { g_pEventManager->postEvent(SHyprIPCEvent{"openlayer", szNamespace}); EMIT_HOOK_EVENT("openLayer", self.lock()); - g_pCompositor->setPreferredScaleForSurface(surface.wlr(), PMONITOR->scale); - g_pCompositor->setPreferredTransformForSurface(surface.wlr(), PMONITOR->transform); + g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform); } void CLayerSurface::onUnmap() { @@ -173,7 +178,7 @@ void CLayerSurface::onUnmap() { std::erase_if(g_pInputManager->m_dExclusiveLSes, [this](const auto& other) { return !other.lock() || other.lock() == self.lock(); }); if (!g_pInputManager->m_dExclusiveLSes.empty()) - g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->layerSurface->surface); + g_pCompositor->focusSurface(g_pInputManager->m_dExclusiveLSes[0]->surface->resource()); if (!g_pCompositor->getMonitorFromID(monitorID) || g_pCompositor->m_bUnsafeState) { Debug::log(WARN, "Layersurface unmapping on invalid monitor (removed?) ignoring."); @@ -197,9 +202,9 @@ void CLayerSurface::onUnmap() { const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID); - const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == layerSurface->surface; + const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == surface->resource(); - surface = nullptr; + surface.reset(); if (!PMONITOR) return; @@ -208,11 +213,11 @@ void CLayerSurface::onUnmap() { if (WASLASTFOCUS) { g_pInputManager->releaseAllMouseButtons(); - Vector2D surfaceCoords; - PHLLS pFoundLayerSurface; - wlr_surface* foundSurface = nullptr; + Vector2D surfaceCoords; + PHLLS pFoundLayerSurface; + SP foundSurface = nullptr; - g_pCompositor->m_pLastFocus = nullptr; + g_pCompositor->m_pLastFocus.reset(); // find LS-es to focus foundSurface = g_pCompositor->vectorToLayerSurface(g_pInputManager->getMouseCoordsInternal(), &PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], @@ -236,8 +241,8 @@ void CLayerSurface::onUnmap() { CBox geomFixed = {geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y, geometry.width, geometry.height}; g_pHyprRenderer->damageBox(&geomFixed); - geomFixed = {geometry.x + (int)PMONITOR->vecPosition.x, geometry.y + (int)PMONITOR->vecPosition.y, (int)layerSurface->surface->current.width, - (int)layerSurface->surface->current.height}; + geomFixed = {geometry.x + (int)PMONITOR->vecPosition.x, geometry.y + (int)PMONITOR->vecPosition.y, (int)layerSurface->surface->current.size.x, + (int)layerSurface->surface->current.size.y}; g_pHyprRenderer->damageBox(&geomFixed); g_pInputManager->sendMotionEventsToFocused(); @@ -284,12 +289,12 @@ void CLayerSurface::onCommit() { position = Vector2D(geometry.x, geometry.y); // update geom if it changed - if (layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layerSurface->surface->current.viewport.has_dst) { + if (layerSurface->surface->current.scale == 1 && PMONITOR->scale != 1.f && layerSurface->surface->current.viewport.hasDestination) { // fractional scaling. Dirty hack. - geometry = {geometry.x, geometry.y, (int)(layerSurface->surface->current.viewport.dst_width), (int)(layerSurface->surface->current.viewport.dst_height)}; + geometry = {geometry.pos(), layerSurface->surface->current.viewport.destination}; } else { // this is because some apps like e.g. rofi-lbonn can't fucking use the protocol correctly. - geometry = {geometry.x, geometry.y, (int)layerSurface->surface->current.width, (int)layerSurface->surface->current.height}; + geometry = {geometry.pos(), layerSurface->surface->current.size}; } } @@ -308,10 +313,10 @@ void CLayerSurface::onCommit() { if (layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) // don't focus if constrained && !keyboardExclusive && mapped) { - g_pCompositor->focusSurface(layerSurface->surface); + g_pCompositor->focusSurface(surface->resource()); const auto LOCAL = g_pInputManager->getMouseCoordsInternal() - Vector2D(geometry.x + PMONITOR->vecPosition.x, geometry.y + PMONITOR->vecPosition.y); - g_pSeatManager->setPointerFocus(layerSurface->surface, LOCAL); + g_pSeatManager->setPointerFocus(surface->resource(), LOCAL); g_pInputManager->m_bEmptyFocusCursorSet = false; } else if (!layerSurface->current.interactivity && (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) && keyboardExclusive) { g_pInputManager->refocus(); @@ -319,10 +324,10 @@ void CLayerSurface::onCommit() { keyboardExclusive = layerSurface->current.interactivity; - g_pHyprRenderer->damageSurface(layerSurface->surface, position.x, position.y); + g_pHyprRenderer->damageSurface(surface->resource(), position.x, position.y); - g_pCompositor->setPreferredScaleForSurface(layerSurface->surface, PMONITOR->scale); - g_pCompositor->setPreferredTransformForSurface(layerSurface->surface, PMONITOR->transform); + g_pCompositor->setPreferredScaleForSurface(surface->resource(), PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(surface->resource(), PMONITOR->transform); } void CLayerSurface::applyRules() { diff --git a/src/desktop/LayerSurface.hpp b/src/desktop/LayerSurface.hpp index d60a6dd0..9fa96d2d 100644 --- a/src/desktop/LayerSurface.hpp +++ b/src/desktop/LayerSurface.hpp @@ -36,7 +36,7 @@ class CLayerSurface { bool keyboardExclusive = false; - CWLSurface surface; + SP surface; bool mapped = false; uint32_t layer = 0; diff --git a/src/desktop/Popup.cpp b/src/desktop/Popup.cpp index 03acfff9..f0fd556c 100644 --- a/src/desktop/Popup.cpp +++ b/src/desktop/Popup.cpp @@ -3,6 +3,7 @@ #include "../Compositor.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" #include CPopup::CPopup(PHLWINDOW pOwner) : m_pWindowOwner(pOwner) { @@ -14,7 +15,8 @@ CPopup::CPopup(PHLLS pOwner) : m_pLayerOwner(pOwner) { } CPopup::CPopup(SP popup, CPopup* pOwner) : m_pParent(pOwner), m_pResource(popup) { - m_sWLSurface.assign(popup->surface->surface, this); + m_pWLSurface = CWLSurface::create(); + m_pWLSurface->assign(popup->surface->surface.lock(), this); m_pLayerOwner = pOwner->m_pLayerOwner; m_pWindowOwner = pOwner->m_pWindowOwner; @@ -26,7 +28,8 @@ CPopup::CPopup(SP popup, CPopup* pOwner) : m_pParent(pOwner), } CPopup::~CPopup() { - m_sWLSurface.unassign(); + if (m_pWLSurface) + m_pWLSurface->unassign(); } void CPopup::initAllSignals() { @@ -45,7 +48,7 @@ void CPopup::initAllSignals() { listeners.reposition = m_pResource->events.reposition.registerListener([this](std::any d) { this->onReposition(); }); listeners.map = m_pResource->surface->events.map.registerListener([this](std::any d) { this->onMap(); }); listeners.unmap = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); - listeners.dismissed = m_pResource->surface->events.unmap.registerListener([this](std::any d) { this->onUnmap(); }); + listeners.dismissed = m_pResource->events.dismissed.registerListener([this](std::any d) { this->onUnmap(); }); listeners.destroy = m_pResource->surface->events.destroy.registerListener([this](std::any d) { this->onDestroy(); }); listeners.commit = m_pResource->surface->events.commit.registerListener([this](std::any d) { this->onCommit(); }); listeners.newPopup = m_pResource->surface->events.newPopup.registerListener([this](std::any d) { this->onNewPopup(std::any_cast>(d)); }); @@ -66,13 +69,17 @@ void CPopup::onDestroy() { } void CPopup::onMap() { - m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; + if (m_bMapped) + return; + + m_bMapped = true; + m_vLastSize = m_pResource->surface->surface->current.size; + const auto COORDS = coordsGlobal(); const auto PMONITOR = g_pCompositor->getMonitorFromVector(COORDS); - CBox box; - wlr_surface_get_extends(m_sWLSurface.wlr(), box.pWlr()); - box.applyFromWlr().translate(COORDS).expand(4); + CBox box = m_pWLSurface->resource()->extends(); + box.translate(COORDS).expand(4); g_pHyprRenderer->damageBox(&box); m_vLastPos = coordsRelativeToParent(); @@ -83,21 +90,28 @@ void CPopup::onMap() { //unconstrain(); sendScale(); - wlr_surface_send_enter(m_pResource->surface->surface, PMONITOR->output); + m_pResource->surface->surface->enter(PMONITOR->self.lock()); if (!m_pLayerOwner.expired() && m_pLayerOwner->layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) g_pHyprOpenGL->markBlurDirtyForMonitor(g_pCompositor->getMonitorFromID(m_pLayerOwner->layer)); } void CPopup::onUnmap() { - if (!m_pResource || !m_pResource->surface) + if (!m_bMapped) return; - m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; + + if (!m_pResource || !m_pResource->surface) { + Debug::log(ERR, "CPopup: orphaned (no surface/resource) and unmaps??"); + onDestroy(); + return; + } + + m_vLastSize = m_pResource->surface->surface->current.size; + const auto COORDS = coordsGlobal(); - CBox box; - wlr_surface_get_extends(m_sWLSurface.wlr(), box.pWlr()); - box.applyFromWlr().translate(COORDS).expand(4); + CBox box = m_pWLSurface->resource()->extends(); + box.translate(COORDS).expand(4); g_pHyprRenderer->damageBox(&box); m_pSubsurfaceHead.reset(); @@ -109,7 +123,7 @@ void CPopup::onUnmap() { // damage all children breadthfirst( - [this](CPopup* p, void* data) { + [](CPopup* p, void* data) { if (!p->m_pResource) return; @@ -120,13 +134,19 @@ void CPopup::onUnmap() { } void CPopup::onCommit(bool ignoreSiblings) { + if (!m_pResource || !m_pResource->surface) { + Debug::log(ERR, "CPopup: orphaned (no surface/resource) and commits??"); + onDestroy(); + return; + } + if (m_pResource->surface->initialCommit) { m_pResource->surface->scheduleConfigure(); return; } if (!m_pWindowOwner.expired() && (!m_pWindowOwner->m_bIsMapped || !m_pWindowOwner->m_pWorkspace->m_bVisible)) { - m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; + m_vLastSize = m_pResource->surface->surface->current.size; static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) @@ -140,11 +160,10 @@ void CPopup::onCommit(bool ignoreSiblings) { const auto COORDS = coordsGlobal(); const auto COORDSLOCAL = coordsRelativeToParent(); - if (m_vLastSize != Vector2D{m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height} || m_bRequestedReposition || - m_vLastPos != COORDSLOCAL) { + if (m_vLastSize != m_pResource->surface->surface->current.size || m_bRequestedReposition || m_vLastPos != COORDSLOCAL) { CBox box = {localToGlobal(m_vLastPos), m_vLastSize}; g_pHyprRenderer->damageBox(&box); - m_vLastSize = {m_pResource->surface->surface->current.width, m_pResource->surface->surface->current.height}; + m_vLastSize = m_pResource->surface->surface->current.size; box = {COORDS, m_vLastSize}; g_pHyprRenderer->damageBox(&box); @@ -154,7 +173,7 @@ void CPopup::onCommit(bool ignoreSiblings) { if (!ignoreSiblings && m_pSubsurfaceHead) m_pSubsurfaceHead->recheckDamageForSubsurfaces(); - g_pHyprRenderer->damageSurface(m_sWLSurface.wlr(), COORDS.x, COORDS.y); + g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y); m_bRequestedReposition = false; @@ -194,7 +213,7 @@ Vector2D CPopup::coordsRelativeToParent() { while (current->m_pParent && current->m_pResource) { - offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy}; + offset += current->m_pWLSurface->resource()->current.offset; offset += current->m_pResource->geometry.pos(); current = current->m_pParent; @@ -243,9 +262,9 @@ Vector2D CPopup::size() { void CPopup::sendScale() { if (!m_pWindowOwner.expired()) - g_pCompositor->setPreferredScaleForSurface(m_sWLSurface.wlr(), m_pWindowOwner->m_pWLSurface.m_fLastScale); + g_pCompositor->setPreferredScaleForSurface(m_pWLSurface->resource(), m_pWindowOwner->m_pWLSurface->m_fLastScale); else if (!m_pLayerOwner.expired()) - g_pCompositor->setPreferredScaleForSurface(m_sWLSurface.wlr(), m_pLayerOwner->surface.m_fLastScale); + g_pCompositor->setPreferredScaleForSurface(m_pWLSurface->resource(), m_pLayerOwner->surface->m_fLastScale); else UNREACHABLE(); } @@ -301,9 +320,8 @@ CPopup* CPopup::at(const Vector2D& globalCoords, bool allowsInput) { return p; } else { const Vector2D offset = p->m_pResource ? (p->size() - p->m_pResource->geometry.size()) / 2.F : Vector2D{}; - const auto REGION = CRegion{&p->m_sWLSurface.wlr()->current.input} - .intersect(CBox{{}, {p->m_sWLSurface.wlr()->current.width, p->m_sWLSurface.wlr()->current.height}}) - .translate(p->coordsGlobal() + offset); + const auto REGION = + CRegion{p->m_pWLSurface->resource()->current.input}.intersect(CBox{{}, p->m_pWLSurface->resource()->current.size}).translate(p->coordsGlobal() + offset); if (REGION.containsPoint(globalCoords)) return p; } diff --git a/src/desktop/Popup.hpp b/src/desktop/Popup.hpp index ba6da55a..91e569e7 100644 --- a/src/desktop/Popup.hpp +++ b/src/desktop/Popup.hpp @@ -39,7 +39,7 @@ class CPopup { CPopup* at(const Vector2D& globalCoords, bool allowsInput = false); // - CWLSurface m_sWLSurface; + SP m_pWLSurface; private: // T1 owners, each popup has to have one of these @@ -56,7 +56,8 @@ class CPopup { bool m_bRequestedReposition = false; - bool m_bInert = false; + bool m_bInert = false; + bool m_bMapped = false; // std::vector> m_vChildren; diff --git a/src/desktop/Subsurface.cpp b/src/desktop/Subsurface.cpp index 21482ff7..71ee16f0 100644 --- a/src/desktop/Subsurface.cpp +++ b/src/desktop/Subsurface.cpp @@ -2,29 +2,31 @@ #include "../events/Events.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" - -static void onNewSubsurface(void* owner, void* data); +#include "../protocols/core/Compositor.hpp" +#include "../protocols/core/Subcompositor.hpp" CSubsurface::CSubsurface(PHLWINDOW pOwner) : m_pWindowParent(pOwner) { initSignals(); - initExistingSubsurfaces(pOwner->m_pWLSurface.wlr()); + initExistingSubsurfaces(pOwner->m_pWLSurface->resource()); } CSubsurface::CSubsurface(CPopup* pOwner) : m_pPopupParent(pOwner) { initSignals(); - initExistingSubsurfaces(pOwner->m_sWLSurface.wlr()); + initExistingSubsurfaces(pOwner->m_pWLSurface->resource()); } -CSubsurface::CSubsurface(wlr_subsurface* pSubsurface, PHLWINDOW pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) { - m_sWLSurface.assign(pSubsurface->surface, this); +CSubsurface::CSubsurface(SP pSubsurface, PHLWINDOW pOwner) : m_pSubsurface(pSubsurface), m_pWindowParent(pOwner) { + m_pWLSurface = CWLSurface::create(); + m_pWLSurface->assign(pSubsurface->surface.lock(), this); initSignals(); - initExistingSubsurfaces(pSubsurface->surface); + initExistingSubsurfaces(pSubsurface->surface.lock()); } -CSubsurface::CSubsurface(wlr_subsurface* pSubsurface, CPopup* pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) { - m_sWLSurface.assign(pSubsurface->surface, this); +CSubsurface::CSubsurface(SP pSubsurface, CPopup* pOwner) : m_pSubsurface(pSubsurface), m_pPopupParent(pOwner) { + m_pWLSurface = CWLSurface::create(); + m_pWLSurface->assign(pSubsurface->surface.lock(), this); initSignals(); - initExistingSubsurfaces(pSubsurface->surface); + initExistingSubsurfaces(pSubsurface->surface.lock()); } CSubsurface::~CSubsurface() { @@ -33,52 +35,27 @@ CSubsurface::~CSubsurface() { if (!m_pSubsurface) return; - m_pSubsurface->data = nullptr; - hyprListener_commitSubsurface.removeCallback(); hyprListener_destroySubsurface.removeCallback(); } -static void onNewSubsurface(void* owner, void* data) { - const auto PSUBSURFACE = (CSubsurface*)owner; - PSUBSURFACE->onNewSubsurface((wlr_subsurface*)data); -} - -static void onDestroySubsurface(void* owner, void* data) { - const auto PSUBSURFACE = (CSubsurface*)owner; - PSUBSURFACE->onDestroy(); -} - -static void onCommitSubsurface(void* owner, void* data) { - const auto PSUBSURFACE = (CSubsurface*)owner; - PSUBSURFACE->onCommit(); -} - -static void onMapSubsurface(void* owner, void* data) { - const auto PSUBSURFACE = (CSubsurface*)owner; - PSUBSURFACE->onMap(); -} - -static void onUnmapSubsurface(void* owner, void* data) { - const auto PSUBSURFACE = (CSubsurface*)owner; - PSUBSURFACE->onUnmap(); -} - void CSubsurface::initSignals() { if (m_pSubsurface) { - m_pSubsurface->data = this; - hyprListener_commitSubsurface.initCallback(&m_pSubsurface->surface->events.commit, &onCommitSubsurface, this, "CSubsurface"); - hyprListener_destroySubsurface.initCallback(&m_pSubsurface->events.destroy, &onDestroySubsurface, this, "CSubsurface"); - hyprListener_newSubsurface.initCallback(&m_pSubsurface->surface->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface"); - hyprListener_mapSubsurface.initCallback(&m_pSubsurface->surface->events.map, &onMapSubsurface, this, "CSubsurface"); - hyprListener_unmapSubsurface.initCallback(&m_pSubsurface->surface->events.unmap, &onUnmapSubsurface, this, "CSubsurface"); + listeners.commitSubsurface = m_pSubsurface->surface->events.commit.registerListener([this](std::any d) { onCommit(); }); + listeners.destroySubsurface = m_pSubsurface->events.destroy.registerListener([this](std::any d) { onDestroy(); }); + listeners.mapSubsurface = m_pSubsurface->surface->events.map.registerListener([this](std::any d) { onMap(); }); + listeners.unmapSubsurface = m_pSubsurface->surface->events.unmap.registerListener([this](std::any d) { onUnmap(); }); + listeners.newSubsurface = + m_pSubsurface->surface->events.newSubsurface.registerListener([this](std::any d) { onNewSubsurface(std::any_cast>(d)); }); } else { - if (!m_pWindowParent.expired()) - hyprListener_newSubsurface.initCallback(&m_pWindowParent->m_pWLSurface.wlr()->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface Head"); + if (m_pWindowParent) + listeners.newSubsurface = m_pWindowParent->m_pWLSurface->resource()->events.newSubsurface.registerListener( + [this](std::any d) { onNewSubsurface(std::any_cast>(d)); }); else if (m_pPopupParent) - hyprListener_newSubsurface.initCallback(&m_pPopupParent->m_sWLSurface.wlr()->events.new_subsurface, &::onNewSubsurface, this, "CSubsurface Head"); + listeners.newSubsurface = m_pPopupParent->m_pWLSurface->resource()->events.newSubsurface.registerListener( + [this](std::any d) { onNewSubsurface(std::any_cast>(d)); }); else - RASSERT(false, "CSubsurface::initSignals empty subsurface"); + ASSERT(false); } } @@ -93,21 +70,21 @@ void CSubsurface::checkSiblingDamage() { continue; const auto COORDS = n->coordsGlobal(); - g_pHyprRenderer->damageSurface(n->m_sWLSurface.wlr(), COORDS.x, COORDS.y, SCALE); + g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y, SCALE); } } void CSubsurface::recheckDamageForSubsurfaces() { for (auto& n : m_vChildren) { const auto COORDS = n->coordsGlobal(); - g_pHyprRenderer->damageSurface(n->m_sWLSurface.wlr(), COORDS.x, COORDS.y); + g_pHyprRenderer->damageSurface(n->m_pWLSurface->resource(), COORDS.x, COORDS.y); } } void CSubsurface::onCommit() { // no damaging if it's not visible if (!m_pWindowParent.expired() && (!m_pWindowParent->m_bIsMapped || !m_pWindowParent->m_pWorkspace->m_bVisible)) { - m_vLastSize = Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height}; + m_vLastSize = m_pWLSurface->resource()->current.size; static auto PLOGDAMAGE = CConfigValue("debug:log_damage"); if (*PLOGDAMAGE) @@ -117,7 +94,7 @@ void CSubsurface::onCommit() { const auto COORDS = coordsGlobal(); - g_pHyprRenderer->damageSurface(m_sWLSurface.wlr(), COORDS.x, COORDS.y); + g_pHyprRenderer->damageSurface(m_pWLSurface->resource(), COORDS.x, COORDS.y); if (m_pPopupParent) m_pPopupParent->recheckTree(); @@ -127,10 +104,10 @@ void CSubsurface::onCommit() { // I do not think this is correct, but it solves a lot of issues with some apps (e.g. firefox) checkSiblingDamage(); - if (m_vLastSize != Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height}) { + if (m_vLastSize != m_pWLSurface->resource()->current.size) { CBox box{COORDS, m_vLastSize}; g_pHyprRenderer->damageBox(&box); - m_vLastSize = Vector2D{m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height}; + m_vLastSize = m_pWLSurface->resource()->current.size; box = {COORDS, m_vLastSize}; g_pHyprRenderer->damageBox(&box); } @@ -149,20 +126,21 @@ void CSubsurface::onDestroy() { std::erase_if(m_pParent->m_vChildren, [this](const auto& other) { return other.get() == this; }); } -void CSubsurface::onNewSubsurface(wlr_subsurface* pSubsurface) { +void CSubsurface::onNewSubsurface(SP pSubsurface) { CSubsurface* PSUBSURFACE = nullptr; if (!m_pWindowParent.expired()) PSUBSURFACE = m_vChildren.emplace_back(std::make_unique(pSubsurface, m_pWindowParent.lock())).get(); else if (m_pPopupParent) PSUBSURFACE = m_vChildren.emplace_back(std::make_unique(pSubsurface, m_pPopupParent)).get(); - PSUBSURFACE->m_pParent = this; ASSERT(PSUBSURFACE); + + PSUBSURFACE->m_pParent = this; } void CSubsurface::onMap() { - m_vLastSize = {m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height}; + m_vLastSize = m_pWLSurface->resource()->current.size; const auto COORDS = coordsGlobal(); CBox box{COORDS, m_vLastSize}; @@ -179,7 +157,7 @@ void CSubsurface::onUnmap() { box.expand(4); g_pHyprRenderer->damageBox(&box); - if (m_sWLSurface.wlr() == g_pCompositor->m_pLastFocus) + if (m_pWLSurface->resource() == g_pCompositor->m_pLastFocus) g_pInputManager->releaseAllMouseButtons(); g_pInputManager->simulateMouseMovement(); @@ -188,19 +166,9 @@ void CSubsurface::onUnmap() { } Vector2D CSubsurface::coordsRelativeToParent() { - Vector2D offset; - - CSubsurface* current = this; - - while (current->m_pParent) { - - offset += {current->m_sWLSurface.wlr()->current.dx, current->m_sWLSurface.wlr()->current.dy}; - offset += {current->m_pSubsurface->current.x, current->m_pSubsurface->current.y}; - - current = current->m_pParent; - } - - return offset; + if (!m_pSubsurface) + return {}; + return m_pSubsurface->posRelativeToParent(); } Vector2D CSubsurface::coordsGlobal() { @@ -214,18 +182,16 @@ Vector2D CSubsurface::coordsGlobal() { return coords; } -void CSubsurface::initExistingSubsurfaces(wlr_surface* pSurface) { - wlr_subsurface* wlrSubsurface; - wl_list_for_each(wlrSubsurface, &pSurface->current.subsurfaces_below, current.link) { - ::onNewSubsurface(this, wlrSubsurface); - } - wl_list_for_each(wlrSubsurface, &pSurface->current.subsurfaces_above, current.link) { - ::onNewSubsurface(this, wlrSubsurface); +void CSubsurface::initExistingSubsurfaces(SP pSurface) { + for (auto& s : pSurface->subsurfaces) { + if (!s || s->surface->hlSurface /* already assigned */) + continue; + onNewSubsurface(s.lock()); } } Vector2D CSubsurface::size() { - return {m_sWLSurface.wlr()->current.width, m_sWLSurface.wlr()->current.height}; + return m_pWLSurface->resource()->current.size; } bool CSubsurface::visible() { diff --git a/src/desktop/Subsurface.hpp b/src/desktop/Subsurface.hpp index f3a5ea4b..101f4f19 100644 --- a/src/desktop/Subsurface.hpp +++ b/src/desktop/Subsurface.hpp @@ -5,6 +5,7 @@ #include "WLSurface.hpp" class CPopup; +class CWLSubsurfaceResource; class CSubsurface { public: @@ -13,8 +14,8 @@ class CSubsurface { CSubsurface(CPopup* pOwner); // real nodes - CSubsurface(wlr_subsurface* pSubsurface, PHLWINDOW pOwner); - CSubsurface(wlr_subsurface* pSubsurface, CPopup* pOwner); + CSubsurface(SP pSubsurface, PHLWINDOW pOwner); + CSubsurface(SP pSubsurface, CPopup* pOwner); ~CSubsurface(); @@ -25,7 +26,7 @@ class CSubsurface { void onCommit(); void onDestroy(); - void onNewSubsurface(wlr_subsurface* pSubsurface); + void onNewSubsurface(SP pSubsurface); void onMap(); void onUnmap(); @@ -37,12 +38,18 @@ class CSubsurface { DYNLISTENER(destroySubsurface); DYNLISTENER(commitSubsurface); DYNLISTENER(newSubsurface); - DYNLISTENER(mapSubsurface); - DYNLISTENER(unmapSubsurface); - wlr_subsurface* m_pSubsurface = nullptr; - CWLSurface m_sWLSurface; - Vector2D m_vLastSize = {}; + struct { + CHyprSignalListener destroySubsurface; + CHyprSignalListener commitSubsurface; + CHyprSignalListener mapSubsurface; + CHyprSignalListener unmapSubsurface; + CHyprSignalListener newSubsurface; + } listeners; + + WP m_pSubsurface; + SP m_pWLSurface; + Vector2D m_vLastSize = {}; // if nullptr, means it's a dummy node CSubsurface* m_pParent = nullptr; @@ -55,6 +62,6 @@ class CSubsurface { bool m_bInert = false; void initSignals(); - void initExistingSubsurfaces(wlr_surface* pSurface); + void initExistingSubsurfaces(SP pSurface); void checkSiblingDamage(); }; \ No newline at end of file diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp index 78b50d45..c7a09b40 100644 --- a/src/desktop/WLSurface.cpp +++ b/src/desktop/WLSurface.cpp @@ -1,36 +1,37 @@ #include "WLSurface.hpp" #include "../Compositor.hpp" +#include "../protocols/core/Compositor.hpp" -void CWLSurface::assign(wlr_surface* pSurface) { - m_pWLRSurface = pSurface; +void CWLSurface::assign(SP pSurface) { + m_pResource = pSurface; init(); m_bInert = false; } -void CWLSurface::assign(wlr_surface* pSurface, PHLWINDOW pOwner) { +void CWLSurface::assign(SP pSurface, PHLWINDOW pOwner) { m_pWindowOwner = pOwner; - m_pWLRSurface = pSurface; + m_pResource = pSurface; init(); m_bInert = false; } -void CWLSurface::assign(wlr_surface* pSurface, PHLLS pOwner) { +void CWLSurface::assign(SP pSurface, PHLLS pOwner) { m_pLayerOwner = pOwner; - m_pWLRSurface = pSurface; + m_pResource = pSurface; init(); m_bInert = false; } -void CWLSurface::assign(wlr_surface* pSurface, CSubsurface* pOwner) { +void CWLSurface::assign(SP pSurface, CSubsurface* pOwner) { m_pSubsurfaceOwner = pOwner; - m_pWLRSurface = pSurface; + m_pResource = pSurface; init(); m_bInert = false; } -void CWLSurface::assign(wlr_surface* pSurface, CPopup* pOwner) { +void CWLSurface::assign(SP pSurface, CPopup* pOwner) { m_pPopupOwner = pOwner; - m_pWLRSurface = pSurface; + m_pResource = pSurface; init(); m_bInert = false; } @@ -44,20 +45,23 @@ CWLSurface::~CWLSurface() { } bool CWLSurface::exists() const { - return m_pWLRSurface; + return m_pResource; } -wlr_surface* CWLSurface::wlr() const { - return m_pWLRSurface; +SP CWLSurface::resource() const { + return m_pResource.lock(); } bool CWLSurface::small() const { if (!validMapped(m_pWindowOwner) || !exists()) return false; + if (!m_pResource->current.buffer) + return false; + const auto O = m_pWindowOwner.lock(); - return O->m_vReportedSize.x > m_pWLRSurface->current.buffer_width + 1 || O->m_vReportedSize.y > m_pWLRSurface->current.buffer_height + 1; + return O->m_vReportedSize.x > m_pResource->current.buffer->size.x + 1 || O->m_vReportedSize.y > m_pResource->current.buffer->size.y + 1; } Vector2D CWLSurface::correctSmallVec() const { @@ -71,29 +75,28 @@ Vector2D CWLSurface::correctSmallVec() const { } Vector2D CWLSurface::getViewporterCorrectedSize() const { - if (!exists()) + if (!exists() || !m_pResource->current.buffer) return {}; - return m_pWLRSurface->current.viewport.has_dst ? Vector2D{m_pWLRSurface->current.viewport.dst_width, m_pWLRSurface->current.viewport.dst_height} : - Vector2D{m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height}; + return m_pResource->current.viewport.hasDestination ? m_pResource->current.viewport.destination : m_pResource->current.buffer->size; } CRegion CWLSurface::logicalDamage() const { - CRegion damage{&m_pWLRSurface->buffer_damage}; - damage.transform(m_pWLRSurface->current.transform, m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height); - damage.scale(1.0 / m_pWLRSurface->current.scale); + if (!m_pResource->current.buffer) + return {}; + + CRegion damage = m_pResource->accumulateCurrentBufferDamage(); + damage.transform(m_pResource->current.transform, m_pResource->current.buffer->size.x, m_pResource->current.buffer->size.y); + damage.scale(1.0 / m_pResource->current.scale); const auto VPSIZE = getViewporterCorrectedSize(); const auto CORRECTVEC = correctSmallVec(); - if (m_pWLRSurface->current.viewport.has_src) { - damage.intersect(CBox{std::floor(m_pWLRSurface->current.viewport.src.x), std::floor(m_pWLRSurface->current.viewport.src.y), - std::ceil(m_pWLRSurface->current.viewport.src.width), std::ceil(m_pWLRSurface->current.viewport.src.height)}); - } + if (m_pResource->current.viewport.hasSource) + damage.intersect(m_pResource->current.viewport.source); - const auto SCALEDSRCSIZE = m_pWLRSurface->current.viewport.has_src ? - Vector2D{m_pWLRSurface->current.viewport.src.width, m_pWLRSurface->current.viewport.src.height} * m_pWLRSurface->current.scale : - Vector2D{m_pWLRSurface->current.buffer_width, m_pWLRSurface->current.buffer_height}; + const auto SCALEDSRCSIZE = + m_pResource->current.viewport.hasSource ? m_pResource->current.viewport.source.size() * m_pResource->current.scale : m_pResource->current.buffer->size; damage.scale({VPSIZE.x / SCALEDSRCSIZE.x, VPSIZE.y / SCALEDSRCSIZE.y}); damage.translate(CORRECTVEC); @@ -102,48 +105,38 @@ CRegion CWLSurface::logicalDamage() const { } void CWLSurface::destroy() { - if (!m_pWLRSurface) + if (!m_pResource) return; events.destroy.emit(); m_pConstraint.reset(); - hyprListener_destroy.removeCallback(); - hyprListener_commit.removeCallback(); - m_pWLRSurface->data = nullptr; + listeners.destroy.reset(); + m_pResource->hlSurface.reset(); m_pWindowOwner.reset(); m_pLayerOwner.reset(); m_pPopupOwner = nullptr; m_pSubsurfaceOwner = nullptr; m_bInert = true; - if (g_pCompositor && g_pCompositor->m_pLastFocus == m_pWLRSurface) - g_pCompositor->m_pLastFocus = nullptr; - if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf == this) + if (g_pHyprRenderer && g_pHyprRenderer->m_sLastCursorData.surf && g_pHyprRenderer->m_sLastCursorData.surf->get() == this) g_pHyprRenderer->m_sLastCursorData.surf.reset(); - m_pWLRSurface = nullptr; + m_pResource.reset(); Debug::log(LOG, "CWLSurface {:x} called destroy()", (uintptr_t)this); } -static void onCommit(void* owner, void* data) { - const auto SURF = (CWLSurface*)owner; - SURF->onCommit(); -} - void CWLSurface::init() { - if (!m_pWLRSurface) + if (!m_pResource) return; - RASSERT(!m_pWLRSurface->data, "Attempted to duplicate CWLSurface ownership!"); + RASSERT(!m_pResource->hlSurface, "Attempted to duplicate CWLSurface ownership!"); - m_pWLRSurface->data = this; + m_pResource->hlSurface = self.lock(); - hyprListener_destroy.initCallback( - &m_pWLRSurface->events.destroy, [&](void* owner, void* data) { destroy(); }, this, "CWLSurface"); - hyprListener_commit.initCallback(&m_pWLRSurface->events.commit, ::onCommit, this, "CWLSurface"); + listeners.destroy = m_pResource->events.destroy.registerListener([this](std::any d) { destroy(); }); Debug::log(LOG, "CWLSurface {:x} called init()", (uintptr_t)this); } @@ -188,10 +181,6 @@ void CWLSurface::appendConstraint(WP constraint) { m_pConstraint = constraint; } -void CWLSurface::onCommit() { - ; -} - SP CWLSurface::constraint() { return m_pConstraint.lock(); } @@ -207,3 +196,9 @@ bool CWLSurface::visible() { return m_pSubsurfaceOwner->visible(); return true; // non-desktop, we don't know much. } + +SP CWLSurface::fromResource(SP pSurface) { + if (!pSurface) + return nullptr; + return pSurface->hlSurface.lock(); +} diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp index 03e81b45..4ba381a9 100644 --- a/src/desktop/WLSurface.hpp +++ b/src/desktop/WLSurface.hpp @@ -7,33 +7,37 @@ class CSubsurface; class CPopup; class CPointerConstraint; +class CWLSurfaceResource; class CWLSurface { public: - CWLSurface() = default; + static SP create() { + auto p = SP(new CWLSurface); + p->self = p; + return p; + } ~CWLSurface(); // anonymous surfaces are non-desktop components, e.g. a cursor surface or a DnD - void assign(wlr_surface* pSurface); - void assign(wlr_surface* pSurface, PHLWINDOW pOwner); - void assign(wlr_surface* pSurface, PHLLS pOwner); - void assign(wlr_surface* pSurface, CSubsurface* pOwner); - void assign(wlr_surface* pSurface, CPopup* pOwner); + void assign(SP pSurface); + void assign(SP pSurface, PHLWINDOW pOwner); + void assign(SP pSurface, PHLLS pOwner); + void assign(SP pSurface, CSubsurface* pOwner); + void assign(SP pSurface, CPopup* pOwner); void unassign(); - CWLSurface(const CWLSurface&) = delete; - CWLSurface(CWLSurface&&) = delete; - CWLSurface& operator=(const CWLSurface&) = delete; - CWLSurface& operator=(CWLSurface&&) = delete; + CWLSurface(const CWLSurface&) = delete; + CWLSurface(CWLSurface&&) = delete; + CWLSurface& operator=(const CWLSurface&) = delete; + CWLSurface& operator=(CWLSurface&&) = delete; - wlr_surface* wlr() const; - bool exists() const; - bool small() const; // means surface is smaller than the requested size - Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces - Vector2D getViewporterCorrectedSize() const; - CRegion logicalDamage() const; - void onCommit(); - bool visible(); + SP resource() const; + bool exists() const; + bool small() const; // means surface is smaller than the requested size + Vector2D correctSmallVec() const; // returns a corrective vector for small() surfaces + Vector2D getViewporterCorrectedSize() const; + CRegion logicalDamage() const; + bool visible(); // getters for owners. PHLWINDOW getWindow(); @@ -55,31 +59,27 @@ class CWLSurface { wl_output_transform m_eLastTransform = (wl_output_transform)-1; // - CWLSurface& operator=(wlr_surface* pSurface) { + CWLSurface& operator=(SP pSurface) { destroy(); - m_pWLRSurface = pSurface; + m_pResource = pSurface; init(); return *this; } bool operator==(const CWLSurface& other) const { - return other.wlr() == wlr(); + return other.resource() == resource(); } - bool operator==(const wlr_surface* other) const { - return other == wlr(); + bool operator==(const SP other) const { + return other == resource(); } explicit operator bool() const { return exists(); } - static CWLSurface* surfaceFromWlr(wlr_surface* pSurface) { - if (!pSurface) - return nullptr; - return (CWLSurface*)pSurface->data; - } + static SP fromResource(SP pSurface); // used by the alpha-modifier protocol float m_pAlphaModifier = 1.F; @@ -88,15 +88,19 @@ class CWLSurface { CSignal destroy; } events; + WP self; + private: - bool m_bInert = true; + CWLSurface() = default; - wlr_surface* m_pWLRSurface = nullptr; + bool m_bInert = true; - PHLWINDOWREF m_pWindowOwner; - PHLLSREF m_pLayerOwner; - CPopup* m_pPopupOwner = nullptr; - CSubsurface* m_pSubsurfaceOwner = nullptr; + WP m_pResource; + + PHLWINDOWREF m_pWindowOwner; + PHLLSREF m_pLayerOwner; + CPopup* m_pPopupOwner = nullptr; + CSubsurface* m_pSubsurfaceOwner = nullptr; // WP m_pConstraint; @@ -105,8 +109,9 @@ class CWLSurface { void init(); bool desktopComponent(); - DYNLISTENER(destroy); - DYNLISTENER(commit); + struct { + CHyprSignalListener destroy; + } listeners; friend class CPointerConstraint; }; \ No newline at end of file diff --git a/src/desktop/Window.cpp b/src/desktop/Window.cpp index 7987c9d6..5d6e32e9 100644 --- a/src/desktop/Window.cpp +++ b/src/desktop/Window.cpp @@ -1,17 +1,23 @@ +#include +#include +#include #include "Window.hpp" #include "../Compositor.hpp" #include "../render/decorations/CHyprDropShadowDecoration.hpp" #include "../render/decorations/CHyprGroupBarDecoration.hpp" #include "../render/decorations/CHyprBorderDecoration.hpp" #include "../config/ConfigValue.hpp" -#include #include "../managers/TokenManager.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../xwayland/XWayland.hpp" -PHLWINDOW CWindow::create() { - PHLWINDOW pWindow = SP(new CWindow); +PHLWINDOW CWindow::create(SP surface) { + PHLWINDOW pWindow = SP(new CWindow(surface)); - pWindow->m_pSelf = pWindow; + pWindow->m_pSelf = pWindow; + pWindow->m_bIsX11 = true; + pWindow->m_iX11Type = surface->overrideRedirect ? 2 : 1; pWindow->m_vRealPosition.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); pWindow->m_vRealSize.create(g_pConfigManager->getAnimationPropertyConfig("windowsIn"), pWindow, AVARDAMAGE_ENTIRE); @@ -46,12 +52,14 @@ PHLWINDOW CWindow::create(SP resource) { pWindow->addWindowDeco(std::make_unique(pWindow)); pWindow->addWindowDeco(std::make_unique(pWindow)); - pWindow->m_pWLSurface.assign(pWindow->m_pXDGSurface->surface, pWindow); + pWindow->m_pWLSurface->assign(pWindow->m_pXDGSurface->surface.lock(), pWindow); return pWindow; } CWindow::CWindow(SP resource) : m_pXDGSurface(resource) { + m_pWLSurface = CWLSurface::create(); + listeners.map = m_pXDGSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); }); listeners.ack = m_pXDGSurface->events.ack.registerListener([this](std::any d) { onAck(std::any_cast(d)); }); listeners.unmap = m_pXDGSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); }); @@ -61,13 +69,26 @@ CWindow::CWindow(SP resource) : m_pXDGSurface(resource) { listeners.updateMetadata = m_pXDGSurface->toplevel->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); }); } -CWindow::CWindow() { - ; +CWindow::CWindow(SP surface) : m_pXWaylandSurface(surface) { + m_pWLSurface = CWLSurface::create(); + + listeners.map = m_pXWaylandSurface->events.map.registerListener([this](std::any d) { Events::listener_mapWindow(this, nullptr); }); + listeners.unmap = m_pXWaylandSurface->events.unmap.registerListener([this](std::any d) { Events::listener_unmapWindow(this, nullptr); }); + listeners.destroy = m_pXWaylandSurface->events.destroy.registerListener([this](std::any d) { Events::listener_destroyWindow(this, nullptr); }); + listeners.commit = m_pXWaylandSurface->events.commit.registerListener([this](std::any d) { Events::listener_commitWindow(this, nullptr); }); + listeners.configure = m_pXWaylandSurface->events.configure.registerListener([this](std::any d) { onX11Configure(std::any_cast(d)); }); + listeners.updateState = m_pXWaylandSurface->events.stateChanged.registerListener([this](std::any d) { onUpdateState(); }); + listeners.updateMetadata = m_pXWaylandSurface->events.metadataChanged.registerListener([this](std::any d) { onUpdateMeta(); }); + listeners.resourceChange = m_pXWaylandSurface->events.resourceChange.registerListener([this](std::any d) { onResourceChangeX11(); }); + listeners.activate = m_pXWaylandSurface->events.activate.registerListener([this](std::any d) { Events::listener_activateX11(this, nullptr); }); + + if (m_pXWaylandSurface->overrideRedirect) + listeners.setGeometry = m_pXWaylandSurface->events.setGeometry.registerListener([this](std::any d) { Events::listener_unmanagedSetGeometry(this, nullptr); }); } CWindow::~CWindow() { if (g_pCompositor->m_pLastWindow.lock().get() == this) { - g_pCompositor->m_pLastFocus = nullptr; + g_pCompositor->m_pLastFocus.reset(); g_pCompositor->m_pLastWindow.reset(); } @@ -108,12 +129,12 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() { if (EXTENTS.bottomRight.y > maxExtents.bottomRight.y) maxExtents.bottomRight.y = EXTENTS.bottomRight.y; - if (m_pWLSurface.exists() && !m_bIsX11 && m_pPopupHead) { + if (m_pWLSurface->exists() && !m_bIsX11 && m_pPopupHead) { CBox surfaceExtents = {0, 0, 0, 0}; // TODO: this could be better, perhaps make a getFullWindowRegion? m_pPopupHead->breadthfirst( [](CPopup* popup, void* data) { - if (!popup->m_sWLSurface.wlr()) + if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource()) return; CBox* pSurfaceExtents = (CBox*)data; @@ -135,11 +156,11 @@ SWindowDecorationExtents CWindow::getFullWindowExtents() { if (-surfaceExtents.y > maxExtents.topLeft.y) maxExtents.topLeft.y = -surfaceExtents.y; - if (surfaceExtents.x + surfaceExtents.width > m_pWLSurface.wlr()->current.width + maxExtents.bottomRight.x) - maxExtents.bottomRight.x = surfaceExtents.x + surfaceExtents.width - m_pWLSurface.wlr()->current.width; + if (surfaceExtents.x + surfaceExtents.width > m_pWLSurface->resource()->current.size.x + maxExtents.bottomRight.x) + maxExtents.bottomRight.x = surfaceExtents.x + surfaceExtents.width - m_pWLSurface->resource()->current.size.x; - if (surfaceExtents.y + surfaceExtents.height > m_pWLSurface.wlr()->current.height + maxExtents.bottomRight.y) - maxExtents.bottomRight.y = surfaceExtents.y + surfaceExtents.height - m_pWLSurface.wlr()->current.height; + if (surfaceExtents.y + surfaceExtents.height > m_pWLSurface->resource()->current.size.y + maxExtents.bottomRight.y) + maxExtents.bottomRight.y = surfaceExtents.y + surfaceExtents.height - m_pWLSurface->resource()->current.size.y; } return maxExtents; @@ -160,11 +181,13 @@ CBox CWindow::getFullWindowBoundingBox() { } CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { - const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - auto POS = m_vPosition; - auto SIZE = m_vSize; + if (!PMONITOR) + return {m_vPosition, m_vSize}; + + auto POS = m_vPosition; + auto SIZE = m_vSize; if (m_bIsFullscreen) { POS = PMONITOR->vecPosition; @@ -192,10 +215,10 @@ CBox CWindow::getWindowIdealBoundingBoxIgnoreReserved() { } CBox CWindow::getWindowBoxUnified(uint64_t properties) { - if (m_sAdditionalConfigData.dimAround) { const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + if (PMONITOR) + return {PMONITOR->vecPosition.x, PMONITOR->vecPosition.y, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; } SWindowDecorationExtents EXTENTS = {{0, 0}, {0, 0}}; @@ -300,10 +323,10 @@ pid_t CWindow::getPID() { wl_client_get_credentials(m_pXDGSurface->owner->client(), &PID, nullptr, nullptr); } else { - if (!m_uSurface.xwayland) + if (!m_pXWaylandSurface) return -1; - PID = m_uSurface.xwayland->pid; + PID = m_pXWaylandSurface->pid; } return PID; @@ -322,17 +345,7 @@ void CWindow::updateToplevel() { updateSurfaceScaleTransformDetails(); } -void sendEnterIter(wlr_surface* pSurface, int x, int y, void* data) { - const auto OUTPUT = (wlr_output*)data; - wlr_surface_send_enter(pSurface, OUTPUT); -} - -void sendLeaveIter(wlr_surface* pSurface, int x, int y, void* data) { - const auto OUTPUT = (wlr_output*)data; - wlr_surface_send_leave(pSurface, OUTPUT); -} - -void CWindow::updateSurfaceScaleTransformDetails() { +void CWindow::updateSurfaceScaleTransformDetails(bool force) { if (!m_bIsMapped || m_bHidden || g_pCompositor->m_bUnsafeState) return; @@ -345,26 +358,25 @@ void CWindow::updateSurfaceScaleTransformDetails() { if (!PNEWMONITOR) return; - if (PNEWMONITOR != PLASTMONITOR) { - if (PLASTMONITOR && PLASTMONITOR->m_bEnabled) - wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output); + if (PNEWMONITOR != PLASTMONITOR || force) { + if (PLASTMONITOR && PLASTMONITOR->m_bEnabled && PNEWMONITOR != PLASTMONITOR) + m_pWLSurface->resource()->breadthfirst([PLASTMONITOR](SP s, const Vector2D& offset, void* d) { s->leave(PLASTMONITOR->self.lock()); }, nullptr); - wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output); + m_pWLSurface->resource()->breadthfirst([PNEWMONITOR](SP s, const Vector2D& offset, void* d) { s->enter(PNEWMONITOR->self.lock()); }, nullptr); } - wlr_surface_for_each_surface( - m_pWLSurface.wlr(), - [](wlr_surface* surf, int x, int y, void* data) { - const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID); + m_pWLSurface->resource()->breadthfirst( + [this](SP s, const Vector2D& offset, void* d) { + const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); - const auto PSURFACE = CWLSurface::surfaceFromWlr(surf); + const auto PSURFACE = CWLSurface::fromResource(s); if (PSURFACE && PSURFACE->m_fLastScale == PMONITOR->scale) return; - g_pCompositor->setPreferredScaleForSurface(surf, PMONITOR->scale); - g_pCompositor->setPreferredTransformForSurface(surf, PMONITOR->transform); + g_pCompositor->setPreferredScaleForSurface(s, PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(s, PMONITOR->transform); }, - this); + nullptr); } void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) { @@ -426,22 +438,23 @@ void CWindow::moveToWorkspace(PHLWORKSPACE pWorkspace) { } PHLWINDOW CWindow::X11TransientFor() { - if (!m_bIsX11) + if (!m_pXWaylandSurface || !m_pXWaylandSurface->parent) return nullptr; - if (!m_uSurface.xwayland->parent) - return nullptr; - - auto PPARENT = g_pCompositor->getWindowFromSurface(m_uSurface.xwayland->parent->surface); - - while (validMapped(PPARENT) && PPARENT->m_uSurface.xwayland->parent) { - PPARENT = g_pCompositor->getWindowFromSurface(PPARENT->m_uSurface.xwayland->parent->surface); + auto s = m_pXWaylandSurface->parent; + while (s) { + if (!s->parent) + break; + s = s->parent; } - if (!validMapped(PPARENT)) - return nullptr; + for (auto& w : g_pCompositor->m_vWindows) { + if (w->m_pXWaylandSurface != s) + continue; + return w; + } - return PPARENT; + return nullptr; } void CWindow::removeDecorationByType(eDecorationType type) { @@ -494,8 +507,6 @@ void CWindow::onUnmap() { std::erase_if(g_pCompositor->m_vWindowFocusHistory, [&](const auto& other) { return other.expired() || other.lock().get() == this; }); - hyprListener_unmapWindow.removeCallback(); - if (*PCLOSEONLASTSPECIAL && g_pCompositor->getWindowsOnWorkspace(workspaceID()) == 0 && onSpecialWorkspace()) { const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); if (PMONITOR && PMONITOR->activeSpecialWorkspace && PMONITOR->activeSpecialWorkspace == m_pWorkspace) @@ -548,12 +559,11 @@ void CWindow::onMap() { g_pCompositor->m_vWindowFocusHistory.push_back(m_pSelf); - if (m_bIsX11) - hyprListener_unmapWindow.initCallback(&m_uSurface.xwayland->surface->events.unmap, &Events::listener_unmapWindow, this, "CWindow"); - m_vReportedSize = m_vPendingReportedSize; m_bAnimatingIn = true; + updateSurfaceScaleTransformDetails(true); + if (m_bIsX11) return; @@ -609,6 +619,13 @@ void CWindow::applyDynamicRule(const SWindowRule& r) { m_sAdditionalConfigData.forceTearing = true; } else if (r.szRule == "nearestneighbor") { m_sAdditionalConfigData.nearestNeighbor = true; + } else if (r.szRule.starts_with("tag")) { + CVarList vars{r.szRule, 0, 's', true}; + + if (vars.size() == 2 && vars[0] == "tag") + m_tags.applyTag(vars[1], true); + else + Debug::log(ERR, "Tag rule invalid: {}", r.szRule); } else if (r.szRule.starts_with("rounding")) { try { m_sAdditionalConfigData.rounding = std::stoi(r.szRule.substr(r.szRule.find_first_of(' ') + 1)); @@ -790,6 +807,8 @@ void CWindow::updateDynamicRules() { m_sAdditionalConfigData.nearestNeighbor = false; m_eIdleInhibitMode = IDLEINHIBIT_NONE; + m_tags.removeDynamicTags(); + m_vMatchedRules = g_pConfigManager->getMatchingRules(m_pSelf.lock()); for (auto& r : m_vMatchedRules) { applyDynamicRule(r); @@ -837,7 +856,7 @@ bool CWindow::hasPopupAt(const Vector2D& pos) { CPopup* popup = m_pPopupHead->at(pos); - return popup && popup->m_sWLSurface.wlr(); + return popup && popup->m_pWLSurface->resource(); } void CWindow::applyGroupRules() { @@ -1112,23 +1131,24 @@ bool CWindow::opaque() { const auto PWORKSPACE = m_pWorkspace; - if (m_pWLSurface.small() && !m_pWLSurface.m_bFillIgnoreSmall) + if (m_pWLSurface->small() && !m_pWLSurface->m_bFillIgnoreSmall) return false; if (PWORKSPACE->m_fAlpha.value() != 1.f) return false; - if (m_bIsX11) - return !m_uSurface.xwayland->has_alpha; + if (m_bIsX11 && m_pXWaylandSurface && m_pXWaylandSurface->surface && m_pXWaylandSurface->surface->current.buffer) + return m_pXWaylandSurface->surface->current.buffer->opaque; - if (m_pXDGSurface->surface->opaque) + if (!m_pWLSurface->resource() || !m_pWLSurface->resource()->current.buffer) + return false; + + // TODO: this is wrong + const auto EXTENTS = m_pXDGSurface->surface->current.opaque.getExtents(); + if (EXTENTS.w >= m_pXDGSurface->surface->current.buffer->size.x && EXTENTS.h >= m_pXDGSurface->surface->current.buffer->size.y) return true; - const auto EXTENTS = pixman_region32_extents(&m_pXDGSurface->surface->opaque_region); - if (EXTENTS->x2 - EXTENTS->x1 >= m_pXDGSurface->surface->current.buffer_width && EXTENTS->y2 - EXTENTS->y1 >= m_pXDGSurface->surface->current.buffer_height) - return true; - - return false; + return m_pWLSurface->resource()->current.buffer->opaque; } float CWindow::rounding() { @@ -1259,8 +1279,7 @@ int CWindow::surfacesCount() { return 1; int no = 0; - wlr_surface_for_each_surface( - m_pWLSurface.wlr(), [](wlr_surface* surf, int x, int y, void* data) { *((int*)data) += 1; }, &no); + m_pWLSurface->resource()->breadthfirst([](SP r, const Vector2D& offset, void* d) { *((int*)d) += 1; }, &no); return no; } @@ -1296,9 +1315,12 @@ std::unordered_map CWindow::getEnv() { needle += 512; } + if (needle <= 1) + return {}; + std::replace(buffer.begin(), buffer.end() - 1, '\0', '\n'); - CVarList envs(std::string{buffer.data(), needle - 1}, 0, '\n', true); + CVarList envs(std::string{buffer.data(), buffer.size() - 1}, 0, '\n', true); for (auto& e : envs) { if (!e.contains('=')) @@ -1330,33 +1352,34 @@ void CWindow::activate(bool force) { g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true); g_pCompositor->focusWindow(m_pSelf.lock()); - g_pCompositor->warpCursorTo(middle()); + warpCursor(); } void CWindow::onUpdateState() { - if (!m_pXDGSurface) - return; + std::optional requestsFS = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsFullscreen : m_pXWaylandSurface->state.requestsFullscreen; + std::optional requestsMX = m_pXDGSurface ? m_pXDGSurface->toplevel->state.requestsMaximize : m_pXWaylandSurface->state.requestsMaximize; - if (m_pXDGSurface->toplevel->state.requestsFullscreen && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) { - bool fs = m_pXDGSurface->toplevel->state.requestsFullscreen.value(); + if (requestsFS.has_value() && !(m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) { + bool fs = requestsFS.value(); - if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) + if (fs != m_bIsFullscreen && m_bIsMapped) g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_FULL); - if (!m_pXDGSurface->mapped) + if (!m_bIsMapped) m_bWantsInitialFullscreen = fs; } - if (m_pXDGSurface->toplevel->state.requestsMaximize && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) { - bool fs = m_pXDGSurface->toplevel->state.requestsMaximize.value(); + if (requestsMX.has_value() && !(m_eSuppressedEvents & SUPPRESS_MAXIMIZE)) { + bool fs = requestsMX.value(); - if (fs != m_bIsFullscreen && m_pXDGSurface->mapped) + if (fs != m_bIsFullscreen && m_bIsMapped) g_pCompositor->setWindowFullscreen(m_pSelf.lock(), fs, FULLSCREEN_MAXIMIZED); } } void CWindow::onUpdateMeta() { const auto NEWTITLE = fetchTitle(); + bool doUpdate = false; if (m_szTitle != NEWTITLE) { m_szTitle = NEWTITLE; @@ -1369,11 +1392,8 @@ void CWindow::onUpdateMeta() { EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); } - updateDynamicRules(); - g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); - updateToplevel(); - Debug::log(LOG, "Window {:x} set title to {}", (uintptr_t)this, m_szTitle); + doUpdate = true; } const auto NEWCLASS = fetchClass(); @@ -1386,11 +1406,14 @@ void CWindow::onUpdateMeta() { EMIT_HOOK_EVENT("activeWindow", m_pSelf.lock()); } + Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); + doUpdate = true; + } + + if (doUpdate) { updateDynamicRules(); g_pCompositor->updateWindowAnimatedDecorationValues(m_pSelf.lock()); updateToplevel(); - - Debug::log(LOG, "Window {:x} set class to {}", (uintptr_t)this, m_szClass); } } @@ -1399,8 +1422,8 @@ std::string CWindow::fetchTitle() { if (m_pXDGSurface && m_pXDGSurface->toplevel) return m_pXDGSurface->toplevel->state.title; } else { - if (m_uSurface.xwayland && m_uSurface.xwayland->title) - return m_uSurface.xwayland->title; + if (m_pXWaylandSurface) + return m_pXWaylandSurface->state.title; } return ""; @@ -1411,8 +1434,8 @@ std::string CWindow::fetchClass() { if (m_pXDGSurface && m_pXDGSurface->toplevel) return m_pXDGSurface->toplevel->state.appid; } else { - if (m_uSurface.xwayland && m_uSurface.xwayland->_class) - return m_uSurface.xwayland->_class; + if (m_pXWaylandSurface) + return m_pXWaylandSurface->state.appid; } return ""; @@ -1426,4 +1449,91 @@ void CWindow::onAck(uint32_t serial) { m_pPendingSizeAck = *SERIAL; std::erase_if(m_vPendingSizeAcks, [&](const auto& el) { return el.first <= SERIAL->first; }); -} \ No newline at end of file +} + +void CWindow::onResourceChangeX11() { + if (m_pXWaylandSurface->surface && !m_pWLSurface->resource()) + m_pWLSurface->assign(m_pXWaylandSurface->surface.lock(), m_pSelf.lock()); + else if (!m_pXWaylandSurface->surface && m_pWLSurface->resource()) + m_pWLSurface->unassign(); + + // update metadata as well, + // could be first assoc and we need to catch the class + onUpdateMeta(); + + Debug::log(LOG, "xwayland window {:x} -> association to {:x}", (uintptr_t)m_pXWaylandSurface.get(), (uintptr_t)m_pWLSurface->resource().get()); +} + +void CWindow::onX11Configure(CBox box) { + + if (!m_pXWaylandSurface->surface || !m_pXWaylandSurface->mapped || !m_bIsMapped) { + m_pXWaylandSurface->configure(box); + m_vPendingReportedSize = box.size(); + m_vReportedSize = box.size(); + if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR) + m_fX11SurfaceScaledBy = PMONITOR->scale; + return; + } + + g_pHyprRenderer->damageWindow(m_pSelf.lock()); + + if (!m_bIsFloating || m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow == m_pSelf) { + g_pXWaylandManager->setWindowSize(m_pSelf.lock(), m_vRealSize.goal(), true); + g_pInputManager->refocus(); + g_pHyprRenderer->damageWindow(m_pSelf.lock()); + return; + } + + if (box.size() > Vector2D{1, 1}) + setHidden(false); + else + setHidden(true); + + const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords(box.pos()); + + m_vRealPosition.setValueAndWarp(LOGICALPOS); + m_vRealSize.setValueAndWarp(box.size()); + + static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); + if (*PXWLFORCESCALEZERO) { + if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR) { + m_vRealSize.setValueAndWarp(m_vRealSize.goal() / PMONITOR->scale); + m_fX11SurfaceScaledBy = PMONITOR->scale; + } + } + + m_vPosition = m_vRealPosition.value(); + m_vSize = m_vRealSize.value(); + + m_pXWaylandSurface->configure(box); + + m_vPendingReportedSize = box.size(); + m_vReportedSize = box.size(); + + updateWindowDecos(); + + if (!g_pCompositor->isWorkspaceVisible(m_pWorkspace)) + return; // further things are only for visible windows + + m_pWorkspace = g_pCompositor->getMonitorFromVector(m_vRealPosition.value() + m_vRealSize.value() / 2.f)->activeWorkspace; + + g_pCompositor->changeWindowZOrder(m_pSelf.lock(), true); + + m_bCreatedOverFullscreen = true; + + if (!m_sAdditionalConfigData.windowDanceCompat) + g_pInputManager->refocus(); + + g_pHyprRenderer->damageWindow(m_pSelf.lock()); +} + +void CWindow::warpCursor() { + static auto PERSISTENTWARPS = CConfigValue("cursor:persistent_warps"); + const auto coords = m_vRelativeCursorCoordsOnLastWarp; + m_vRelativeCursorCoordsOnLastWarp.x = -1; // reset m_vRelativeCursorCoordsOnLastWarp + + if (*PERSISTENTWARPS && coords.x > 0 && coords.y > 0 && coords < m_vSize) // don't warp cursor outside the window + g_pCompositor->warpCursorTo(m_vPosition + coords); + else + g_pCompositor->warpCursorTo(middle()); +} diff --git a/src/desktop/Window.hpp b/src/desktop/Window.hpp index 24d9562b..85c74622 100644 --- a/src/desktop/Window.hpp +++ b/src/desktop/Window.hpp @@ -1,20 +1,24 @@ #pragma once -#include "../defines.hpp" -#include "Subsurface.hpp" -#include "../helpers/AnimatedVariable.hpp" -#include "../render/decorations/IHyprWindowDecoration.hpp" #include +#include + #include "../config/ConfigDataValues.hpp" +#include "../defines.hpp" +#include "../helpers/AnimatedVariable.hpp" #include "../helpers/Vector2D.hpp" -#include "WLSurface.hpp" -#include "Popup.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/TagKeeper.hpp" #include "../macros.hpp" #include "../managers/XWaylandManager.hpp" +#include "../render/decorations/IHyprWindowDecoration.hpp" #include "DesktopTypes.hpp" -#include "../helpers/signal/Signal.hpp" +#include "Popup.hpp" +#include "Subsurface.hpp" +#include "WLSurface.hpp" class CXDGSurfaceResource; +class CXWaylandSurface; enum eIdleInhibitMode { IDLEINHIBIT_NONE = 0, @@ -182,6 +186,7 @@ struct SWindowRule { std::string szClass; std::string szInitialTitle; std::string szInitialClass; + std::string szTag; int bX11 = -1; // -1 means "ANY" int bFloating = -1; int bFullscreen = -1; @@ -199,48 +204,23 @@ struct SInitialWorkspaceToken { class CWindow { public: static PHLWINDOW create(SP); - // xwl - static PHLWINDOW create(); + static PHLWINDOW create(SP); private: CWindow(SP resource); - CWindow(); + CWindow(SP surface); public: ~CWindow(); - DYNLISTENER(commitWindow); - DYNLISTENER(mapWindow); - DYNLISTENER(unmapWindow); - DYNLISTENER(destroyWindow); - DYNLISTENER(setTitleWindow); - DYNLISTENER(setGeometryX11U); - DYNLISTENER(fullscreenWindow); - DYNLISTENER(requestMove); - DYNLISTENER(requestMinimize); - DYNLISTENER(requestMaximize); - DYNLISTENER(requestResize); - DYNLISTENER(activateX11); - DYNLISTENER(configureX11); - DYNLISTENER(toplevelClose); - DYNLISTENER(toplevelActivate); - DYNLISTENER(toplevelFullscreen); - DYNLISTENER(setOverrideRedirect); - DYNLISTENER(associateX11); - DYNLISTENER(dissociateX11); - DYNLISTENER(ackConfigure); - // DYNLISTENER(newSubsurfaceWindow); - - CWLSurface m_pWLSurface; + SP m_pWLSurface; struct { CSignal destroy; } events; - union { - wlr_xwayland_surface* xwayland; - } m_uSurface; WP m_pXDGSurface; + WP m_pXWaylandSurface; // this is the position and size of the "bounding box" Vector2D m_vPosition = Vector2D(0, 0); @@ -265,8 +245,11 @@ class CWindow { Vector2D m_vFloatingOffset = Vector2D(0, 0); // this is used for pseudotiling - bool m_bIsPseudotiled = false; - Vector2D m_vPseudoSize = Vector2D(1280, 720); + bool m_bIsPseudotiled = false; + Vector2D m_vPseudoSize = Vector2D(1280, 720); + + // for recovering relative cursor position + Vector2D m_vRelativeCursorCoordsOnLastWarp = Vector2D(-1, -1); bool m_bFirstMap = false; // for layouts bool m_bIsFloating = false; @@ -389,9 +372,12 @@ class CWindow { // stores the currently matched window rules std::vector m_vMatchedRules; + // window tags + CTagKeeper m_tags; + // For the list lookup bool operator==(const CWindow& rhs) { - return m_pXDGSurface == rhs.m_pXDGSurface && m_uSurface.xwayland == rhs.m_uSurface.xwayland && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && + return m_pXDGSurface == rhs.m_pXDGSurface && m_pXWaylandSurface == rhs.m_pXWaylandSurface && m_vPosition == rhs.m_vPosition && m_vSize == rhs.m_vSize && m_bFadingOut == rhs.m_bFadingOut; } @@ -410,7 +396,7 @@ class CWindow { IHyprWindowDecoration* getDecorationByType(eDecorationType); void removeDecorationByType(eDecorationType); void updateToplevel(); - void updateSurfaceScaleTransformDetails(); + void updateSurfaceScaleTransformDetails(bool force = false); void moveToWorkspace(PHLWORKSPACE); PHLWINDOW X11TransientFor(); void onUnmap(); @@ -459,8 +445,11 @@ class CWindow { void onWorkspaceAnimUpdate(); void onUpdateState(); void onUpdateMeta(); + void onX11Configure(CBox box); + void onResourceChangeX11(); std::string fetchTitle(); std::string fetchClass(); + void warpCursor(); // listeners void onAck(uint32_t serial); @@ -478,8 +467,12 @@ class CWindow { CHyprSignalListener unmap; CHyprSignalListener commit; CHyprSignalListener destroy; + CHyprSignalListener activate; + CHyprSignalListener configure; + CHyprSignalListener setGeometry; CHyprSignalListener updateState; CHyprSignalListener updateMetadata; + CHyprSignalListener resourceChange; } listeners; private: diff --git a/src/devices/Tablet.cpp b/src/devices/Tablet.cpp index f5b610a9..b5ab16c1 100644 --- a/src/devices/Tablet.cpp +++ b/src/devices/Tablet.cpp @@ -1,6 +1,7 @@ #include "Tablet.hpp" #include "../defines.hpp" #include "../protocols/Tablet.hpp" +#include "../protocols/core/Compositor.hpp" SP CTablet::create(wlr_tablet* tablet) { SP pTab = SP(new CTablet(tablet)); @@ -295,32 +296,29 @@ CTabletTool::~CTabletTool() { void CTabletTool::disconnectCallbacks() { hyprListener_destroy.removeCallback(); - hyprListener_destroySurface.removeCallback(); + listeners.destroySurface.reset(); } -wlr_surface* CTabletTool::getSurface() { - return pSurface; +SP CTabletTool::getSurface() { + return pSurface.lock(); } -void CTabletTool::setSurface(wlr_surface* surf) { +void CTabletTool::setSurface(SP surf) { if (surf == pSurface) return; if (pSurface) { - hyprListener_destroySurface.removeCallback(); - pSurface = nullptr; + listeners.destroySurface.reset(); + pSurface.reset(); } pSurface = surf; if (surf) { - hyprListener_destroySurface.initCallback( - &surf->events.destroy, - [this](void* owner, void* data) { - PROTO::tablet->proximityOut(self.lock()); - pSurface = nullptr; - hyprListener_destroySurface.removeCallback(); - }, - this, "CTabletTool"); + listeners.destroySurface = surf->events.destroy.registerListener([this](std::any d) { + PROTO::tablet->proximityOut(self.lock()); + pSurface.reset(); + listeners.destroySurface.reset(); + }); } } diff --git a/src/devices/Tablet.hpp b/src/devices/Tablet.hpp index f2444972..1805f3ba 100644 --- a/src/devices/Tablet.hpp +++ b/src/devices/Tablet.hpp @@ -12,6 +12,7 @@ struct wlr_tablet_pad; class CTabletTool; class CTabletPad; +class CWLSurfaceResource; /* A tablet device @@ -197,32 +198,35 @@ class CTabletTool : public IHID { HID_TABLET_TOOL_CAPABILITY_WHEEL = (1 << 5), }; - virtual uint32_t getCapabilities(); - wlr_tablet_tool* wlr(); - virtual eHIDType getType(); - wlr_surface* getSurface(); - void setSurface(wlr_surface*); + virtual uint32_t getCapabilities(); + wlr_tablet_tool* wlr(); + virtual eHIDType getType(); + SP getSurface(); + void setSurface(SP); - WP self; - Vector2D tilt; - bool active = false; // true if in proximity - uint32_t toolCapabilities = 0; + WP self; + Vector2D tilt; + bool active = false; // true if in proximity + uint32_t toolCapabilities = 0; - bool isDown = false; - std::vector buttonsDown; - Vector2D absolutePos; // last known absolute position. + bool isDown = false; + std::vector buttonsDown; + Vector2D absolutePos; // last known absolute position. - std::string hlName; + std::string hlName; private: CTabletTool(wlr_tablet_tool* tool); - void disconnectCallbacks(); + void disconnectCallbacks(); - wlr_surface* pSurface = nullptr; + WP pSurface; - wlr_tablet_tool* tool = nullptr; + wlr_tablet_tool* tool = nullptr; DYNLISTENER(destroy); - DYNLISTENER(destroySurface); + + struct { + CHyprSignalListener destroySurface; + } listeners; }; \ No newline at end of file diff --git a/src/events/Events.hpp b/src/events/Events.hpp index 0b757842..f8eb9d2f 100644 --- a/src/events/Events.hpp +++ b/src/events/Events.hpp @@ -33,8 +33,6 @@ namespace Events { DYNLISTENFUNC(requestMinimize); DYNLISTENFUNC(requestMaximize); DYNLISTENFUNC(setOverrideRedirect); - DYNLISTENFUNC(associateX11); - DYNLISTENFUNC(dissociateX11); DYNLISTENFUNC(ackConfigure); LISTENER(newInput); @@ -56,7 +54,6 @@ namespace Events { DYNLISTENFUNC(monitorBind); // XWayland - LISTENER(readyXWayland); LISTENER(surfaceXWayland); // Renderer destroy diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp index 7152730e..6580d93e 100644 --- a/src/events/Misc.cpp +++ b/src/events/Misc.cpp @@ -25,50 +25,6 @@ void Events::listener_leaseRequest(wl_listener* listener, void* data) { } } -void Events::listener_readyXWayland(wl_listener* listener, void* data) { -#ifndef NO_XWAYLAND - const auto XCBCONNECTION = xcb_connect(g_pXWaylandManager->m_sWLRXWayland->display_name, NULL); - const auto ERR = xcb_connection_has_error(XCBCONNECTION); - if (ERR) { - Debug::log(LogLevel::ERR, "XWayland -> xcb_connection_has_error failed with {}", ERR); - return; - } - - for (auto& ATOM : HYPRATOMS) { - xcb_intern_atom_cookie_t cookie = xcb_intern_atom(XCBCONNECTION, 0, ATOM.first.length(), ATOM.first.c_str()); - xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(XCBCONNECTION, cookie, NULL); - - if (!reply) { - Debug::log(LogLevel::ERR, "XWayland -> Atom failed: {}", ATOM.first); - continue; - } - - ATOM.second = reply->atom; - - free(reply); - } - - //wlr_xwayland_set_seat(g_pXWaylandManager->m_sWLRXWayland, g_pCompositor->m_sSeat.seat); - - g_pCursorManager->setXWaylandCursor(g_pXWaylandManager->m_sWLRXWayland); - - const auto ROOT = xcb_setup_roots_iterator(xcb_get_setup(XCBCONNECTION)).data->root; - auto cookie = xcb_get_property(XCBCONNECTION, 0, ROOT, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_ANY, 0, 2048); - auto reply = xcb_get_property_reply(XCBCONNECTION, cookie, nullptr); - - const auto XWMWINDOW = *(xcb_window_t*)xcb_get_property_value(reply); - const char* name = "Hyprland"; - - xcb_change_property(wlr_xwayland_get_xwm_connection(g_pXWaylandManager->m_sWLRXWayland), XCB_PROP_MODE_REPLACE, XWMWINDOW, HYPRATOMS["_NET_WM_NAME"], HYPRATOMS["UTF8_STRING"], - 8, // format - strlen(name), name); - - free(reply); - - xcb_disconnect(XCBCONNECTION); -#endif -} - void Events::listener_RendererDestroy(wl_listener* listener, void* data) { Debug::log(LOG, "!!Renderer destroyed!!"); } diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 384ea9ed..88b28c87 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -9,6 +9,8 @@ #include "../config/ConfigValue.hpp" #include "../protocols/LayerShell.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../xwayland/XSurface.hpp" // ------------------------------------------------------------ // // __ _______ _ _ _____ ______ _______ // @@ -20,19 +22,6 @@ // // // ------------------------------------------------------------ // -void addViewCoords(void* pWindow, int* x, int* y) { - const auto PWINDOW = (CWindow*)pWindow; - *x += PWINDOW->m_vRealPosition.goal().x; - *y += PWINDOW->m_vRealPosition.goal().y; - - if (!PWINDOW->m_bIsX11 && PWINDOW->m_bIsMapped) { - Vector2D pos = PWINDOW->m_pXDGSurface->current.geometry.pos(); - - *x -= pos.x; - *y -= pos.y; - } -} - void setAnimToMove(void* data) { auto* const PANIMCFG = g_pConfigManager->getAnimationPropertyConfig("windowsMove"); @@ -68,7 +57,6 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_bReadyToDelete = false; PWINDOW->m_bFadingOut = false; PWINDOW->m_szTitle = PWINDOW->fetchTitle(); - PWINDOW->m_iX11Type = PWINDOW->m_bIsX11 ? (PWINDOW->m_uSurface.xwayland->override_redirect ? 2 : 1) : 1; PWINDOW->m_bFirstMap = true; PWINDOW->m_szInitialTitle = PWINDOW->m_szTitle; PWINDOW->m_szInitialClass = PWINDOW->fetchClass(); @@ -117,7 +105,7 @@ void Events::listener_mapWindow(void* owner, void* data) { // registers the animated vars and stuff PWINDOW->onMap(); - const auto PWINDOWSURFACE = PWINDOW->m_pWLSurface.wlr(); + const auto PWINDOWSURFACE = PWINDOW->m_pWLSurface->resource(); if (!PWINDOWSURFACE) { g_pCompositor->removeWindowFromVectorSafe(PWINDOW); @@ -129,8 +117,7 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_bRequestsFloat = true; } - PWINDOW->m_bX11ShouldntFocus = - PWINDOW->m_bX11ShouldntFocus || (PWINDOW->m_bIsX11 && PWINDOW->m_iX11Type == 2 && !wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland)); + PWINDOW->m_bX11ShouldntFocus = PWINDOW->m_bX11ShouldntFocus || (PWINDOW->m_bIsX11 && PWINDOW->m_iX11Type == 2 && !PWINDOW->m_pXWaylandSurface->wantsFocus()); if (PWORKSPACE->m_bDefaultFloating) PWINDOW->m_bIsFloating = true; @@ -144,7 +131,7 @@ void Events::listener_mapWindow(void* owner, void* data) { // window rules PWINDOW->m_vMatchedRules = g_pConfigManager->getMatchingRules(PWINDOW, false); - bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen); + bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->fullscreen); bool requestsFakeFullscreen = false; bool requestsMaximize = false; bool overridingNoFullscreen = false; @@ -477,24 +464,23 @@ void Events::listener_mapWindow(void* owner, void* data) { // check LS focus grab const auto PFORCEFOCUS = g_pCompositor->getForceFocus(); - const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus); + const auto PLSFROMFOCUS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock()); if (PLSFROMFOCUS && PLSFROMFOCUS->layerSurface->current.interactivity != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) PWINDOW->m_bNoInitialFocus = true; - if (PWORKSPACE->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) { + if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow && !requestsFullscreen && !PWINDOW->m_bIsFloating) { if (*PNEWTAKESOVERFS == 0) PWINDOW->m_bNoInitialFocus = true; else if (*PNEWTAKESOVERFS == 2) - g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID), false, FULLSCREEN_INVALID); - else if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) + g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID), false, FULLSCREEN_INVALID); + else if (PWINDOW->m_pWorkspace->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) requestsMaximize = true; else requestsFullscreen = true; } if (!PWINDOW->m_sAdditionalConfigData.noFocus && !PWINDOW->m_bNoInitialFocus && - (PWINDOW->m_iX11Type != 2 || - (PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->window_type_len > 0 && wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland))) && - !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) && !g_pInputManager->isConstrained()) { + (PWINDOW->m_iX11Type != 2 || (PWINDOW->m_bIsX11 && PWINDOW->m_pXWaylandSurface->wantsFocus())) && !workspaceSilent && (!PFORCEFOCUS || PFORCEFOCUS == PWINDOW) && + !g_pInputManager->isConstrained()) { g_pCompositor->focusWindow(PWINDOW); PWINDOW->m_fActiveInactiveAlpha.setValueAndWarp(*PACTIVEALPHA); PWINDOW->m_fDimPercent.setValueAndWarp(PWINDOW->m_sAdditionalConfigData.forceNoDim ? 0.f : *PDIMSTRENGTH); @@ -503,27 +489,11 @@ void Events::listener_mapWindow(void* owner, void* data) { PWINDOW->m_fDimPercent.setValueAndWarp(0); } - if (PWINDOW->m_bIsX11) { - PWINDOW->hyprListener_fullscreenWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_fullscreen, &Events::listener_fullscreenWindow, PWINDOW.get(), - "XWayland Window Late"); - PWINDOW->hyprListener_activateX11.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_activate, &Events::listener_activateX11, PWINDOW.get(), - "XWayland Window Late"); - PWINDOW->hyprListener_setTitleWindow.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_title, &Events::listener_setTitleWindow, PWINDOW.get(), "XWayland Window Late"); - PWINDOW->hyprListener_requestMinimize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_minimize, &Events::listener_requestMinimize, PWINDOW.get(), - "Xwayland Window Late"); - PWINDOW->hyprListener_requestMaximize.initCallback(&PWINDOW->m_uSurface.xwayland->events.request_maximize, &Events::listener_requestMaximize, PWINDOW.get(), - "Xwayland Window Late"); - - if (PWINDOW->m_iX11Type == 2) - PWINDOW->hyprListener_setGeometryX11U.initCallback(&PWINDOW->m_uSurface.xwayland->events.set_geometry, &Events::listener_unmanagedSetGeometry, PWINDOW.get(), - "XWayland Window Late"); - } - if ((requestsFullscreen && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN) || overridingNoFullscreen)) || (requestsMaximize && (!(PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) || overridingNoMaximize)) || requestsFakeFullscreen) { // fix fullscreen on requested (basically do a switcheroo) - if (PWORKSPACE->m_bHasFullscreenWindow) { - const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID); + if (PWINDOW->m_pWorkspace->m_bHasFullscreenWindow) { + const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWINDOW->m_pWorkspace->m_iID); g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, FULLSCREEN_FULL); } @@ -649,8 +619,8 @@ void Events::listener_mapWindow(void* owner, void* data) { if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFullscreen && !PWINDOW->m_bIsFloating) PWINDOW->m_fAlpha.setValueAndWarp(0.f); - g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale); - g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->transform); + g_pCompositor->setPreferredScaleForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->scale); + g_pCompositor->setPreferredTransformForSurface(PWINDOW->m_pWLSurface->resource(), PMONITOR->transform); if (g_pSeatManager->mouse.expired() || !g_pInputManager->isConstrained()) g_pInputManager->sendMotionEventsToFocused(); @@ -669,7 +639,7 @@ void Events::listener_unmapWindow(void* owner, void* data) { Debug::log(LOG, "{:c} unmapped", PWINDOW); - if (!PWINDOW->m_pWLSurface.exists() || !PWINDOW->m_bIsMapped) { + if (!PWINDOW->m_pWLSurface->exists() || !PWINDOW->m_bIsMapped) { Debug::log(WARN, "{} unmapped without being mapped??", PWINDOW); PWINDOW->m_bFadingOut = false; return; @@ -687,16 +657,6 @@ void Events::listener_unmapWindow(void* owner, void* data) { g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW); - if (!PWINDOW->m_bIsX11) { - Debug::log(LOG, "Unregistered late callbacks XWL"); - PWINDOW->hyprListener_fullscreenWindow.removeCallback(); - PWINDOW->hyprListener_activateX11.removeCallback(); - PWINDOW->hyprListener_setTitleWindow.removeCallback(); - PWINDOW->hyprListener_setGeometryX11U.removeCallback(); - PWINDOW->hyprListener_requestMaximize.removeCallback(); - PWINDOW->hyprListener_requestMinimize.removeCallback(); - } - if (PWINDOW->m_bIsFullscreen) g_pCompositor->setWindowFullscreen(PWINDOW, false, FULLSCREEN_FULL); @@ -715,7 +675,7 @@ void Events::listener_unmapWindow(void* owner, void* data) { if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { wasLastWindow = true; g_pCompositor->m_pLastWindow.reset(); - g_pCompositor->m_pLastFocus = nullptr; + g_pCompositor->m_pLastFocus.reset(); g_pInputManager->releaseAllMouseButtons(); } @@ -829,7 +789,7 @@ void Events::listener_commitWindow(void* owner, void* data) { if (!PWINDOW->m_pWorkspace->m_bVisible) return; - g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y, + g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface->resource(), PWINDOW->m_vRealPosition.goal().x, PWINDOW->m_vRealPosition.goal().y, PWINDOW->m_bIsX11 ? 1.0 / PWINDOW->m_fX11SurfaceScaledBy : 1.0); if (!PWINDOW->m_bIsX11) { @@ -839,9 +799,8 @@ void Events::listener_commitWindow(void* owner, void* data) { // tearing: if solitary, redraw it. This still might be a single surface window const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); - if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && - PWINDOW->m_pWLSurface.wlr()->current.committed & WLR_SURFACE_STATE_BUFFER) { - CRegion damageBox{&PWINDOW->m_pWLSurface.wlr()->buffer_damage}; + if (PMONITOR && PMONITOR->solitaryClient.lock() == PWINDOW && PWINDOW->canBeTorn() && PMONITOR->tearingState.canTear && PWINDOW->m_pWLSurface->resource()->current.buffer) { + CRegion damageBox{PWINDOW->m_pWLSurface->resource()->current.bufferDamage}; if (!damageBox.empty()) { if (PMONITOR->tearingState.busy) { @@ -859,24 +818,12 @@ void Events::listener_destroyWindow(void* owner, void* data) { Debug::log(LOG, "{:c} destroyed, queueing.", PWINDOW); - if (PWINDOW->m_bIsX11) - Debug::log(LOG, "XWayland class raw: {}", PWINDOW->m_uSurface.xwayland->_class ? PWINDOW->m_uSurface.xwayland->_class : "null"); - if (PWINDOW == g_pCompositor->m_pLastWindow.lock()) { g_pCompositor->m_pLastWindow.reset(); - g_pCompositor->m_pLastFocus = nullptr; + g_pCompositor->m_pLastFocus.reset(); } - PWINDOW->m_pWLSurface.unassign(); - - PWINDOW->hyprListener_commitWindow.removeCallback(); - PWINDOW->hyprListener_mapWindow.removeCallback(); - PWINDOW->hyprListener_unmapWindow.removeCallback(); - PWINDOW->hyprListener_destroyWindow.removeCallback(); - PWINDOW->hyprListener_configureX11.removeCallback(); - PWINDOW->hyprListener_setOverrideRedirect.removeCallback(); - PWINDOW->hyprListener_associateX11.removeCallback(); - PWINDOW->hyprListener_dissociateX11.removeCallback(); + PWINDOW->m_pWLSurface->unassign(); PWINDOW->listeners = {}; @@ -901,39 +848,6 @@ void Events::listener_setTitleWindow(void* owner, void* data) { PWINDOW->onUpdateMeta(); } -void Events::listener_fullscreenWindow(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - // x11 only - - if (!PWINDOW->m_bIsMapped) { - PWINDOW->m_bWantsInitialFullscreen = true; - return; - } - - if (PWINDOW->isHidden() || (PWINDOW->m_eSuppressedEvents & SUPPRESS_FULLSCREEN)) - return; - - bool requestedFullState = false; - - if (!PWINDOW->m_uSurface.xwayland->surface->mapped) - return; - - if (!PWINDOW->m_bFakeFullscreenState) - g_pCompositor->setWindowFullscreen(PWINDOW, PWINDOW->m_uSurface.xwayland->fullscreen, FULLSCREEN_FULL); - - requestedFullState = PWINDOW->m_uSurface.xwayland->fullscreen; - - if (!requestedFullState && PWINDOW->m_bFakeFullscreenState) { - g_pXWaylandManager->setWindowFullscreen(PWINDOW, false); // fixes for apps expecting a de-fullscreen (e.g. ff) - g_pXWaylandManager->setWindowFullscreen(PWINDOW, true); - } - - PWINDOW->updateToplevel(); - - Debug::log(LOG, "{} fullscreen to {}", PWINDOW, PWINDOW->m_bIsFullscreen); -} - void Events::listener_activateX11(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); @@ -946,7 +860,7 @@ void Events::listener_activateX11(void* owner, void* data) { if (g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->getPID() != PWINDOW->getPID()) return; - if (!wlr_xwayland_or_surface_wants_focus(PWINDOW->m_uSurface.xwayland)) + if (!PWINDOW->m_pXWaylandSurface->wantsFocus()) return; g_pCompositor->focusWindow(PWINDOW); @@ -959,82 +873,16 @@ void Events::listener_activateX11(void* owner, void* data) { PWINDOW->activate(); } -void Events::listener_configureX11(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - const auto E = (wlr_xwayland_surface_configure_event*)data; - - if (!PWINDOW->m_uSurface.xwayland->surface || !PWINDOW->m_uSurface.xwayland->surface->mapped || !PWINDOW->m_bIsMapped) { - wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height); - PWINDOW->m_vPendingReportedSize = {E->width, E->height}; - PWINDOW->m_vReportedSize = {E->width, E->height}; - if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) - PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale; - return; - } - - g_pHyprRenderer->damageWindow(PWINDOW); - - if (!PWINDOW->m_bIsFloating || PWINDOW->m_bIsFullscreen || g_pInputManager->currentlyDraggedWindow.lock() == PWINDOW) { - g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goal(), true); - g_pInputManager->refocus(); - g_pHyprRenderer->damageWindow(PWINDOW); - return; - } - - if (E->width > 1 && E->height > 1) - PWINDOW->setHidden(false); - else - PWINDOW->setHidden(true); - - const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords({E->x, E->y}); - - PWINDOW->m_vRealPosition.setValueAndWarp(LOGICALPOS); - PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(E->width, E->height)); - - static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - if (*PXWLFORCESCALEZERO) { - if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) { - PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_vRealSize.goal() / PMONITOR->scale); - PWINDOW->m_fX11SurfaceScaledBy = PMONITOR->scale; - } - } - - PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.value(); - PWINDOW->m_vSize = PWINDOW->m_vRealSize.value(); - - wlr_xwayland_surface_configure(PWINDOW->m_uSurface.xwayland, E->x, E->y, E->width, E->height); - - PWINDOW->m_vPendingReportedSize = {E->width, E->height}; - PWINDOW->m_vReportedSize = {E->width, E->height}; - - PWINDOW->updateWindowDecos(); - - if (!g_pCompositor->isWorkspaceVisible(PWINDOW->m_pWorkspace)) - return; // further things are only for visible windows - - PWINDOW->m_pWorkspace = g_pCompositor->getMonitorFromVector(PWINDOW->m_vRealPosition.value() + PWINDOW->m_vRealSize.value() / 2.f)->activeWorkspace; - - g_pCompositor->changeWindowZOrder(PWINDOW, true); - - PWINDOW->m_bCreatedOverFullscreen = true; - - if (!PWINDOW->m_sAdditionalConfigData.windowDanceCompat) - g_pInputManager->refocus(); - - g_pHyprRenderer->damageWindow(PWINDOW); -} - void Events::listener_unmanagedSetGeometry(void* owner, void* data) { PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - if (!PWINDOW->m_bIsMapped) + if (!PWINDOW->m_bIsMapped || !PWINDOW->m_pXWaylandSurface || !PWINDOW->m_pXWaylandSurface->overrideRedirect) return; const auto POS = PWINDOW->m_vRealPosition.goal(); const auto SIZ = PWINDOW->m_vRealSize.goal(); - if (PWINDOW->m_uSurface.xwayland->width > 1 && PWINDOW->m_uSurface.xwayland->height > 1) + if (PWINDOW->m_pXWaylandSurface->geometry.size() > Vector2D{1, 1}) PWINDOW->setHidden(false); else PWINDOW->setHidden(true); @@ -1047,18 +895,17 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { static auto PXWLFORCESCALEZERO = CConfigValue("xwayland:force_zero_scaling"); - const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords({PWINDOW->m_uSurface.xwayland->x, PWINDOW->m_uSurface.xwayland->y}); + const auto LOGICALPOS = g_pXWaylandManager->xwaylandToWaylandCoords(PWINDOW->m_pXWaylandSurface->geometry.pos()); - if (abs(std::floor(POS.x) - LOGICALPOS.x) > 2 || abs(std::floor(POS.y) - LOGICALPOS.y) > 2 || abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 || - abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2) { - Debug::log(LOG, "Unmanaged window {} requests geometry update to {:j} {} {}", PWINDOW, LOGICALPOS, (int)PWINDOW->m_uSurface.xwayland->width, - (int)PWINDOW->m_uSurface.xwayland->height); + if (abs(std::floor(POS.x) - LOGICALPOS.x) > 2 || abs(std::floor(POS.y) - LOGICALPOS.y) > 2 || abs(std::floor(SIZ.x) - PWINDOW->m_pXWaylandSurface->geometry.width) > 2 || + abs(std::floor(SIZ.y) - PWINDOW->m_pXWaylandSurface->geometry.height) > 2) { + Debug::log(LOG, "Unmanaged window {} requests geometry update to {:j} {:j}", PWINDOW, LOGICALPOS, PWINDOW->m_pXWaylandSurface->geometry.size()); g_pHyprRenderer->damageWindow(PWINDOW); PWINDOW->m_vRealPosition.setValueAndWarp(Vector2D(LOGICALPOS.x, LOGICALPOS.y)); - if (abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2) - PWINDOW->m_vRealSize.setValueAndWarp(Vector2D(PWINDOW->m_uSurface.xwayland->width, PWINDOW->m_uSurface.xwayland->height)); + if (abs(std::floor(SIZ.x) - PWINDOW->m_pXWaylandSurface->geometry.w) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_pXWaylandSurface->geometry.h) > 2) + PWINDOW->m_vRealSize.setValueAndWarp(PWINDOW->m_pXWaylandSurface->geometry.size()); if (*PXWLFORCESCALEZERO) { if (const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID); PMONITOR) { @@ -1079,92 +926,3 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) { PWINDOW->m_vPendingReportedSize = PWINDOW->m_vRealSize.goal(); } } - -void Events::listener_setOverrideRedirect(void* owner, void* data) { - // const auto PWINDOW = (CWindow*)owner; - - //if (!PWINDOW->m_bIsMapped && PWINDOW->m_uSurface.xwayland->mapped) { - // Events::listener_mapWindow(PWINDOW, nullptr); - //} -} - -void Events::listener_associateX11(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - PWINDOW->hyprListener_mapWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.map, &Events::listener_mapWindow, PWINDOW.get(), "XWayland Window"); - PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xwayland->surface->events.commit, &Events::listener_commitWindow, PWINDOW.get(), "XWayland Window"); - - PWINDOW->m_pWLSurface.assign(PWINDOW->m_uSurface.xwayland->surface, PWINDOW); -} - -void Events::listener_dissociateX11(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - PWINDOW->m_pWLSurface.unassign(); - - PWINDOW->hyprListener_mapWindow.removeCallback(); - PWINDOW->hyprListener_commitWindow.removeCallback(); -} - -void Events::listener_surfaceXWayland(wl_listener* listener, void* data) { - const auto XWSURFACE = (wlr_xwayland_surface*)data; - - Debug::log(LOG, "New XWayland Surface created (class {}).", XWSURFACE->_class ? XWSURFACE->_class : "null"); - if (XWSURFACE->parent) - Debug::log(LOG, "Window parent data: {} at {:x}", XWSURFACE->parent->_class ? XWSURFACE->parent->_class : "null", (uintptr_t)XWSURFACE->parent); - - const auto PNEWWINDOW = g_pCompositor->m_vWindows.emplace_back(CWindow::create()); - - PNEWWINDOW->m_uSurface.xwayland = XWSURFACE; - PNEWWINDOW->m_iX11Type = XWSURFACE->override_redirect ? 2 : 1; - PNEWWINDOW->m_bIsX11 = true; - - PNEWWINDOW->m_pX11Parent = g_pCompositor->getX11Parent(PNEWWINDOW); - - PNEWWINDOW->hyprListener_associateX11.initCallback(&XWSURFACE->events.associate, &Events::listener_associateX11, PNEWWINDOW.get(), "XWayland Window"); - PNEWWINDOW->hyprListener_dissociateX11.initCallback(&XWSURFACE->events.dissociate, &Events::listener_dissociateX11, PNEWWINDOW.get(), "XWayland Window"); - PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XWSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW.get(), "XWayland Window"); - PNEWWINDOW->hyprListener_setOverrideRedirect.initCallback(&XWSURFACE->events.set_override_redirect, &Events::listener_setOverrideRedirect, PNEWWINDOW.get(), "XWayland Window"); - PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW.get(), "XWayland Window"); -} - -void Events::listener_requestMaximize(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - if (PWINDOW->m_eSuppressedEvents & SUPPRESS_MAXIMIZE) - return; - - Debug::log(LOG, "Maximize request for {}", PWINDOW); - if (!PWINDOW->m_bIsX11) { - - g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, - FULLSCREEN_MAXIMIZED); // this will be rejected if there already is a fullscreen window - - } else { - if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1) - return; - - g_pCompositor->setWindowFullscreen(PWINDOW, !PWINDOW->m_bIsFullscreen, FULLSCREEN_MAXIMIZED); - } -} - -void Events::listener_requestMinimize(void* owner, void* data) { - PHLWINDOW PWINDOW = ((CWindow*)owner)->m_pSelf.lock(); - - Debug::log(LOG, "Minimize request for {}", PWINDOW); - - if (PWINDOW->m_bIsX11) { - if (!PWINDOW->m_bIsMapped || PWINDOW->m_iX11Type != 1) - return; - - const auto E = (wlr_xwayland_minimize_event*)data; - - g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW.get(), (int)E->minimize)}); - EMIT_HOOK_EVENT("minimize", (std::vector{PWINDOW, E->minimize})); - - wlr_xwayland_surface_set_minimized(PWINDOW->m_uSurface.xwayland, E->minimize && g_pCompositor->m_pLastWindow.lock() != PWINDOW); // fucking DXVK - } else { - g_pEventManager->postEvent({"minimize", std::format("{:x},{}", (uintptr_t)PWINDOW.get(), 1)}); - EMIT_HOOK_EVENT("minimize", (std::vector{PWINDOW, (int64_t)(1)})); - } -} diff --git a/src/helpers/Format.cpp b/src/helpers/Format.cpp new file mode 100644 index 00000000..343440b4 --- /dev/null +++ b/src/helpers/Format.cpp @@ -0,0 +1,272 @@ +#include "Format.hpp" +#include +#include "../includes.hpp" +#include "debug/Log.hpp" + +/* + DRM formats are LE, while OGL is BE. The two primary formats + will be flipped, so we will set flipRB which will later use swizzle + to flip the red and blue channels. + This will not work on GLES2, but I want to drop support for it one day anyways. +*/ +inline const std::vector GLES3_FORMATS = { + { + .drmFormat = DRM_FORMAT_ARGB8888, + .flipRB = true, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XRGB8888, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_XRGB8888, + .flipRB = true, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XRGB8888, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_XBGR8888, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR8888, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_ABGR8888, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR8888, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_BGR888, + .glFormat = GL_RGB, + .glType = GL_UNSIGNED_BYTE, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_BGR888, + .bytesPerBlock = 3, + }, + { + .drmFormat = DRM_FORMAT_RGBX4444, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_4_4_4_4, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_RGBX4444, + .bytesPerBlock = 2, + }, + { + .drmFormat = DRM_FORMAT_RGBA4444, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_4_4_4_4, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_RGBX4444, + .bytesPerBlock = 2, + }, + { + .drmFormat = DRM_FORMAT_RGBX5551, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_5_5_5_1, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_RGBX5551, + .bytesPerBlock = 2, + }, + { + .drmFormat = DRM_FORMAT_RGBA5551, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT_5_5_5_1, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_RGBX5551, + .bytesPerBlock = 2, + }, + { + .drmFormat = DRM_FORMAT_RGB565, + .glFormat = GL_RGB, + .glType = GL_UNSIGNED_SHORT_5_6_5, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_RGB565, + .bytesPerBlock = 2, + }, + { + .drmFormat = DRM_FORMAT_XBGR2101010, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR2101010, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_ABGR2101010, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR2101010, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_XRGB2101010, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XRGB2101010, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_ARGB2101010, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_INT_2_10_10_10_REV, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XRGB2101010, + .bytesPerBlock = 4, + }, + { + .drmFormat = DRM_FORMAT_XBGR16161616F, + .glFormat = GL_RGBA, + .glType = GL_HALF_FLOAT, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR16161616F, + .bytesPerBlock = 8, + }, + { + .drmFormat = DRM_FORMAT_ABGR16161616F, + .glFormat = GL_RGBA, + .glType = GL_HALF_FLOAT, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR16161616F, + .bytesPerBlock = 8, + }, + { + .drmFormat = DRM_FORMAT_XBGR16161616, + .glInternalFormat = GL_RGBA16UI, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT, + .withAlpha = false, + .alphaStripped = DRM_FORMAT_XBGR16161616, + .bytesPerBlock = 8, + }, + { + .drmFormat = DRM_FORMAT_ABGR16161616, + .glInternalFormat = GL_RGBA16UI, + .glFormat = GL_RGBA, + .glType = GL_UNSIGNED_SHORT, + .withAlpha = true, + .alphaStripped = DRM_FORMAT_XBGR16161616, + .bytesPerBlock = 8, + }, + { + .drmFormat = DRM_FORMAT_YVYU, + .bytesPerBlock = 4, + .blockSize = {2, 1}, + }, + { + .drmFormat = DRM_FORMAT_VYUY, + .bytesPerBlock = 4, + .blockSize = {2, 1}, + }, + { + .drmFormat = DRM_FORMAT_R8, + .bytesPerBlock = 1, + }, + { + .drmFormat = DRM_FORMAT_GR88, + .bytesPerBlock = 2, + }, + { + .drmFormat = DRM_FORMAT_RGB888, + .bytesPerBlock = 3, + }, + { + .drmFormat = DRM_FORMAT_BGR888, + .bytesPerBlock = 3, + }, + { + .drmFormat = DRM_FORMAT_RGBX4444, + .bytesPerBlock = 2, + }, +}; + +SHMFormat FormatUtils::drmToShm(DRMFormat drm) { + switch (drm) { + case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888; + case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888; + default: return drm; + } + + return drm; +} + +DRMFormat FormatUtils::shmToDRM(SHMFormat shm) { + switch (shm) { + case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888; + case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888; + default: return shm; + } + + return shm; +} + +const SPixelFormat* FormatUtils::getPixelFormatFromDRM(DRMFormat drm) { + for (auto& fmt : GLES3_FORMATS) { + if (fmt.drmFormat == drm) + return &fmt; + } + + return nullptr; +} + +const SPixelFormat* FormatUtils::getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) { + for (auto& fmt : GLES3_FORMATS) { + if (fmt.glFormat == (int)glFormat && fmt.glType == (int)glType && fmt.withAlpha == alpha) + return &fmt; + } + + return nullptr; +} + +bool FormatUtils::isFormatOpaque(DRMFormat drm) { + const auto FMT = FormatUtils::getPixelFormatFromDRM(drm); + if (!FMT) + return false; + + return !FMT->withAlpha; +} + +int FormatUtils::pixelsPerBlock(const SPixelFormat* const fmt) { + return fmt->blockSize.x * fmt->blockSize.y > 0 ? fmt->blockSize.x * fmt->blockSize.y : 1; +} + +int FormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) { + return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt)); +} + +uint32_t FormatUtils::drmFormatToGL(DRMFormat drm) { + switch (drm) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case. + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: +#ifdef GLES2 + return GL_RGB10_A2_EXT; +#else + return GL_RGB10_A2; +#endif + default: return GL_RGBA; + } + UNREACHABLE(); + return GL_RGBA; +} + +uint32_t FormatUtils::glFormatToType(uint32_t gl) { + return gl != GL_RGBA ? +#ifdef GLES2 + GL_UNSIGNED_INT_2_10_10_10_REV_EXT : +#else + GL_UNSIGNED_INT_2_10_10_10_REV : +#endif + GL_UNSIGNED_BYTE; +} diff --git a/src/helpers/Format.hpp b/src/helpers/Format.hpp new file mode 100644 index 00000000..b86f44dd --- /dev/null +++ b/src/helpers/Format.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include "Vector2D.hpp" + +typedef uint32_t DRMFormat; +typedef uint32_t SHMFormat; + +struct SPixelFormat { + DRMFormat drmFormat = 0; /* DRM_FORMAT_INVALID */ + bool flipRB = false; + int glInternalFormat = 0; + int glFormat = 0; + int glType = 0; + bool withAlpha = true; + DRMFormat alphaStripped = 0; /* DRM_FORMAT_INVALID */ + uint32_t bytesPerBlock = 0; + Vector2D blockSize; +}; + +struct SDRMFormat { + uint32_t format = 0; + std::vector mods; +}; + +namespace FormatUtils { + SHMFormat drmToShm(DRMFormat drm); + DRMFormat shmToDRM(SHMFormat shm); + + const SPixelFormat* getPixelFormatFromDRM(DRMFormat drm); + const SPixelFormat* getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha); + bool isFormatOpaque(DRMFormat drm); + int pixelsPerBlock(const SPixelFormat* const fmt); + int minStride(const SPixelFormat* const fmt, int32_t width); + uint32_t drmFormatToGL(DRMFormat drm); + uint32_t glFormatToType(uint32_t gl); +}; diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 7382d24c..32cd9868 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -649,6 +649,8 @@ void logSystemInfo() { #if defined(__DragonFly__) || defined(__FreeBSD__) const std::string GPUINFO = execAndGet("pciconf -lv | fgrep -A4 vga"); +#elif defined(__arm__) || defined(__aarch64__) + const std::string GPUINFO = execAndGet("cat /proc/device-tree/soc*/gpu*/compatible"); #else const std::string GPUINFO = execAndGet("lspci -vnn | grep VGA"); #endif @@ -855,33 +857,6 @@ void throwError(const std::string& err) { throw std::runtime_error(err); } -uint32_t drmFormatToGL(uint32_t drm) { - switch (drm) { - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case. - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: -#ifdef GLES2 - return GL_RGB10_A2_EXT; -#else - return GL_RGB10_A2; -#endif - default: return GL_RGBA; - } - UNREACHABLE(); - return GL_RGBA; -} - -uint32_t glFormatToType(uint32_t gl) { - return gl != GL_RGBA ? -#ifdef GLES2 - GL_UNSIGNED_INT_2_10_10_10_REV_EXT : -#else - GL_UNSIGNED_INT_2_10_10_10_REV : -#endif - GL_UNSIGNED_BYTE; -} - bool envEnabled(const std::string& env) { const auto ENV = getenv(env.c_str()); if (!ENV) @@ -920,3 +895,42 @@ int allocateSHMFile(size_t len) { return fd; } + +bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr) { + auto [fd, name] = openExclusiveShm(); + if (fd < 0) { + return false; + } + + // CLOEXEC is guaranteed to be set by shm_open + int ro_fd = shm_open(name.c_str(), O_RDONLY, 0); + if (ro_fd < 0) { + shm_unlink(name.c_str()); + close(fd); + return false; + } + + shm_unlink(name.c_str()); + + // Make sure the file cannot be re-opened in read-write mode (e.g. via + // "/proc/self/fd/" on Linux) + if (fchmod(fd, 0) != 0) { + close(fd); + close(ro_fd); + return false; + } + + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + close(ro_fd); + return false; + } + + *rw_fd_ptr = fd; + *ro_fd_ptr = ro_fd; + return true; +} diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index 80103eac..9d34174c 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -35,10 +35,9 @@ double normalizeAngleRad(double ang); std::string replaceInString(std::string subject, const std::string& search, const std::string& replace); std::vector getBacktrace(); void throwError(const std::string& err); -uint32_t drmFormatToGL(uint32_t drm); -uint32_t glFormatToType(uint32_t gl); bool envEnabled(const std::string& env); int allocateSHMFile(size_t len); +bool allocateSHMFilePair(size_t size, int* rw_fd_ptr, int* ro_fd_ptr); template [[deprecated("use std::format instead")]] std::string getFormat(std::format_string fmt, Args&&... args) { diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index d13acf0c..a30f1057 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -184,6 +184,9 @@ void CMonitor::onConnect(bool noRule) { forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering. // + if (!activeMonitorRule.mirrorOf.empty()) + setMirror(activeMonitorRule.mirrorOf); + g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName}); g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)}); EMIT_HOOK_EVENT("monitorAdded", this); @@ -216,7 +219,6 @@ void CMonitor::onConnect(bool noRule) { PROTO::gamma->applyGammaToState(this); events.connect.emit(); - updateGlobal(); } void CMonitor::onDisconnect(bool destroy) { @@ -284,8 +286,6 @@ void CMonitor::onDisconnect(bool destroy) { m_bEnabled = false; m_bRenderingInitPassed = false; - updateGlobal(); - if (BACKUPMON) { // snap cursor g_pCompositor->warpCursorTo(BACKUPMON->vecPosition + BACKUPMON->vecTransformedSize / 2.F, true); @@ -304,7 +304,7 @@ void CMonitor::onDisconnect(bool destroy) { w->startAnim(true, true, true); } } else { - g_pCompositor->m_pLastFocus = nullptr; + g_pCompositor->m_pLastFocus.reset(); g_pCompositor->m_pLastWindow.reset(); g_pCompositor->m_pLastMonitor.reset(); } @@ -520,6 +520,8 @@ void CMonitor::setMirror(const std::string& mirrorOf) { g_pCompositor->sanityCheckWorkspaces(); } + + events.modeChanged.emit(); } float CMonitor::getDefaultScale() { @@ -750,13 +752,6 @@ CBox CMonitor::logicalBox() { return {vecPosition, vecSize}; } -void CMonitor::updateGlobal() { - if (output->width > 0 && output->height > 0 && m_bEnabled) - wlr_output_create_global(output, g_pCompositor->m_sWLDisplay); - else - wlr_output_destroy_global(output); -} - CMonitorState::CMonitorState(CMonitor* owner) { m_pOwner = owner; wlr_output_state_init(&m_state); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 7aa07a86..4bfbf53c 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -13,7 +13,8 @@ #include "signal/Signal.hpp" // Enum for the different types of auto directions, e.g. auto-left, auto-up. -enum class eAutoDirs { +enum eAutoDirs { + DIR_AUTO_NONE = 0, /* None will be treated as right. */ DIR_AUTO_UP, DIR_AUTO_DOWN, DIR_AUTO_LEFT, @@ -21,7 +22,7 @@ enum class eAutoDirs { }; struct SMonitorRule { - eAutoDirs autoDir; + eAutoDirs autoDir = DIR_AUTO_NONE; std::string name = ""; Vector2D resolution = Vector2D(1280, 720); Vector2D offset = Vector2D(0, 0); @@ -171,7 +172,6 @@ class CMonitor { int64_t activeWorkspaceID(); int64_t activeSpecialWorkspaceID(); CBox logicalBox(); - void updateGlobal(); bool m_bEnabled = false; bool m_bRenderingInitPassed = false; diff --git a/src/helpers/Region.cpp b/src/helpers/Region.cpp index 20eaf452..9e572b34 100644 --- a/src/helpers/Region.cpp +++ b/src/helpers/Region.cpp @@ -4,6 +4,8 @@ extern "C" { #include } +constexpr const int64_t MAX_REGION_SIDE = 10000000; + CRegion::CRegion() { pixman_region32_init(&m_rRegion); } @@ -103,6 +105,11 @@ CRegion& CRegion::transform(const wl_output_transform t, double w, double h) { return *this; } +CRegion& CRegion::rationalize() { + intersect(CBox{-MAX_REGION_SIDE, -MAX_REGION_SIDE, MAX_REGION_SIDE * 2, MAX_REGION_SIDE * 2}); + return *this; +} + CRegion CRegion::copy() const { return CRegion(*this); } diff --git a/src/helpers/Region.hpp b/src/helpers/Region.hpp index 27f460f4..42693c21 100644 --- a/src/helpers/Region.hpp +++ b/src/helpers/Region.hpp @@ -50,6 +50,7 @@ class CRegion { CRegion& invert(const CBox& box); CRegion& scale(float scale); CRegion& scale(const Vector2D& scale); + CRegion& rationalize(); CBox getExtents(); bool containsPoint(const Vector2D& vec) const; bool empty() const; diff --git a/src/helpers/SdDaemon.cpp b/src/helpers/SdDaemon.cpp index 497101e4..25e0ca3b 100644 --- a/src/helpers/SdDaemon.cpp +++ b/src/helpers/SdDaemon.cpp @@ -21,8 +21,8 @@ namespace Systemd { } int SdNotify(int unsetEnvironment, const char* state) { - int fd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (fd == -1) + int fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) return -errno; constexpr char envVar[] = "NOTIFY_SOCKET"; @@ -47,12 +47,12 @@ namespace Systemd { if (unixAddr.sun_path[0] == '@') unixAddr.sun_path[0] = '\0'; - if (!connect(fd, (const sockaddr*)&unixAddr, sizeof(struct sockaddr_un))) - return 1; + if (connect(fd, (const sockaddr*)&unixAddr, sizeof(struct sockaddr_un)) < 0) + return -errno; // arbitrary value which seems to be enough for s-d messages - size_t stateLen = strnlen(state, 128); - if (write(fd, state, stateLen) >= 0) + ssize_t stateLen = strnlen(state, 128); + if (write(fd, state, stateLen) == stateLen) return 1; return -errno; diff --git a/src/helpers/TagKeeper.cpp b/src/helpers/TagKeeper.cpp new file mode 100644 index 00000000..f960accb --- /dev/null +++ b/src/helpers/TagKeeper.cpp @@ -0,0 +1,40 @@ +#include "TagKeeper.hpp" + +bool CTagKeeper::isTagged(const std::string& tag, bool strict) { + return m_tags.contains(tag) || (!strict && m_tags.contains(tag + "*")); +} + +bool CTagKeeper::applyTag(const std::string& tag, bool dynamic) { + + std::string tagReal = tag; + + if (dynamic && !tag.ends_with("*")) + tagReal += "*"; + + bool changed = true; + bool setTag = true; + + if (tagReal.starts_with("-")) { // unset + tagReal = tagReal.substr(1); + changed = isTagged(tagReal, true); + setTag = false; + } else if (tagReal.starts_with("+")) { // set + tagReal = tagReal.substr(1); + changed = !isTagged(tagReal, true); + } else // toggle if without prefix + setTag = !isTagged(tagReal, true); + + if (!changed) + return false; + + if (setTag) + m_tags.emplace(tagReal); + else + m_tags.erase(tagReal); + + return true; +} + +bool CTagKeeper::removeDynamicTags() { + return std::erase_if(m_tags, [](const auto& tag) { return tag.ends_with("*"); }); +} diff --git a/src/helpers/TagKeeper.hpp b/src/helpers/TagKeeper.hpp new file mode 100644 index 00000000..deeabf48 --- /dev/null +++ b/src/helpers/TagKeeper.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +class CTagKeeper { + public: + bool isTagged(const std::string& tag, bool strict = false); + bool applyTag(const std::string& tag, bool dynamic = false); + bool removeDynamicTags(); + + inline const auto& getTags() { + return m_tags; + }; + + private: + std::set m_tags; +}; diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 30f5aebf..d5be9f40 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -13,6 +13,7 @@ class CMonitor; class IPointer; class IKeyboard; +class CWLSurfaceResource; struct SRenderData { CMonitor* pMonitor; @@ -20,9 +21,9 @@ struct SRenderData { double x, y; // for iters - void* data = nullptr; - wlr_surface* surface = nullptr; - double w, h; + void* data = nullptr; + SP surface = nullptr; + double w, h; // for rounding bool dontRound = true; @@ -52,12 +53,6 @@ struct SRenderData { bool popup = false; }; -struct SExtensionFindingData { - Vector2D origin; - Vector2D vec; - wlr_surface** found; -}; - struct SSwipeGesture { PHLWORKSPACE pWorkspaceBegin = nullptr; diff --git a/src/helpers/XWaylandStubs.hpp b/src/helpers/XWaylandStubs.hpp deleted file mode 100644 index c21041cd..00000000 --- a/src/helpers/XWaylandStubs.hpp +++ /dev/null @@ -1,172 +0,0 @@ -#pragma once - -#include - -typedef unsigned int xcb_atom_t; -struct xcb_icccm_wm_hints_t; -typedef struct { - /** User specified flags */ - uint32_t flags; - /** User-specified position */ - int32_t x, y; - /** User-specified size */ - int32_t width, height; - /** Program-specified minimum size */ - int32_t min_width, min_height; - /** Program-specified maximum size */ - int32_t max_width, max_height; - /** Program-specified resize increments */ - int32_t width_inc, height_inc; - /** Program-specified minimum aspect ratios */ - int32_t min_aspect_num, min_aspect_den; - /** Program-specified maximum aspect ratios */ - int32_t max_aspect_num, max_aspect_den; - /** Program-specified base size */ - int32_t base_width, base_height; - /** Program-specified window gravity */ - uint32_t win_gravity; -} xcb_size_hints_t; -typedef unsigned int xcb_window_t; - -typedef enum xcb_stack_mode_t { - XCB_STACK_MODE_ABOVE = 0, - XCB_STACK_MODE_BELOW = 1, - XCB_STACK_MODE_TOP_IF = 2, - XCB_STACK_MODE_BOTTOM_IF = 3, - XCB_STACK_MODE_OPPOSITE = 4 -} xcb_stack_mode_t; - -struct wlr_xwayland { - struct wlr_xwayland_server* server; - struct wlr_xwm* xwm; - struct wlr_xwayland_cursor* cursor; - - const char* display_name; - - struct wl_display* wl_display; - struct wlr_compositor* compositor; - struct wlr_seat* seat; - - void* data; -}; - -struct wlr_xwayland_surface { - xcb_window_t window_id; - struct wlr_xwm* xwm; - uint32_t surface_id; - - struct wl_list link; - struct wl_list stack_link; - struct wl_list unpaired_link; - - struct wlr_surface* surface; - int16_t x, y; - uint16_t width, height; - uint16_t saved_width, saved_height; - bool override_redirect; - bool mapped; - - char* title; - char* _class; - char* instance; - char* role; - char* startup_id; - pid_t pid; - bool has_utf8_title; - - struct wl_list children; // wlr_xwayland_surface::parent_link - struct wlr_xwayland_surface* parent; - struct wl_list parent_link; // wlr_xwayland_surface::children - - xcb_atom_t* window_type; - size_t window_type_len; - - xcb_atom_t* protocols; - size_t protocols_len; - - uint32_t decorations; - xcb_icccm_wm_hints_t* hints; - xcb_size_hints_t* size_hints; - - bool pinging; - struct wl_event_source* ping_timer; - - // _NET_WM_STATE - bool modal; - bool fullscreen; - bool maximized_vert, maximized_horz; - bool minimized; - - bool has_alpha; - - struct { - struct wl_signal destroy; - struct wl_signal request_configure; - struct wl_signal request_move; - struct wl_signal request_resize; - struct wl_signal request_minimize; - struct wl_signal request_maximize; - struct wl_signal request_fullscreen; - struct wl_signal request_activate; - - struct wl_signal map; - struct wl_signal unmap; - struct wl_signal associate; - struct wl_signal dissociate; - struct wl_signal set_title; - struct wl_signal set_class; - struct wl_signal set_role; - struct wl_signal set_parent; - struct wl_signal set_startup_id; - struct wl_signal set_window_type; - struct wl_signal set_hints; - struct wl_signal set_decorations; - struct wl_signal set_override_redirect; - struct wl_signal set_geometry; - struct wl_signal ping_timeout; - } events; -}; - -struct wlr_xwayland_surface_configure_event { - struct wlr_xwayland_surface* surface; - int16_t x, y; - uint16_t width, height; - uint16_t mask; // xcb_config_window_t -}; - -struct wlr_xwayland_minimize_event { - struct wlr_xwayland_surface* surface; - bool minimize; -}; - -inline void wlr_xwayland_destroy(wlr_xwayland*) {} - -inline void wlr_xwayland_surface_configure(wlr_xwayland_surface*, int, int, int, int) {} - -inline bool wlr_surface_is_xwayland_surface(void*) { - return false; -} - -inline void wlr_xwayland_surface_activate(wlr_xwayland_surface*, bool) {} - -inline void wlr_xwayland_surface_restack(wlr_xwayland_surface*, void*, xcb_stack_mode_t) {} - -inline wlr_xwayland_surface* wlr_xwayland_surface_from_wlr_surface(void*) { - return nullptr; -} - -inline void wlr_xwayland_surface_close(wlr_xwayland_surface*) {} - -inline void wlr_xwayland_surface_set_fullscreen(wlr_xwayland_surface*, bool) {} - -inline void wlr_xwayland_surface_set_minimized(wlr_xwayland_surface*, bool) {} - -inline wlr_xwayland_surface* wlr_xwayland_surface_try_from_wlr_surface(wlr_surface*) { - return nullptr; -} - -inline bool wlr_xwayland_or_surface_wants_focus(const wlr_xwayland_surface*) { - return false; -} - -inline void wlr_xwayland_set_cursor(wlr_xwayland* wlr_xwayland, uint8_t* pixels, uint32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y) {} \ No newline at end of file diff --git a/src/helpers/memory/SharedPtr.hpp b/src/helpers/memory/SharedPtr.hpp index 77f5164a..02900911 100644 --- a/src/helpers/memory/SharedPtr.hpp +++ b/src/helpers/memory/SharedPtr.hpp @@ -60,7 +60,7 @@ namespace CSharedPointer_ { bool _destroying = false; void _destroy() { - if (!_data) + if (!_data || _destroying) return; // first, we destroy the data, but keep the pointer. @@ -297,6 +297,6 @@ static CSharedPointer makeShared(Args&&... args) { template struct std::hash> { std::size_t operator()(const CSharedPointer& p) const noexcept { - return std::hash{}(p->impl_); + return std::hash{}(p.impl_); } }; diff --git a/src/helpers/memory/WeakPtr.hpp b/src/helpers/memory/WeakPtr.hpp index f0e72146..872f8e55 100644 --- a/src/helpers/memory/WeakPtr.hpp +++ b/src/helpers/memory/WeakPtr.hpp @@ -185,6 +185,6 @@ class CWeakPointer { template struct std::hash> { std::size_t operator()(const CWeakPointer& p) const noexcept { - return std::hash{}(p->impl_); + return std::hash{}(p.impl_); } }; diff --git a/src/helpers/signal/Signal.cpp b/src/helpers/signal/Signal.cpp index fdd2cc23..fd2d11c8 100644 --- a/src/helpers/signal/Signal.cpp +++ b/src/helpers/signal/Signal.cpp @@ -2,19 +2,40 @@ #include void CSignal::emit(std::any data) { - bool dirty = false; + bool dirty = false; + std::vector> listeners; for (auto& l : m_vListeners) { - if (const CHyprSignalListener L = l.lock()) - L->emit(data); - else + if (l.expired()) { dirty = true; + continue; + } + + listeners.emplace_back(l.lock()); } + std::vector statics; for (auto& l : m_vStaticListeners) { + statics.emplace_back(l.get()); + } + + for (auto& l : listeners) { + // if there is only one lock, it means the event is only held by the listeners + // vector and was removed during our iteration + if (l.strongRef() == 1) { + dirty = true; + continue; + } l->emit(data); } + for (auto& l : statics) { + l->emit(data); + } + + // release SPs + listeners.clear(); + if (dirty) std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); }); } diff --git a/src/hyprerror/HyprError.cpp b/src/hyprerror/HyprError.cpp index d147a6bb..a325d858 100644 --- a/src/hyprerror/HyprError.cpp +++ b/src/hyprerror/HyprError.cpp @@ -22,6 +22,8 @@ CHyprError::CHyprError() { if (m_fFadeOpacity.isBeingAnimated() || m_bMonitorChanged) g_pHyprRenderer->damageBox(&m_bDamageBox); }); + + m_pTexture = makeShared(); } CHyprError::~CHyprError() { @@ -34,9 +36,8 @@ void CHyprError::queueCreate(std::string message, const CColor& color) { } void CHyprError::createQueued() { - if (m_bIsCreated) { - m_tTexture.destroyTexture(); - } + if (m_bIsCreated) + m_pTexture->destroyTexture(); m_fFadeOpacity.setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn")); @@ -136,8 +137,8 @@ void CHyprError::createQueued() { // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - m_tTexture.allocate(); - glBindTexture(GL_TEXTURE_2D, m_tTexture.m_iTexID); + m_pTexture->allocate(); + glBindTexture(GL_TEXTURE_2D, m_pTexture->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -170,7 +171,7 @@ void CHyprError::draw() { if (!m_fFadeOpacity.isBeingAnimated()) { if (m_fFadeOpacity.value() == 0.f) { m_bQueuedDestroy = false; - m_tTexture.destroyTexture(); + m_pTexture->destroyTexture(); m_bIsCreated = false; m_szQueued = ""; return; @@ -193,7 +194,7 @@ void CHyprError::draw() { m_bMonitorChanged = false; - g_pHyprOpenGL->renderTexture(m_tTexture, &monbox, m_fFadeOpacity.value(), 0); + g_pHyprOpenGL->renderTexture(m_pTexture, &monbox, m_fFadeOpacity.value(), 0); } void CHyprError::destroy() { diff --git a/src/hyprerror/HyprError.hpp b/src/hyprerror/HyprError.hpp index aaa8bd12..8dbb4521 100644 --- a/src/hyprerror/HyprError.hpp +++ b/src/hyprerror/HyprError.hpp @@ -21,7 +21,7 @@ class CHyprError { CColor m_cQueued; bool m_bQueuedDestroy = false; bool m_bIsCreated = false; - CTexture m_tTexture; + SP m_pTexture; CAnimatedVariable m_fFadeOpacity; CBox m_bDamageBox = {0, 0, 0, 0}; diff --git a/src/includes.hpp b/src/includes.hpp index dfbe0221..dbae7635 100644 --- a/src/includes.hpp +++ b/src/includes.hpp @@ -82,10 +82,6 @@ extern "C" { #if WLR_HAS_X11_BACKEND #include #endif - -#ifndef NO_XWAYLAND -#include -#endif } #undef delete @@ -110,7 +106,6 @@ extern "C" { #ifdef NO_XWAYLAND #define XWAYLAND false -#include "helpers/XWaylandStubs.hpp" #else #define XWAYLAND true #endif diff --git a/src/layout/IHyprLayout.cpp b/src/layout/IHyprLayout.cpp index ffd9ddea..b4270bb8 100644 --- a/src/layout/IHyprLayout.cpp +++ b/src/layout/IHyprLayout.cpp @@ -5,6 +5,8 @@ #include "../config/ConfigValue.hpp" #include "../desktop/Window.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../xwayland/XSurface.hpp" void IHyprLayout::onWindowCreated(PHLWINDOW pWindow, eDirection direction) { if (pWindow->m_bIsFloating) { @@ -98,8 +100,8 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { } if (desiredGeometry.width <= 5 || desiredGeometry.height <= 5) { - const auto PWINDOWSURFACE = pWindow->m_pWLSurface.wlr(); - pWindow->m_vRealSize = Vector2D(PWINDOWSURFACE->current.width, PWINDOWSURFACE->current.height); + const auto PWINDOWSURFACE = pWindow->m_pWLSurface->resource(); + pWindow->m_vRealSize = PWINDOWSURFACE->current.size; if ((desiredGeometry.width <= 1 || desiredGeometry.height <= 1) && pWindow->m_bIsX11 && pWindow->m_iX11Type == 2) { // XDG windows should be fine. TODO: check for weird atoms? @@ -111,10 +113,10 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { if (pWindow->m_vRealSize.goal().x <= 5 || pWindow->m_vRealSize.goal().y <= 5) pWindow->m_vRealSize = PMONITOR->vecSize / 2.f; - if (pWindow->m_bIsX11 && pWindow->m_uSurface.xwayland->override_redirect) { + if (pWindow->m_bIsX11 && pWindow->m_iX11Type == 2) { - if (pWindow->m_uSurface.xwayland->x != 0 && pWindow->m_uSurface.xwayland->y != 0) - pWindow->m_vRealPosition = g_pXWaylandManager->xwaylandToWaylandCoords({pWindow->m_uSurface.xwayland->x, pWindow->m_uSurface.xwayland->y}); + if (pWindow->m_pXWaylandSurface->geometry.x != 0 && pWindow->m_pXWaylandSurface->geometry.y != 0) + pWindow->m_vRealPosition = g_pXWaylandManager->xwaylandToWaylandCoords(pWindow->m_pXWaylandSurface->geometry.pos()); else pWindow->m_vRealPosition = Vector2D(PMONITOR->vecPosition.x + (PMONITOR->vecSize.x - pWindow->m_vRealSize.goal().x) / 2.f, PMONITOR->vecPosition.y + (PMONITOR->vecSize.y - pWindow->m_vRealSize.goal().y) / 2.f); @@ -146,7 +148,7 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { pWindow->m_vRealPosition = pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealPosition.goal() + pWindow->m_pXDGSurface->toplevel->parent->window->m_vRealSize.goal() / 2.F - desiredGeometry.size() / 2.F; else - pWindow->m_vRealPosition = PMONITOR->vecPosition + desiredGeometry.size() / 2.F; + pWindow->m_vRealPosition = PMONITOR->vecPosition + PMONITOR->vecSize / 2.F - desiredGeometry.size() / 2.F; } else { // if it is, we respect where it wants to put itself, but apply monitor offset if outside // most of these are popups @@ -161,7 +163,7 @@ void IHyprLayout::onWindowCreatedFloating(PHLWINDOW pWindow) { if (*PXWLFORCESCALEZERO && pWindow->m_bIsX11) pWindow->m_vRealSize = pWindow->m_vRealSize.goal() / PMONITOR->scale; - if (pWindow->m_bX11DoesntWantBorders || (pWindow->m_bIsX11 && pWindow->m_uSurface.xwayland->override_redirect)) { + if (pWindow->m_bX11DoesntWantBorders || (pWindow->m_bIsX11 && pWindow->m_iX11Type == 2)) { pWindow->m_vRealPosition.warp(); pWindow->m_vRealSize.warp(); } diff --git a/src/macros.hpp b/src/macros.hpp index 66dfe783..4c6d621c 100644 --- a/src/macros.hpp +++ b/src/macros.hpp @@ -89,4 +89,14 @@ } #else #define UNREACHABLE() std::unreachable(); -#endif \ No newline at end of file +#endif + +#define GLCALL(__CALL__) \ + { \ + __CALL__; \ + auto err = glGetError(); \ + if (err != GL_NO_ERROR) { \ + Debug::log(ERR, "[GLES] Error in call at {}@{}: 0x{:x}", __LINE__, \ + ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })(), err); \ + } \ + } diff --git a/src/managers/CursorManager.cpp b/src/managers/CursorManager.cpp index ea36b9b2..4430d0f4 100644 --- a/src/managers/CursorManager.cpp +++ b/src/managers/CursorManager.cpp @@ -2,6 +2,7 @@ #include "Compositor.hpp" #include "../config/ConfigValue.hpp" #include "PointerManager.hpp" +#include "../xwayland/XWayland.hpp" extern "C" { #include @@ -57,6 +58,9 @@ CCursorManager::CCursorManager() { CCursorManager::~CCursorManager() { if (m_pWLRXCursorMgr) wlr_xcursor_manager_destroy(m_pWLRXCursorMgr); + + if (m_pAnimationTimer) + wl_event_source_remove(m_pAnimationTimer); } void CCursorManager::dropBufferRef(CCursorManager::CCursorBuffer* ref) { @@ -113,8 +117,8 @@ wlr_buffer* CCursorManager::getCursorBuffer() { return !m_vCursorBuffers.empty() ? &m_vCursorBuffers.back()->wlrBuffer.base : nullptr; } -void CCursorManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot) { - if (!surf || !surf->wlr()) +void CCursorManager::setCursorSurface(SP surf, const Vector2D& hotspot) { + if (!surf || !surf->resource()) g_pPointerManager->resetCursorImage(); else g_pPointerManager->setCursorSurface(surf, hotspot); @@ -246,14 +250,14 @@ SCursorImageData CCursorManager::dataFor(const std::string& name) { return IMAGES.images[0]; } -void CCursorManager::setXWaylandCursor(wlr_xwayland* xwayland) { +void CCursorManager::setXWaylandCursor() { const auto CURSOR = dataFor("left_ptr"); if (CURSOR.surface) { - wlr_xwayland_set_cursor(xwayland, cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), CURSOR.size, CURSOR.size, CURSOR.hotspotX, - CURSOR.hotspotY); + g_pXWayland->setCursor(cairo_image_surface_get_data(CURSOR.surface), cairo_image_surface_get_stride(CURSOR.surface), {CURSOR.size, CURSOR.size}, + {CURSOR.hotspotX, CURSOR.hotspotY}); } else if (const auto XCURSOR = wlr_xcursor_manager_get_xcursor(m_pWLRXCursorMgr, "left_ptr", 1); XCURSOR) { - wlr_xwayland_set_cursor(xwayland, XCURSOR->images[0]->buffer, XCURSOR->images[0]->width * 4, XCURSOR->images[0]->width, XCURSOR->images[0]->height, - XCURSOR->images[0]->hotspot_x, XCURSOR->images[0]->hotspot_y); + g_pXWayland->setCursor(XCURSOR->images[0]->buffer, XCURSOR->images[0]->width * 4, {XCURSOR->images[0]->width, XCURSOR->images[0]->height}, + {XCURSOR->images[0]->hotspot_x, XCURSOR->images[0]->hotspot_y}); } else Debug::log(ERR, "CursorManager: no valid cursor for xwayland"); } @@ -266,9 +270,6 @@ void CCursorManager::updateTheme() { highestScale = m->scale; } - if (std::round(highestScale * m_iSize) == m_sCurrentStyleInfo.size) - return; - if (m_sCurrentStyleInfo.size && m_pHyprcursor->valid()) m_pHyprcursor->cursorSurfaceStyleDone(m_sCurrentStyleInfo); @@ -286,13 +287,52 @@ void CCursorManager::updateTheme() { } } -void CCursorManager::changeTheme(const std::string& name, const int size) { - m_pHyprcursor = std::make_unique(name.empty() ? "" : name.c_str(), hcLogger); - m_szTheme = name; - m_iSize = size; +bool CCursorManager::changeTheme(const std::string& name, const int size) { + auto options = Hyprcursor::SManagerOptions(); + options.logFn = hcLogger; + options.allowDefaultFallback = false; - if (!m_pHyprcursor->valid()) - Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to X.", m_szTheme); + m_pHyprcursor = std::make_unique(name.empty() ? "" : name.c_str(), options); + if (m_pHyprcursor->valid()) { + m_szTheme = name; + m_iSize = size; + updateTheme(); + return true; + } + + Debug::log(ERR, "Hyprcursor failed loading theme \"{}\", falling back to X.", name); + + if (m_pWLRXCursorMgr) + 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_iSize = size; + updateTheme(); + return true; + } + + Debug::log(ERR, "X also failed loading theme \"{}\", falling back to previous theme.", name); + + m_pHyprcursor = std::make_unique(m_szTheme.c_str(), hcLogger); + + wlr_xcursor_manager_destroy(m_pWLRXCursorMgr); + m_pWLRXCursorMgr = wlr_xcursor_manager_create(m_szTheme.c_str(), m_iSize); + wlr_xcursor_manager_load(m_pWLRXCursorMgr, 1.0); updateTheme(); + return false; } diff --git a/src/managers/CursorManager.hpp b/src/managers/CursorManager.hpp index b4c5232a..6fbff636 100644 --- a/src/managers/CursorManager.hpp +++ b/src/managers/CursorManager.hpp @@ -8,7 +8,6 @@ struct wlr_buffer; struct wlr_xcursor_manager; -struct wlr_xwayland; class CWLSurface; class CCursorManager { @@ -19,13 +18,13 @@ class CCursorManager { wlr_buffer* getCursorBuffer(); void setCursorFromName(const std::string& name); - void setCursorSurface(CWLSurface* surf, const Vector2D& hotspot); + void setCursorSurface(SP surf, const Vector2D& hotspot); void setXCursor(const std::string& name); - void changeTheme(const std::string& name, const int size); + bool changeTheme(const std::string& name, const int size); void updateTheme(); SCursorImageData dataFor(const std::string& name); // for xwayland - void setXWaylandCursor(wlr_xwayland* xwayland); + void setXWaylandCursor(); void tickAnimatedCursor(); diff --git a/src/managers/HookSystemManager.cpp b/src/managers/HookSystemManager.cpp index a1920863..208c79ae 100644 --- a/src/managers/HookSystemManager.cpp +++ b/src/managers/HookSystemManager.cpp @@ -28,7 +28,7 @@ void CHookSystemManager::emit(std::vector* const callbacks, SCal return; std::vector faultyHandles; - bool needsDeadCleanup = false; + volatile bool needsDeadCleanup = false; for (auto& cb : *callbacks) { @@ -80,4 +80,4 @@ std::vector* CHookSystemManager::getVecForEvent(const std::strin Debug::log(LOG, "[hookSystem] New hook event registered: {}", event); return &m_mRegisteredHooks[event]; -} \ No newline at end of file +} diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index 925a7fb7..5811f4a1 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -1,17 +1,17 @@ -#include "KeybindManager.hpp" -#include "../render/decorations/CHyprGroupBarDecoration.hpp" -#include "debug/Log.hpp" -#include "helpers/VarList.hpp" #include "../config/ConfigValue.hpp" -#include "TokenManager.hpp" -#include "../protocols/ShortcutsInhibit.hpp" #include "../devices/IKeyboard.hpp" #include "../managers/SeatManager.hpp" +#include "../protocols/ShortcutsInhibit.hpp" +#include "../render/decorations/CHyprGroupBarDecoration.hpp" +#include "KeybindManager.hpp" +#include "TokenManager.hpp" +#include "debug/Log.hpp" +#include "helpers/VarList.hpp" #include -#include +#include #include -#include +#include #include #include @@ -87,8 +87,10 @@ CKeybindManager::CKeybindManager() { m_mDispatchers["cyclenext"] = circleNext; m_mDispatchers["focuswindowbyclass"] = focusWindow; m_mDispatchers["focuswindow"] = focusWindow; + m_mDispatchers["tagwindow"] = tagWindow; m_mDispatchers["submap"] = setSubmap; m_mDispatchers["pass"] = pass; + m_mDispatchers["sendshortcut"] = sendshortcut; m_mDispatchers["layoutmsg"] = layoutmsg; m_mDispatchers["toggleopaque"] = toggleOpaque; m_mDispatchers["dpms"] = dpms; @@ -255,6 +257,16 @@ bool CKeybindManager::ensureMouseBindState() { return false; } +void updateRelativeCursorCoords() { + static auto PNOWARPS = CConfigValue("cursor:no_warps"); + + if (*PNOWARPS) + return; + + if (g_pCompositor->m_pLastWindow) + g_pCompositor->m_pLastWindow->m_vRelativeCursorCoordsOnLastWarp = g_pInputManager->getMouseCoordsInternal() - g_pCompositor->m_pLastWindow->m_vPosition; +} + bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) { if (!monitor) return false; @@ -275,8 +287,9 @@ bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) { const auto PNEWWINDOW = PNEWWORKSPACE->getLastFocusedWindow(); if (PNEWWINDOW) { + updateRelativeCursorCoords(); g_pCompositor->focusWindow(PNEWWINDOW); - g_pCompositor->warpCursorTo(PNEWWINDOW->middle()); + PNEWWINDOW->warpCursor(); g_pInputManager->m_pForcedFocus = PNEWWINDOW; g_pInputManager->simulateMouseMovement(); @@ -311,8 +324,9 @@ void CKeybindManager::switchToWindow(PHLWINDOW PWINDOWTOCHANGETO) { if (!PWINDOWTOCHANGETO->m_bPinned) g_pCompositor->setWindowFullscreen(PWINDOWTOCHANGETO, true, FSMODE); } else { + updateRelativeCursorCoords(); g_pCompositor->focusWindow(PWINDOWTOCHANGETO); - g_pCompositor->warpCursorTo(PWINDOWTOCHANGETO->middle()); + PWINDOWTOCHANGETO->warpCursor(); g_pInputManager->m_pForcedFocus = PWINDOWTOCHANGETO; g_pInputManager->simulateMouseMovement(); @@ -589,7 +603,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t modmask, const SPressedKeyWi } for (auto& k : m_lKeybinds) { - const bool SPECIALDISPATCHER = k.handler == "global" || k.handler == "pass" || k.handler == "mouse"; + const bool SPECIALDISPATCHER = k.handler == "global" || k.handler == "pass" || k.handler == "sendshortcut" || k.handler == "mouse"; const bool SPECIALTRIGGERED = std::find_if(m_vPressedSpecialBinds.begin(), m_vPressedSpecialBinds.end(), [&](const auto& other) { return other == &k; }) != m_vPressedSpecialBinds.end(); const bool IGNORECONDITIONS = @@ -1071,6 +1085,14 @@ void CKeybindManager::changeworkspace(std::string args) { g_pCompositor->setActiveMonitor(PMONITORWORKSPACEOWNER); + if (BISWORKSPACECURRENT) { + if (*PALLOWWORKSPACECYCLES) + pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); + else if (!EXPLICITPREVIOUS && !*PBACKANDFORTH) + pWorkspaceToChangeTo->rememberPrevWorkspace(nullptr); + } else + pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); + PMONITORWORKSPACEOWNER->changeWorkspace(pWorkspaceToChangeTo, false, true); if (PMONITOR != PMONITORWORKSPACEOWNER) { @@ -1083,14 +1105,6 @@ void CKeybindManager::changeworkspace(std::string args) { g_pCompositor->warpCursorTo(middle); } - if (BISWORKSPACECURRENT) { - if (*PALLOWWORKSPACECYCLES) - pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); - else if (!EXPLICITPREVIOUS && !*PBACKANDFORTH) - pWorkspaceToChangeTo->rememberPrevWorkspace(nullptr); - } else - pWorkspaceToChangeTo->rememberPrevWorkspace(PCURRENTWORKSPACE); - if (!g_pInputManager->m_bLastFocusOnLS) { if (g_pCompositor->m_pLastFocus) g_pInputManager->sendMotionEventsToFocused(); @@ -1144,6 +1158,8 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) { const auto POLDWS = PWINDOW->m_pWorkspace; static auto PALLOWWORKSPACECYCLES = CConfigValue("binds:allow_workspace_cycles"); + updateRelativeCursorCoords(); + g_pHyprRenderer->damageWindow(PWINDOW); if (pWorkspace) { @@ -1163,13 +1179,13 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) { else if (POLDWS->m_bIsSpecialWorkspace) g_pCompositor->getMonitorFromID(POLDWS->m_iMonitorID)->setSpecialWorkspace(nullptr); + if (*PALLOWWORKSPACECYCLES) + pWorkspace->rememberPrevWorkspace(POLDWS); + pMonitor->changeWorkspace(pWorkspace); g_pCompositor->focusWindow(PWINDOW); - g_pCompositor->warpCursorTo(PWINDOW->middle()); - - if (*PALLOWWORKSPACECYCLES) - pWorkspace->rememberPrevWorkspace(POLDWS); + PWINDOW->warpCursor(); } void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) { @@ -1301,8 +1317,9 @@ void CKeybindManager::swapActive(std::string args) { if (!PWINDOWTOCHANGETO) return; + updateRelativeCursorCoords(); g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, PWINDOWTOCHANGETO); - g_pCompositor->warpCursorTo(PLASTWINDOW->middle()); + PLASTWINDOW->warpCursor(); } void CKeybindManager::moveActiveTo(std::string args) { @@ -1355,9 +1372,11 @@ void CKeybindManager::moveActiveTo(std::string args) { // If the window to change to is on the same workspace, switch them const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg); if (PWINDOWTOCHANGETO) { + updateRelativeCursorCoords(); + g_pLayoutManager->getCurrentLayout()->moveWindowTo(PLASTWINDOW, args, silent); if (!silent) - g_pCompositor->warpCursorTo(PLASTWINDOW->middle()); + PLASTWINDOW->warpCursor(); return; } @@ -1894,6 +1913,8 @@ void CKeybindManager::focusWindow(std::string regexp) { return; } + updateRelativeCursorCoords(); + if (g_pCompositor->m_pLastMonitor && g_pCompositor->m_pLastMonitor->activeWorkspace != PWINDOW->m_pWorkspace && g_pCompositor->m_pLastMonitor->activeSpecialWorkspace != PWINDOW->m_pWorkspace) { Debug::log(LOG, "Fake executing workspace to move focus"); @@ -1924,7 +1945,24 @@ void CKeybindManager::focusWindow(std::string regexp) { } else g_pCompositor->focusWindow(PWINDOW); - g_pCompositor->warpCursorTo(PWINDOW->middle()); + PWINDOW->warpCursor(); +} + +void CKeybindManager::tagWindow(std::string args) { + PHLWINDOW PWINDOW = nullptr; + CVarList vars{args, 0, 's', true}; + + if (vars.size() == 1) + PWINDOW = g_pCompositor->m_pLastWindow.lock(); + else if (vars.size() == 2) + PWINDOW = g_pCompositor->getWindowByRegex(vars[1]); + else + return; + + if (PWINDOW && PWINDOW->m_tags.applyTag(vars[0])) { + PWINDOW->updateDynamicRules(); + g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW->m_pSelf.lock()); + } } void CKeybindManager::setSubmap(std::string submap) { @@ -1965,14 +2003,14 @@ void CKeybindManager::pass(std::string regexp) { } const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsX11; - const auto LASTSRF = g_pCompositor->m_pLastFocus; + const auto LASTSRF = g_pCompositor->m_pLastFocus.lock(); // pass all mf shit if (!XWTOXW) { if (g_pKeybindManager->m_uLastCode != 0) - g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface.wlr()); + g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface->resource()); else - g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface.wlr(), {1, 1}); + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface->resource(), {1, 1}); } g_pSeatManager->sendKeyboardMods(g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0); @@ -2006,10 +2044,10 @@ void CKeybindManager::pass(std::string regexp) { // please kill me if (PWINDOW->m_bIsX11) { if (g_pKeybindManager->m_uLastCode != 0) { - g_pSeatManager->state.keyboardFocus = nullptr; + g_pSeatManager->state.keyboardFocus.reset(); g_pSeatManager->state.keyboardFocusResource.reset(); } else { - g_pSeatManager->state.pointerFocus = nullptr; + g_pSeatManager->state.pointerFocus.reset(); g_pSeatManager->state.pointerFocusResource.reset(); } } @@ -2019,7 +2057,158 @@ void CKeybindManager::pass(std::string regexp) { if (g_pKeybindManager->m_uLastCode != 0) g_pSeatManager->setKeyboardFocus(LASTSRF); else - g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface.wlr(), SL); + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface->resource(), SL); +} + +void CKeybindManager::sendshortcut(std::string args) { + // args=[,WINDOW_RULES] + const auto ARGS = CVarList(args, 3); + if (ARGS.size() != 3) { + Debug::log(ERR, "sendshortcut: invalid args"); + return; + } + + const auto MOD = g_pKeybindManager->stringToModMask(ARGS[0]); + const auto KEY = ARGS[1]; + uint32_t keycode = 0; + bool isMouse = 0; + + // similar to parseKey in ConfigManager + if (isNumber(KEY) && std::stoi(KEY) > 9) + keycode = std::stoi(KEY); + else if (KEY.compare(0, 5, "code:") == 0 && isNumber(KEY.substr(5))) + keycode = std::stoi(KEY.substr(5)); + else if (KEY.compare(0, 6, "mouse:") == 0 && isNumber(KEY.substr(6))) { + keycode = std::stoi(KEY.substr(6)); + isMouse = 1; + if (keycode < 272) { + Debug::log(ERR, "sendshortcut: invalid mouse button"); + return; + } + } else { + + // here, we need to find the keycode from the key name + // this is not possible through xkb's lib, so we need to iterate through all keycodes + // once found, we save it to the cache + + const auto KEYSYM = xkb_keysym_from_name(KEY.c_str(), XKB_KEYSYM_CASE_INSENSITIVE); + keycode = 0; + + const auto KB = g_pSeatManager->keyboard; + + if (!KB) { + Debug::log(ERR, "sendshortcut: no kb"); + return; + } + + const auto KEYPAIRSTRING = std::format("{}{}", (uintptr_t)KB.get(), KEY); + + if (!g_pKeybindManager->m_mKeyToCodeCache.contains(KEYPAIRSTRING)) { + xkb_keymap* km = KB->wlr()->keymap; + xkb_state* ks = KB->xkbTranslationState; + + xkb_keycode_t keycode_min, keycode_max; + keycode_min = xkb_keymap_min_keycode(km); + keycode_max = xkb_keymap_max_keycode(km); + + for (xkb_keycode_t kc = keycode_min; kc <= keycode_max; ++kc) { + xkb_keysym_t sym = xkb_state_key_get_one_sym(ks, kc); + + if (sym == KEYSYM) { + keycode = kc; + g_pKeybindManager->m_mKeyToCodeCache[KEYPAIRSTRING] = keycode; + } + } + + if (!keycode) { + Debug::log(ERR, "sendshortcut: key not found"); + return; + } + + } else + keycode = g_pKeybindManager->m_mKeyToCodeCache[KEYPAIRSTRING]; + } + + if (!keycode) { + Debug::log(ERR, "sendshortcut: invalid key"); + return; + } + + const std::string regexp = ARGS[2]; + PHLWINDOW PWINDOW = nullptr; + const auto LASTSURFACE = g_pCompositor->m_pLastFocus.lock(); + + //if regexp is not empty, send shortcut to current window + //else, dont change focus + if (regexp != "") { + PWINDOW = g_pCompositor->getWindowByRegex(regexp); + + if (!PWINDOW) { + Debug::log(ERR, "sendshortcut: window not found"); + return; + } + + if (!g_pSeatManager->keyboard) { + Debug::log(ERR, "No kb in sendshortcut?"); + return; + } + + if (!isMouse) + g_pSeatManager->setKeyboardFocus(PWINDOW->m_pWLSurface->resource()); + else + g_pSeatManager->setPointerFocus(PWINDOW->m_pWLSurface->resource(), {1, 1}); + } + + //copied the rest from pass and modified it + // if wl -> xwl, activate destination + if (PWINDOW && PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow && !g_pCompositor->m_pLastWindow->m_bIsX11) + g_pXWaylandManager->activateSurface(PWINDOW->m_pWLSurface->resource(), true); + // if xwl -> xwl, send to current. Timing issues make this not work. + if (PWINDOW && PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->m_bIsX11) + PWINDOW = nullptr; + + g_pSeatManager->sendKeyboardMods(MOD, 0, 0, 0); + + if (g_pKeybindManager->m_iPassPressed == 1) { + if (!isMouse) + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + else + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_PRESSED); + } else if (g_pKeybindManager->m_iPassPressed == 0) { + if (!isMouse) + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + else + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_RELEASED); + } else { + // dynamic call of the dispatcher + if (!isMouse) { + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_PRESSED); + g_pSeatManager->sendKeyboardKey(g_pKeybindManager->m_uTimeLastMs, keycode - 8, WL_KEYBOARD_KEY_STATE_RELEASED); + } else { + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_PRESSED); + g_pSeatManager->sendPointerButton(g_pKeybindManager->m_uTimeLastMs, keycode, WL_POINTER_BUTTON_STATE_RELEASED); + } + } + + if (!PWINDOW) + return; + + if (PWINDOW->m_bIsX11) { //xwayland hack, see pass + if (!isMouse) { + g_pSeatManager->state.keyboardFocus.reset(); + g_pSeatManager->state.keyboardFocusResource.reset(); + } else { + g_pSeatManager->state.pointerFocus.reset(); + g_pSeatManager->state.pointerFocusResource.reset(); + } + } + + const auto SL = PWINDOW->m_vRealPosition.goal() - g_pInputManager->getMouseCoordsInternal(); + + if (!isMouse) + g_pSeatManager->setKeyboardFocus(LASTSURFACE); + else + g_pSeatManager->setPointerFocus(LASTSURFACE, SL); } void CKeybindManager::layoutmsg(std::string msg) { @@ -2275,6 +2464,8 @@ void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowIn if (pWindow->m_sGroupData.deny) return; + updateRelativeCursorCoords(); + g_pLayoutManager->getCurrentLayout()->onWindowRemoved(pWindow); // This removes groupped property! static auto USECURRPOS = CConfigValue("group:insert_after_current"); @@ -2285,7 +2476,7 @@ void CKeybindManager::moveWindowIntoGroup(PHLWINDOW pWindow, PHLWINDOW pWindowIn pWindow->updateWindowDecos(); g_pLayoutManager->getCurrentLayout()->recalculateWindow(pWindow); g_pCompositor->focusWindow(pWindow); - g_pCompositor->warpCursorTo(pWindow->middle()); + pWindow->warpCursor(); if (!pWindow->getDecorationByType(DECORATION_GROUPBAR)) pWindow->addWindowDeco(std::make_unique(pWindow)); @@ -2308,6 +2499,8 @@ void CKeybindManager::moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& default: direction = DIRECTION_DEFAULT; } + updateRelativeCursorCoords(); + if (pWindow->m_sGroupData.pNextWindow.lock() == pWindow) { pWindow->destroyGroup(); } else { @@ -2323,10 +2516,10 @@ void CKeybindManager::moveWindowOutOfGroup(PHLWINDOW pWindow, const std::string& if (*BFOCUSREMOVEDWINDOW) { g_pCompositor->focusWindow(pWindow); - g_pCompositor->warpCursorTo(pWindow->middle()); + pWindow->warpCursor(); } else { g_pCompositor->focusWindow(PWINDOWPREV); - g_pCompositor->warpCursorTo(PWINDOWPREV->middle()); + PWINDOWPREV->warpCursor(); } g_pEventManager->postEvent(SHyprIPCEvent{"moveoutofgroup", std::format("{:x}", (uintptr_t)pWindow.get())}); @@ -2406,24 +2599,26 @@ void CKeybindManager::moveWindowOrGroup(std::string args) { const bool ISWINDOWGROUPLOCKED = ISWINDOWGROUP && PWINDOW->getGroupHead()->m_sGroupData.locked; const bool ISWINDOWGROUPSINGLE = ISWINDOWGROUP && PWINDOW->m_sGroupData.pNextWindow.lock() == PWINDOW; + updateRelativeCursorCoords(); + // note: PWINDOWINDIR is not null implies !PWINDOW->m_bIsFloating if (PWINDOWINDIR && PWINDOWINDIR->m_sGroupData.pNextWindow) { // target is group if (!*PIGNOREGROUPLOCK && (PWINDOWINDIR->getGroupHead()->m_sGroupData.locked || ISWINDOWGROUPLOCKED || PWINDOW->m_sGroupData.deny)) { g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); - g_pCompositor->warpCursorTo(PWINDOW->middle()); + PWINDOW->warpCursor(); } else moveWindowIntoGroup(PWINDOW, PWINDOWINDIR); } else if (PWINDOWINDIR) { // target is regular window if ((!*PIGNOREGROUPLOCK && ISWINDOWGROUPLOCKED) || !ISWINDOWGROUP || (ISWINDOWGROUPSINGLE && PWINDOW->m_eGroupRules & GROUP_SET_ALWAYS)) { g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); - g_pCompositor->warpCursorTo(PWINDOW->middle()); + PWINDOW->warpCursor(); } else moveWindowOutOfGroup(PWINDOW, args); } else if ((*PIGNOREGROUPLOCK || !ISWINDOWGROUPLOCKED) && ISWINDOWGROUP) { // no target window moveWindowOutOfGroup(PWINDOW, args); } else if (!PWINDOWINDIR && !ISWINDOWGROUP) { // no target in dir and not in group g_pLayoutManager->getCurrentLayout()->moveWindowTo(PWINDOW, args); - g_pCompositor->warpCursorTo(PWINDOW->middle()); + PWINDOW->warpCursor(); } g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW); diff --git a/src/managers/KeybindManager.hpp b/src/managers/KeybindManager.hpp index a2025bb0..305e563f 100644 --- a/src/managers/KeybindManager.hpp +++ b/src/managers/KeybindManager.hpp @@ -95,6 +95,12 @@ class CKeybindManager { std::list m_lKeybinds; + //since we cant find keycode through keyname in xkb: + //on sendshortcut call, we once search for keyname (e.g. "g") the correct keycode (e.g. 42) + //and cache it in this map to make sendshortcut calls faster + //we also store the keyboard pointer (in the string) to differentiate between different keyboard (layouts) + std::unordered_map m_mKeyToCodeCache; + private: std::deque m_dPressedKeys; @@ -175,8 +181,10 @@ class CKeybindManager { static void resizeWindow(std::string); static void circleNext(std::string); static void focusWindow(std::string); + static void tagWindow(std::string); static void setSubmap(std::string); static void pass(std::string); + static void sendshortcut(std::string); static void layoutmsg(std::string); static void toggleOpaque(std::string); static void dpms(std::string); diff --git a/src/managers/PointerManager.cpp b/src/managers/PointerManager.cpp index 21f0521b..ca16b14d 100644 --- a/src/managers/PointerManager.cpp +++ b/src/managers/PointerManager.cpp @@ -3,6 +3,7 @@ #include "../config/ConfigValue.hpp" #include "../protocols/PointerGestures.hpp" #include "../protocols/FractionalScale.hpp" +#include "../protocols/core/Compositor.hpp" #include "SeatManager.hpp" #include #include @@ -119,7 +120,17 @@ static bool output_pick_cursor_format(struct wlr_output* output, struct wlr_drm_ } } - return output_pick_format(output, display_formats, format, DRM_FORMAT_ARGB8888); + // 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() { @@ -131,7 +142,11 @@ CPointerManager::CPointerManager() { PMONITOR->events.modeChanged.registerStaticListener([this](void* owner, std::any data) { onMonitorLayoutChange(); }, nullptr); PMONITOR->events.disconnect.registerStaticListener([this](void* owner, std::any data) { onMonitorLayoutChange(); }, nullptr); PMONITOR->events.destroy.registerStaticListener( - [this](void* owner, std::any data) { std::erase_if(monitorStates, [](const auto& other) { return other->monitor.expired(); }); }, nullptr); + [this](void* owner, std::any data) { + if (g_pCompositor && !g_pCompositor->m_bIsShuttingDown) + std::erase_if(monitorStates, [](const auto& other) { return other->monitor.expired(); }); + }, + nullptr); }); } @@ -198,13 +213,13 @@ void CPointerManager::setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, damageIfSoftware(); } -void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot) { +void CPointerManager::setCursorSurface(SP surf, const Vector2D& hotspot) { damageIfSoftware(); if (surf == currentCursorImage.surface) { - if (hotspot != currentCursorImage.hotspot || (surf && surf->wlr() ? surf->wlr()->current.scale : 1.F) != currentCursorImage.scale) { + if (hotspot != currentCursorImage.hotspot || (surf && surf->resource() ? surf->resource()->current.scale : 1.F) != currentCursorImage.scale) { currentCursorImage.hotspot = hotspot; - currentCursorImage.scale = surf && surf->wlr() ? surf->wlr()->current.scale : 1.F; + currentCursorImage.scale = surf && surf->resource() ? surf->resource()->current.scale : 1.F; updateCursorBackend(); damageIfSoftware(); } @@ -215,27 +230,24 @@ void CPointerManager::setCursorSurface(CWLSurface* surf, const Vector2D& hotspot resetCursorImage(false); if (surf) { - currentCursorImage.size = {surf->wlr()->current.buffer_width, surf->wlr()->current.buffer_height}; currentCursorImage.surface = surf; - currentCursorImage.scale = surf->wlr()->current.scale; + currentCursorImage.scale = surf->resource()->current.scale; currentCursorImage.destroySurface = surf->events.destroy.registerListener([this](std::any data) { resetCursorImage(); }); - currentCursorImage.hyprListener_commitSurface.initCallback( - &surf->wlr()->events.commit, - [this](void* owner, void* data) { - damageIfSoftware(); - currentCursorImage.size = {currentCursorImage.surface->wlr()->current.buffer_width, currentCursorImage.surface->wlr()->current.buffer_height}; - currentCursorImage.scale = currentCursorImage.surface && currentCursorImage.surface->wlr() ? currentCursorImage.surface->wlr()->current.scale : 1.F; - recheckEnteredOutputs(); - updateCursorBackend(); - damageIfSoftware(); - }, - nullptr, "CPointerManager"); + currentCursorImage.commitSurface = surf->resource()->events.commit.registerListener([this](std::any data) { + damageIfSoftware(); + currentCursorImage.size = currentCursorImage.surface->resource()->current.buffer ? currentCursorImage.surface->resource()->current.buffer->size : Vector2D{}; + currentCursorImage.scale = currentCursorImage.surface ? currentCursorImage.surface->resource()->current.scale : 1.F; + recheckEnteredOutputs(); + updateCursorBackend(); + damageIfSoftware(); + }); - if (wlr_surface_has_buffer(surf->wlr())) { + if (surf->resource()->current.buffer) { + currentCursorImage.size = surf->resource()->current.buffer->size; timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(surf->wlr(), &now); + surf->resource()->frame(&now); } } @@ -264,9 +276,9 @@ void CPointerManager::recheckEnteredOutputs() { if (!currentCursorImage.surface) continue; - wlr_surface_send_enter(currentCursorImage.surface->wlr(), s->monitor->output); - PROTO::fractional->sendScale(currentCursorImage.surface->wlr(), s->monitor->scale); - g_pCompositor->setPreferredScaleForSurface(currentCursorImage.surface->wlr(), s->monitor->scale); + currentCursorImage.surface->resource()->enter(s->monitor.lock()); + PROTO::fractional->sendScale(currentCursorImage.surface->resource(), s->monitor->scale); + g_pCompositor->setPreferredScaleForSurface(currentCursorImage.surface->resource(), s->monitor->scale); } else if (s->entered && !overlaps) { s->entered = false; @@ -279,7 +291,7 @@ void CPointerManager::recheckEnteredOutputs() { if (!currentCursorImage.surface) continue; - wlr_surface_send_leave(currentCursorImage.surface->wlr(), s->monitor->output); + currentCursorImage.surface->resource()->leave(s->monitor.lock()); } } } @@ -289,12 +301,12 @@ void CPointerManager::resetCursorImage(bool apply) { if (currentCursorImage.surface) { for (auto& m : g_pCompositor->m_vMonitors) { - wlr_surface_send_leave(currentCursorImage.surface->wlr(), m->output); + currentCursorImage.surface->resource()->leave(m); } currentCursorImage.destroySurface.reset(); - currentCursorImage.hyprListener_commitSurface.removeCallback(); - currentCursorImage.surface = nullptr; + currentCursorImage.commitSurface.reset(); + currentCursorImage.surface.reset(); } else if (currentCursorImage.pBuffer) { wlr_buffer_unlock(currentCursorImage.pBuffer); currentCursorImage.hyprListener_destroyBuffer.removeCallback(); @@ -437,7 +449,7 @@ bool CPointerManager::setHWCursorBuffer(SP state, wlr_buff return true; } -wlr_buffer* CPointerManager::renderHWCursorBuffer(SP state, wlr_texture* texture) { +wlr_buffer* CPointerManager::renderHWCursorBuffer(SP state, SP texture) { auto output = state->monitor->output; int w = currentCursorImage.size.x, h = currentCursorImage.size.y; @@ -514,7 +526,7 @@ void CPointerManager::renderSoftwareCursorsFor(SP pMonitor, timespec* if ((!state->hardwareFailed && state->softwareLocks == 0)) { if (currentCursorImage.surface) - wlr_surface_send_frame_done(currentCursorImage.surface->wlr(), now); + currentCursorImage.surface->resource()->frame(now); return; } @@ -536,7 +548,7 @@ void CPointerManager::renderSoftwareCursorsFor(SP pMonitor, timespec* g_pHyprOpenGL->renderTextureWithDamage(texture, &box, &damage, 1.F); if (currentCursorImage.surface) - wlr_surface_send_frame_done(currentCursorImage.surface->wlr(), now); + currentCursorImage.surface->resource()->frame(now); } Vector2D CPointerManager::getCursorPosForMonitor(SP pMonitor) { @@ -550,7 +562,7 @@ Vector2D CPointerManager::transformedHotspot(SP pMonitor) { if (!pMonitor->output->cursor_swapchain) return {}; // doesn't matter, we have no hw cursor, and this is only for hw cursors - return CBox{currentCursorImage.hotspot, {0, 0}} + return CBox{currentCursorImage.hotspot * pMonitor->scale, {0, 0}} .transform(wlr_output_transform_invert(pMonitor->transform), pMonitor->output->cursor_swapchain->width, pMonitor->output->cursor_swapchain->height) .pos(); } @@ -560,7 +572,7 @@ CBox CPointerManager::getCursorBoxLogicalForMonitor(SP pMonitor) { } CBox CPointerManager::getCursorBoxGlobal() { - return CBox{pointerPos, currentCursorImage.size / currentCursorImage.scale}.translate(-currentCursorImage.hotspot / currentCursorImage.scale); + return CBox{pointerPos, currentCursorImage.size / currentCursorImage.scale}.translate(-currentCursorImage.hotspot); } Vector2D CPointerManager::closestValid(const Vector2D& pos) { @@ -696,8 +708,11 @@ void CPointerManager::warpAbsolute(Vector2D abs, SP dev) { } } - if (!TAB->boundBox.empty()) - mappedArea = TAB->boundBox.translate(currentMonitor->vecPosition); + mappedArea.translate(TAB->boundBox.pos()); + if (!TAB->boundBox.empty()) { + mappedArea.w = TAB->boundBox.w; + mappedArea.h = TAB->boundBox.h; + } break; } case HID_TYPE_TOUCH: { @@ -745,17 +760,19 @@ void CPointerManager::onMonitorLayoutChange() { damageIfSoftware(); } -wlr_texture* CPointerManager::getCurrentCursorTexture() { - if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !wlr_surface_get_texture(currentCursorImage.surface->wlr()))) +SP CPointerManager::getCurrentCursorTexture() { + if (!currentCursorImage.pBuffer && (!currentCursorImage.surface || !currentCursorImage.surface->resource()->current.buffer)) return nullptr; if (currentCursorImage.pBuffer) { - if (!currentCursorImage.pBufferTexture) + if (!currentCursorImage.pBufferTexture) { currentCursorImage.pBufferTexture = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, currentCursorImage.pBuffer); - return currentCursorImage.pBufferTexture; + currentCursorImage.bufferTex = makeShared(currentCursorImage.pBufferTexture); + } + return currentCursorImage.bufferTex; } - return wlr_surface_get_texture(currentCursorImage.surface->wlr()); + return currentCursorImage.surface->resource()->current.buffer->texture; } void CPointerManager::attachPointer(SP pointer) { diff --git a/src/managers/PointerManager.hpp b/src/managers/PointerManager.hpp index b6cb0c7a..b71a79c3 100644 --- a/src/managers/PointerManager.hpp +++ b/src/managers/PointerManager.hpp @@ -11,6 +11,7 @@ class CMonitor; struct wlr_input_device; class IHID; +class CTexture; /* The naming here is a bit confusing. @@ -37,7 +38,7 @@ class CPointerManager { void warpAbsolute(Vector2D abs, SP dev); void setCursorBuffer(wlr_buffer* buf, const Vector2D& hotspot, const float& scale); - void setCursorSurface(CWLSurface* buf, const Vector2D& hotspot); + void setCursorSurface(SP buf, const Vector2D& hotspot); void resetCursorImage(bool apply = true); void lockSoftwareForMonitor(SP pMonitor); @@ -76,7 +77,7 @@ class CPointerManager { Vector2D transformedHotspot(SP pMonitor); - wlr_texture* getCurrentCursorTexture(); + SP getCurrentCursorTexture(); struct SPointerListener { CHyprSignalListener destroy; @@ -129,8 +130,9 @@ class CPointerManager { } currentMonitorLayout; struct { - wlr_buffer* pBuffer = nullptr; - CWLSurface* surface = nullptr; + wlr_buffer* pBuffer = nullptr; + SP bufferTex; + WP surface; wlr_texture* pBufferTexture = nullptr; Vector2D hotspot; @@ -138,7 +140,7 @@ class CPointerManager { float scale = 1.F; CHyprSignalListener destroySurface; - DYNLISTENER(commitSurface); + CHyprSignalListener commitSurface; DYNLISTENER(destroyBuffer); } currentCursorImage; // TODO: support various sizes per-output so we can have pixel-perfect cursors @@ -146,6 +148,11 @@ class CPointerManager { struct SMonitorPointerState { SMonitorPointerState(SP m) : monitor(m) {} + ~SMonitorPointerState() { + if (cursorFrontBuffer) + wlr_buffer_unlock(cursorFrontBuffer); + } + WP monitor; int softwareLocks = 0; @@ -160,7 +167,7 @@ class CPointerManager { std::vector> monitorStates; SP stateFor(SP mon); bool attemptHardwareCursor(SP state); - wlr_buffer* renderHWCursorBuffer(SP state, wlr_texture* texture); + wlr_buffer* renderHWCursorBuffer(SP state, SP texture); bool setHWCursorBuffer(SP state, wlr_buffer* buf); struct { diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index c43e4c56..74cebd0c 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -31,17 +31,73 @@ #include "../protocols/XDGShell.hpp" #include "../protocols/DataDeviceWlr.hpp" #include "../protocols/PrimarySelection.hpp" +#include "../protocols/XWaylandShell.hpp" +#include "../protocols/Viewporter.hpp" +#include "../protocols/MesaDRM.hpp" +#include "../protocols/LinuxDMABUF.hpp" #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../protocols/core/Subcompositor.hpp" +#include "../protocols/core/Output.hpp" +#include "../protocols/core/Shm.hpp" + +#include "../helpers/Monitor.hpp" +#include "../render/Renderer.hpp" + +void CProtocolManager::onMonitorModeChange(CMonitor* pMonitor) { + const bool ISMIRROR = pMonitor->isMirror(); + + // onModeChanged we check if the current mirror status matches the global. + // mirrored outputs should have their global removed, as they are not physical parts of the + // layout. + + if (ISMIRROR && PROTO::outputs.contains(pMonitor->szName)) + PROTO::outputs.at(pMonitor->szName)->remove(); + else if (!ISMIRROR && (!PROTO::outputs.contains(pMonitor->szName) || PROTO::outputs.at(pMonitor->szName)->isDefunct())) { + if (PROTO::outputs.contains(pMonitor->szName)) + PROTO::outputs.erase(pMonitor->szName); + PROTO::outputs.emplace(pMonitor->szName, + std::make_unique(&wl_output_interface, 4, std::format("WLOutput ({})", pMonitor->szName), pMonitor->self.lock())); + } +} CProtocolManager::CProtocolManager() { + // Outputs are a bit dumb, we have to agree. + static auto P = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) { + auto M = std::any_cast(param); + + // ignore mirrored outputs. I don't think this will ever be hit as mirrors are applied after + // this event is emitted iirc. + if (M->isMirror()) + return; + + if (PROTO::outputs.contains(M->szName)) + PROTO::outputs.erase(M->szName); + PROTO::outputs.emplace(M->szName, std::make_unique(&wl_output_interface, 4, std::format("WLOutput ({})", M->szName), M->self.lock())); + + m_mModeChangeListeners[M->szName] = M->events.modeChanged.registerListener([M, this](std::any d) { onMonitorModeChange(M); }); + }); + + static auto P2 = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) { + auto M = std::any_cast(param); + if (!PROTO::outputs.contains(M->szName)) + return; + PROTO::outputs.at(M->szName)->remove(); + m_mModeChangeListeners.erase(M->szName); + }); + // Core - PROTO::seat = std::make_unique(&wl_seat_interface, 9, "WLSeat"); - PROTO::data = std::make_unique(&wl_data_device_manager_interface, 3, "WLDataDevice"); + PROTO::seat = std::make_unique(&wl_seat_interface, 9, "WLSeat"); + PROTO::data = std::make_unique(&wl_data_device_manager_interface, 3, "WLDataDevice"); + PROTO::compositor = std::make_unique(&wl_compositor_interface, 6, "WLCompositor"); + PROTO::subcompositor = std::make_unique(&wl_subcompositor_interface, 1, "WLSubcompositor"); + PROTO::shm = std::make_unique(&wl_shm_interface, 1, "WLSHM"); // Extensions + PROTO::viewport = std::make_unique(&wp_viewporter_interface, 1, "Viewporter"); PROTO::tearing = std::make_unique(&wp_tearing_control_manager_v1_interface, 1, "TearingControl"); PROTO::fractional = std::make_unique(&wp_fractional_scale_manager_v1_interface, 1, "FractionalScale"); PROTO::xdgOutput = std::make_unique(&zxdg_output_manager_v1_interface, 3, "XDGOutput"); @@ -73,6 +129,13 @@ CProtocolManager::CProtocolManager() { PROTO::xdgShell = std::make_unique(&xdg_wm_base_interface, 6, "XDGShell"); PROTO::dataWlr = std::make_unique(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr"); PROTO::primarySelection = std::make_unique(&zwp_primary_selection_device_manager_v1_interface, 1, "PrimarySelection"); + PROTO::xwaylandShell = std::make_unique(&xwayland_shell_v1_interface, 1, "XWaylandShell"); + + if (g_pHyprOpenGL->getDRMFormats().size() > 0) { + PROTO::mesaDRM = std::make_unique(&wl_drm_interface, 2, "MesaDRM"); + PROTO::linuxDma = std::make_unique(&zwp_linux_dmabuf_v1_interface, 5, "LinuxDMABUF"); + } else + Debug::log(WARN, "ProtocolManager: Not binding linux-dmabuf and MesaDRM: DMABUF not available"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/ProtocolManager.hpp b/src/managers/ProtocolManager.hpp index 9af7b928..52a54253 100644 --- a/src/managers/ProtocolManager.hpp +++ b/src/managers/ProtocolManager.hpp @@ -5,6 +5,9 @@ #include "../protocols/TextInputV1.hpp" #include "../protocols/GlobalShortcuts.hpp" #include "../protocols/Screencopy.hpp" +#include "../helpers/memory/WeakPtr.hpp" +#include "../helpers/signal/Listener.hpp" +#include class CProtocolManager { public: @@ -15,6 +18,11 @@ class CProtocolManager { std::unique_ptr m_pTextInputV1ProtocolManager; std::unique_ptr m_pGlobalShortcutsProtocolManager; std::unique_ptr m_pScreencopyProtocolManager; + + private: + std::unordered_map m_mModeChangeListeners; + + void onMonitorModeChange(CMonitor* pMonitor); }; inline std::unique_ptr g_pProtocolManager; diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index bbbe01d4..862bd2b5 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -3,6 +3,7 @@ #include "../protocols/core/DataDevice.hpp" #include "../protocols/DataDeviceWlr.hpp" #include "../protocols/PrimarySelection.hpp" +#include "../protocols/core/Compositor.hpp" #include "../Compositor.hpp" #include "../devices/IKeyboard.hpp" #include @@ -98,7 +99,7 @@ void CSeatManager::updateActiveKeyboardData() { PROTO::seat->updateKeymap(); } -void CSeatManager::setKeyboardFocus(wlr_surface* surf) { +void CSeatManager::setKeyboardFocus(SP surf) { if (state.keyboardFocus == surf) return; @@ -107,15 +108,9 @@ void CSeatManager::setKeyboardFocus(wlr_surface* surf) { return; } - hyprListener_keyboardSurfaceDestroy.removeCallback(); + listeners.keyboardSurfaceDestroy.reset(); if (state.keyboardFocusResource) { - // we will iterate over all bound wl_seat - // resources here, because some idiotic apps (e.g. those based on smithay) - // tend to bind wl_seat twice. - // I can't be arsed to actually pass all events to all seat resources, so we will - // only pass enter and leave. - // If you have an issue with that, fix your app. auto client = state.keyboardFocusResource->client(); for (auto& s : seatResources) { if (s->resource->client() != client) @@ -138,7 +133,7 @@ void CSeatManager::setKeyboardFocus(wlr_surface* surf) { return; } - auto client = wl_resource_get_client(surf->resource); + auto client = surf->client(); for (auto& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; @@ -153,8 +148,7 @@ void CSeatManager::setKeyboardFocus(wlr_surface* surf) { } } - hyprListener_keyboardSurfaceDestroy.initCallback( - &surf->events.destroy, [this](void* owner, void* data) { setKeyboardFocus(nullptr); }, nullptr, "CSeatManager"); + listeners.keyboardSurfaceDestroy = surf->events.destroy.registerListener([this](std::any d) { setKeyboardFocus(nullptr); }); events.keyboardFocusChange.emit(); } @@ -163,11 +157,16 @@ void CSeatManager::sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_ke if (!state.keyboardFocusResource) return; - for (auto& k : state.keyboardFocusResource->keyboards) { - if (!k) + for (auto& s : seatResources) { + if (s->resource->client() != state.keyboardFocusResource->client()) continue; - k->sendKey(timeMs, key, state_); + for (auto& k : s->resource->keyboards) { + if (!k) + continue; + + k->sendKey(timeMs, key, state_); + } } } @@ -175,15 +174,20 @@ void CSeatManager::sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32 if (!state.keyboardFocusResource) return; - for (auto& k : state.keyboardFocusResource->keyboards) { - if (!k) + for (auto& s : seatResources) { + if (s->resource->client() != state.keyboardFocusResource->client()) continue; - k->sendMods(depressed, latched, locked, group); + for (auto& k : s->resource->keyboards) { + if (!k) + continue; + + k->sendMods(depressed, latched, locked, group); + } } } -void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { +void CSeatManager::setPointerFocus(SP surf, const Vector2D& local) { if (state.pointerFocus == surf) return; @@ -192,7 +196,7 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { return; } - hyprListener_pointerSurfaceDestroy.removeCallback(); + listeners.pointerSurfaceDestroy.reset(); if (state.pointerFocusResource) { auto client = state.pointerFocusResource->client(); @@ -220,7 +224,7 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { return; } - auto client = wl_resource_get_client(surf->resource); + auto client = surf->client(); for (auto& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; @@ -239,8 +243,7 @@ void CSeatManager::setPointerFocus(wlr_surface* surf, const Vector2D& local) { sendPointerFrame(); - hyprListener_pointerSurfaceDestroy.initCallback( - &surf->events.destroy, [this](void* owner, void* data) { setPointerFocus(nullptr, {}); }, nullptr, "CSeatManager"); + listeners.pointerSurfaceDestroy = surf->events.destroy.registerListener([this](std::any d) { setPointerFocus(nullptr, {}); }); events.pointerFocusChange.emit(); } @@ -249,11 +252,16 @@ void CSeatManager::sendPointerMotion(uint32_t timeMs, const Vector2D& local) { if (!state.pointerFocusResource) return; - for (auto& p : state.pointerFocusResource->pointers) { - if (!p) + for (auto& s : seatResources) { + if (s->resource->client() != state.pointerFocusResource->client()) continue; - p->sendMotion(timeMs, local); + for (auto& p : s->resource->pointers) { + if (!p) + continue; + + p->sendMotion(timeMs, local); + } } lastLocalCoords = local; @@ -263,11 +271,16 @@ void CSeatManager::sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_b if (!state.pointerFocusResource) return; - for (auto& p : state.pointerFocusResource->pointers) { - if (!p) + for (auto& s : seatResources) { + if (s->resource->client() != state.pointerFocusResource->client()) continue; - p->sendButton(timeMs, key, state_); + for (auto& p : s->resource->pointers) { + if (!p) + continue; + + p->sendButton(timeMs, key, state_); + } } } @@ -282,40 +295,52 @@ void CSeatManager::sendPointerFrame(WP pResource) { if (!pResource) return; - for (auto& p : pResource->pointers) { - if (!p) + for (auto& s : seatResources) { + if (s->resource->client() != pResource->client()) continue; - p->sendFrame(); + for (auto& p : s->resource->pointers) { + if (!p) + continue; + + p->sendFrame(); + } } } -void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source, +void CSeatManager::sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, int32_t value120, wl_pointer_axis_source source, wl_pointer_axis_relative_direction relative) { if (!state.pointerFocusResource) return; - for (auto& p : state.pointerFocusResource->pointers) { - if (!p) + for (auto& s : seatResources) { + if (s->resource->client() != state.pointerFocusResource->client()) continue; - p->sendAxis(timeMs, axis, value); - p->sendAxisSource(source); - p->sendAxisRelativeDirection(axis, relative); + for (auto& p : s->resource->pointers) { + if (!p) + continue; - if (source == 0) - p->sendAxisDiscrete(axis, discrete); + p->sendAxis(timeMs, axis, value); + p->sendAxisSource(source); + p->sendAxisRelativeDirection(axis, relative); - if (value == 0) - p->sendAxisStop(timeMs, axis); + if (source == 0) { + p->sendAxisValue120(axis, value120); + p->sendAxisDiscrete(axis, discrete); + } + + if (value == 0) + p->sendAxisStop(timeMs, axis); + } } } -void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local) { +void CSeatManager::sendTouchDown(SP surf, uint32_t timeMs, int32_t id, const Vector2D& local) { if (state.touchFocus == surf) return; - hyprListener_touchSurfaceDestroy.removeCallback(); + listeners.touchSurfaceDestroy.reset(); if (state.touchFocusResource) { auto client = state.touchFocusResource->client(); @@ -340,7 +365,7 @@ void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, return; } - auto client = wl_resource_get_client(surf->resource); + auto client = surf->client(); for (auto& r : seatResources | std::views::reverse) { if (r->resource->client() != client) continue; @@ -354,8 +379,7 @@ void CSeatManager::sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, } } - hyprListener_touchSurfaceDestroy.initCallback( - &surf->events.destroy, [this, timeMs, id](void* owner, void* data) { sendTouchUp(timeMs + 10, id); }, nullptr, "CSeatManager"); + listeners.touchSurfaceDestroy = surf->events.destroy.registerListener([this, timeMs, id](std::any d) { sendTouchUp(timeMs + 10, id); }); events.touchFocusChange.emit(); } @@ -368,11 +392,16 @@ void CSeatManager::sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& if (!state.touchFocusResource) return; - for (auto& t : state.touchFocusResource->touches) { - if (!t) + for (auto& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - t->sendMotion(timeMs, id, local); + for (auto& t : s->resource->touches) { + if (!t) + continue; + + t->sendMotion(timeMs, id, local); + } } } @@ -380,11 +409,16 @@ void CSeatManager::sendTouchFrame() { if (!state.touchFocusResource) return; - for (auto& t : state.touchFocusResource->touches) { - if (!t) + for (auto& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - t->sendFrame(); + for (auto& t : s->resource->touches) { + if (!t) + continue; + + t->sendFrame(); + } } } @@ -392,11 +426,16 @@ void CSeatManager::sendTouchCancel() { if (!state.touchFocusResource) return; - for (auto& t : state.touchFocusResource->touches) { - if (!t) + for (auto& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - t->sendCancel(); + for (auto& t : s->resource->touches) { + if (!t) + continue; + + t->sendCancel(); + } } } @@ -404,11 +443,16 @@ void CSeatManager::sendTouchShape(int32_t id, const Vector2D& shape) { if (!state.touchFocusResource) return; - for (auto& t : state.touchFocusResource->touches) { - if (!t) + for (auto& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - t->sendShape(id, shape); + for (auto& t : s->resource->touches) { + if (!t) + continue; + + t->sendShape(id, shape); + } } } @@ -416,11 +460,16 @@ void CSeatManager::sendTouchOrientation(int32_t id, double angle) { if (!state.touchFocusResource) return; - for (auto& t : state.touchFocusResource->touches) { - if (!t) + for (auto& s : seatResources) { + if (s->resource->client() != state.touchFocusResource->client()) continue; - t->sendOrientation(id, angle); + for (auto& t : s->resource->touches) { + if (!t) + continue; + + t->sendOrientation(id, angle); + } } } @@ -432,7 +481,7 @@ void CSeatManager::refocusGrab() { // try to find a surf in focus first const auto MOUSE = g_pInputManager->getMouseCoordsInternal(); for (auto& s : seatGrab->surfs) { - auto hlSurf = CWLSurface::surfaceFromWlr(s); + auto hlSurf = CWLSurface::fromResource(s.lock()); if (!hlSurf) continue; @@ -444,13 +493,13 @@ void CSeatManager::refocusGrab() { continue; if (seatGrab->keyboard) - setKeyboardFocus(s); + setKeyboardFocus(s.lock()); if (seatGrab->pointer) - setPointerFocus(s, MOUSE - b->pos()); + setPointerFocus(s.lock(), MOUSE - b->pos()); return; } - wlr_surface* surf = seatGrab->surfs.at(0); + SP surf = seatGrab->surfs.at(0).lock(); if (seatGrab->keyboard) setKeyboardFocus(surf); if (seatGrab->pointer) @@ -458,7 +507,7 @@ void CSeatManager::refocusGrab() { } } -void CSeatManager::onSetCursor(SP seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot) { +void CSeatManager::onSetCursor(SP seatResource, uint32_t serial, SP surf, const Vector2D& hotspot) { if (!state.pointerFocusResource || !seatResource || seatResource->client() != state.pointerFocusResource->client()) { Debug::log(LOG, "[seatmgr] Rejecting a setCursor because the client ain't in focus"); return; @@ -498,6 +547,8 @@ void CSeatManager::setCurrentSelection(SP source) { PROTO::data->setSelection(source); PROTO::dataWlr->setSelection(source, false); } + + events.setSelection.emit(); } void CSeatManager::setCurrentPrimarySelection(SP source) { @@ -521,6 +572,8 @@ void CSeatManager::setCurrentPrimarySelection(SP source) { PROTO::primarySelection->setSelection(source); PROTO::dataWlr->setSelection(source, true); } + + events.setPrimarySelection.emit(); } void CSeatManager::setGrab(SP grab) { @@ -541,10 +594,10 @@ void CSeatManager::setGrab(SP grab) { } void CSeatManager::resendEnterEvents() { - wlr_surface* kb = state.keyboardFocus; - wlr_surface* pt = state.pointerFocus; + SP kb = state.keyboardFocus.lock(); + SP pt = state.pointerFocus.lock(); - auto last = lastLocalCoords; + auto last = lastLocalCoords; setKeyboardFocus(nullptr); setPointerFocus(nullptr, {}); @@ -553,15 +606,15 @@ void CSeatManager::resendEnterEvents() { setPointerFocus(pt, last); } -bool CSeatGrab::accepts(wlr_surface* surf) { +bool CSeatGrab::accepts(SP surf) { return std::find(surfs.begin(), surfs.end(), surf) != surfs.end(); } -void CSeatGrab::add(wlr_surface* surf) { +void CSeatGrab::add(SP surf) { surfs.push_back(surf); } -void CSeatGrab::remove(wlr_surface* surf) { +void CSeatGrab::remove(SP surf) { std::erase(surfs, surf); if ((keyboard && g_pSeatManager->state.keyboardFocus == surf) || (pointer && g_pSeatManager->state.pointerFocus == surf)) g_pSeatManager->refocusGrab(); diff --git a/src/managers/SeatManager.hpp b/src/managers/SeatManager.hpp index fee3efa9..e74d9ace 100644 --- a/src/managers/SeatManager.hpp +++ b/src/managers/SeatManager.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "../helpers/WLListener.hpp" #include "../macros.hpp" @@ -10,7 +11,7 @@ constexpr size_t MAX_SERIAL_STORE_LEN = 100; -struct wlr_surface; +class CWLSurfaceResource; class CWLSeatResource; class IPointer; class IKeyboard; @@ -28,9 +29,9 @@ class IKeyboard; */ class CSeatGrab { public: - bool accepts(wlr_surface* surf); - void add(wlr_surface* surf); - void remove(wlr_surface* surf); + bool accepts(SP surf); + void add(SP surf); + void remove(SP surf); void setCallback(std::function onEnd_); void clear(); @@ -41,8 +42,8 @@ class CSeatGrab { bool removeOnInput = true; // on hard input e.g. click outside, remove private: - std::vector surfs; // read-only - std::function onEnd; + std::vector> surfs; + std::function onEnd; friend class CSeatManager; }; @@ -56,18 +57,19 @@ class CSeatManager { void setKeyboard(SP keeb); void updateActiveKeyboardData(); // updates the clients with the keymap and repeat info - void setKeyboardFocus(wlr_surface* surf); + void setKeyboardFocus(SP surf); void sendKeyboardKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); void sendKeyboardMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); - void setPointerFocus(wlr_surface* surf, const Vector2D& local); + void setPointerFocus(SP surf, const Vector2D& local); void sendPointerMotion(uint32_t timeMs, const Vector2D& local); void sendPointerButton(uint32_t timeMs, uint32_t key, wl_pointer_button_state state); void sendPointerFrame(); void sendPointerFrame(WP pResource); - void sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, wl_pointer_axis_source source, wl_pointer_axis_relative_direction relative); + void sendPointerAxis(uint32_t timeMs, wl_pointer_axis axis, double value, int32_t discrete, int32_t value120, wl_pointer_axis_source source, + wl_pointer_axis_relative_direction relative); - void sendTouchDown(wlr_surface* surf, uint32_t timeMs, int32_t id, const Vector2D& local); + void sendTouchDown(SP surf, uint32_t timeMs, int32_t id, const Vector2D& local); void sendTouchUp(uint32_t timeMs, int32_t id); void sendTouchMotion(uint32_t timeMs, int32_t id, const Vector2D& local); void sendTouchFrame(); @@ -81,24 +83,24 @@ class CSeatManager { // pops the serial if it was valid, meaning it is consumed. bool serialValid(SP seatResource, uint32_t serial); - void onSetCursor(SP seatResource, uint32_t serial, wlr_surface* surf, const Vector2D& hotspot); + void onSetCursor(SP seatResource, uint32_t serial, SP surf, const Vector2D& hotspot); SP seatResourceForClient(wl_client* client); struct { - wlr_surface* keyboardFocus = nullptr; - WP keyboardFocusResource; + WP keyboardFocus; + WP keyboardFocusResource; - wlr_surface* pointerFocus = nullptr; - WP pointerFocusResource; + WP pointerFocus; + WP pointerFocusResource; - wlr_surface* touchFocus = nullptr; - WP touchFocusResource; + WP touchFocus; + WP touchFocusResource; } state; struct SSetCursorEvent { - wlr_surface* surf = nullptr; - Vector2D hotspot; + SP surf = nullptr; + Vector2D hotspot; }; struct { @@ -106,6 +108,8 @@ class CSeatManager { CSignal pointerFocusChange; CSignal touchFocusChange; CSignal setCursor; // SSetCursorEvent + CSignal setSelection; + CSignal setPrimarySelection; } events; struct { @@ -145,14 +149,13 @@ class CSeatManager { struct { CHyprSignalListener newSeatResource; + CHyprSignalListener keyboardSurfaceDestroy; + CHyprSignalListener pointerSurfaceDestroy; + CHyprSignalListener touchSurfaceDestroy; } listeners; Vector2D lastLocalCoords; - DYNLISTENER(keyboardSurfaceDestroy); - DYNLISTENER(pointerSurfaceDestroy); - DYNLISTENER(touchSurfaceDestroy); - friend struct SSeatResourceContainer; friend class CSeatGrab; }; diff --git a/src/managers/SessionLockManager.cpp b/src/managers/SessionLockManager.cpp index fd4841f4..3bec1c4b 100644 --- a/src/managers/SessionLockManager.cpp +++ b/src/managers/SessionLockManager.cpp @@ -20,7 +20,7 @@ SSessionLockSurface::SSessionLockSurface(SP surface_) : sur listeners.destroy = surface_->events.destroy.registerListener([this](std::any data) { if (pWlrSurface == g_pCompositor->m_pLastFocus) - g_pCompositor->m_pLastFocus = nullptr; + g_pCompositor->m_pLastFocus.reset(); g_pSessionLockManager->removeSessionLockSurface(this); }); @@ -70,7 +70,7 @@ void CSessionLockManager::onNewSessionLock(SP pLock) { g_pHyprRenderer->damageMonitor(m.get()); }); - m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([this](std::any data) { + m_pSessionLock->listeners.destroy = pLock->events.destroyed.registerListener([](std::any data) { g_pCompositor->focusSurface(nullptr); for (auto& m : g_pCompositor->m_vMonitors) @@ -117,7 +117,7 @@ float CSessionLockManager::getRedScreenAlphaForMonitor(uint64_t id) { return std::clamp(NOMAPPEDSURFACETIMER->second.getSeconds() - /* delay for screencopy */ 0.5f, 0.f, 1.f); } -bool CSessionLockManager::isSurfaceSessionLock(wlr_surface* pSurface) { +bool CSessionLockManager::isSurfaceSessionLock(SP pSurface) { // TODO: this has some edge cases when it's wrong (e.g. destroyed lock but not yet surfaces) // but can be easily fixed when I rewrite wlr_surface diff --git a/src/managers/SessionLockManager.hpp b/src/managers/SessionLockManager.hpp index d655a2bc..ea1b2029 100644 --- a/src/managers/SessionLockManager.hpp +++ b/src/managers/SessionLockManager.hpp @@ -8,13 +8,14 @@ class CSessionLockSurface; class CSessionLock; +class CWLSurfaceResource; struct SSessionLockSurface { SSessionLockSurface(SP surface_); WP surface; - wlr_surface* pWlrSurface = nullptr; - uint64_t iMonitorID = -1; + WP pWlrSurface; + uint64_t iMonitorID = -1; bool mapped = false; @@ -49,7 +50,7 @@ class CSessionLockManager { bool isSessionLocked(); bool isSessionLockPresent(); - bool isSurfaceSessionLock(wlr_surface*); + bool isSurfaceSessionLock(SP); void removeSessionLockSurface(SSessionLockSurface*); diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index 390c6ece..12387900 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -3,29 +3,15 @@ #include "../events/Events.hpp" #include "../config/ConfigValue.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../xwayland/XWayland.hpp" #define OUTPUT_MANAGER_VERSION 3 #define OUTPUT_DONE_DEPRECATED_SINCE_VERSION 3 #define OUTPUT_DESCRIPTION_MUTABLE_SINCE_VERSION 3 CHyprXWaylandManager::CHyprXWaylandManager() { -#ifndef NO_XWAYLAND - m_sWLRXWayland = wlr_xwayland_create(g_pCompositor->m_sWLDisplay, g_pCompositor->m_sWLRCompositor, 1); - - if (!m_sWLRXWayland) { - Debug::log(ERR, "Couldn't start up the XWaylandManager because wlr_xwayland_create returned a nullptr!"); - return; - } - - addWLSignal(&m_sWLRXWayland->events.ready, &Events::listen_readyXWayland, m_sWLRXWayland, "XWayland Manager"); - addWLSignal(&m_sWLRXWayland->events.new_surface, &Events::listen_surfaceXWayland, m_sWLRXWayland, "XWayland Manager"); - - setenv("DISPLAY", m_sWLRXWayland->display_name, 1); - - Debug::log(LOG, "CHyprXWaylandManager started on display {}", m_sWLRXWayland->display_name); -#else - unsetenv("DISPLAY"); // unset DISPLAY so that X11 apps do not try to start on a different/invalid DISPLAY -#endif + ; } CHyprXWaylandManager::~CHyprXWaylandManager() { @@ -34,32 +20,31 @@ CHyprXWaylandManager::~CHyprXWaylandManager() { #endif } -wlr_surface* CHyprXWaylandManager::getWindowSurface(PHLWINDOW pWindow) { - return pWindow->m_pWLSurface.wlr(); +SP CHyprXWaylandManager::getWindowSurface(PHLWINDOW pWindow) { + return pWindow->m_pWLSurface->resource(); } -void CHyprXWaylandManager::activateSurface(wlr_surface* pSurface, bool activate) { +void CHyprXWaylandManager::activateSurface(SP pSurface, bool activate) { if (!pSurface) return; - if (wlr_xwayland_surface_try_from_wlr_surface(pSurface)) { - const auto XSURF = wlr_xwayland_surface_try_from_wlr_surface(pSurface); - wlr_xwayland_surface_activate(XSURF, activate); - - if (activate && !XSURF->override_redirect) - wlr_xwayland_surface_restack(XSURF, nullptr, XCB_STACK_MODE_ABOVE); - } - // TODO: // this cannot be nicely done until we rewrite wlr_surface for (auto& w : g_pCompositor->m_vWindows) { - if (w->m_bIsX11 || !w->m_bIsMapped) + if (!w->m_bIsMapped) continue; - if (w->m_pWLSurface.wlr() != pSurface) + if (w->m_pWLSurface->resource() != pSurface) continue; - w->m_pXDGSurface->toplevel->setActive(activate); + if (w->m_bIsX11) { + if (activate) { + w->m_pXWaylandSurface->setMinimized(false); + w->m_pXWaylandSurface->restackToTop(); + } + w->m_pXWaylandSurface->activate(activate); + } else + w->m_pXDGSurface->toplevel->setActive(activate); } } @@ -68,12 +53,12 @@ void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { setWindowSize(pWindow, pWindow->m_vRealSize.value()); // update xwayland output pos if (activate) { - wlr_xwayland_surface_set_minimized(pWindow->m_uSurface.xwayland, false); - if (!pWindow->m_uSurface.xwayland->override_redirect) - wlr_xwayland_surface_restack(pWindow->m_uSurface.xwayland, nullptr, XCB_STACK_MODE_ABOVE); + pWindow->m_pXWaylandSurface->setMinimized(false); + if (pWindow->m_iX11Type != 2) + pWindow->m_pXWaylandSurface->restackToTop(); } - wlr_xwayland_surface_activate(pWindow->m_uSurface.xwayland, activate); + pWindow->m_pXWaylandSurface->activate(activate); } else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) pWindow->m_pXDGSurface->toplevel->setActive(activate); @@ -88,7 +73,7 @@ void CHyprXWaylandManager::activateWindow(PHLWINDOW pWindow, bool activate) { void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) { if (pWindow->m_bIsX11) { - const auto SIZEHINTS = pWindow->m_uSurface.xwayland->size_hints; + const auto SIZEHINTS = pWindow->m_pXWaylandSurface->sizeHints.get(); if (SIZEHINTS && pWindow->m_iX11Type != 2) { pbox->x = SIZEHINTS->x; @@ -96,10 +81,7 @@ void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) { pbox->width = SIZEHINTS->width; pbox->height = SIZEHINTS->height; } else { - pbox->x = pWindow->m_uSurface.xwayland->x; - pbox->y = pWindow->m_uSurface.xwayland->y; - pbox->width = pWindow->m_uSurface.xwayland->width; - pbox->height = pWindow->m_uSurface.xwayland->height; + *pbox = pWindow->m_pXWaylandSurface->geometry; } } else if (pWindow->m_pXDGSurface) *pbox = pWindow->m_pXDGSurface->current.geometry; @@ -107,7 +89,7 @@ void CHyprXWaylandManager::getGeometryForWindow(PHLWINDOW pWindow, CBox* pbox) { void CHyprXWaylandManager::sendCloseWindow(PHLWINDOW pWindow) { if (pWindow->m_bIsX11) - wlr_xwayland_surface_close(pWindow->m_uSurface.xwayland); + pWindow->m_pXWaylandSurface->close(); else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) pWindow->m_pXDGSurface->toplevel->close(); } @@ -145,56 +127,43 @@ void CHyprXWaylandManager::setWindowSize(PHLWINDOW pWindow, Vector2D size, bool } if (pWindow->m_bIsX11) - wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, windowPos.x, windowPos.y, size.x, size.y); + pWindow->m_pXWaylandSurface->configure({windowPos, size}); else if (pWindow->m_pXDGSurface->toplevel) pWindow->m_vPendingSizeAcks.push_back(std::make_pair<>(pWindow->m_pXDGSurface->toplevel->setSize(size), size.floor())); } -wlr_surface* CHyprXWaylandManager::surfaceAt(PHLWINDOW pWindow, const Vector2D& client, Vector2D& surface) { - return wlr_surface_surface_at(pWindow->m_pWLSurface.wlr(), client.x, client.y, &surface.x, &surface.y); -} - bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { if (pWindow->m_bIsX11) { - for (size_t i = 0; i < pWindow->m_uSurface.xwayland->window_type_len; i++) - if (pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DIALOG"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLBAR"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_UTILITY"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DOCK"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"]) { + for (auto& a : pWindow->m_pXWaylandSurface->atoms) + if (a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DIALOG"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLBAR"] || + a == HYPRATOMS["_NET_WM_WINDOW_TYPE_UTILITY"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] || + a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DOCK"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] || + a == HYPRATOMS["_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"]) { - if (pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"]) + if (a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"]) pWindow->m_bX11ShouldntFocus = true; pWindow->m_bNoInitialFocus = true; return true; } - if (pWindow->m_uSurface.xwayland->role) { - try { - std::string winrole = std::string(pWindow->m_uSurface.xwayland->role); - if (winrole.contains("pop-up") || winrole.contains("task_dialog")) { - return true; - } - } catch (std::exception& e) { Debug::log(ERR, "Error in shouldBeFloated, winrole threw {}", e.what()); } - } - - if (pWindow->m_uSurface.xwayland->modal) { + if (pWindow->m_pXWaylandSurface->modal) { pWindow->m_bIsModal = true; return true; } - if (pWindow->m_iX11Type == 2) - return true; // override_redirect + if (pWindow->m_pXWaylandSurface->transient) + return true; - const auto SIZEHINTS = pWindow->m_uSurface.xwayland->size_hints; - if (SIZEHINTS && (pWindow->m_uSurface.xwayland->parent || ((SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height)))) + if (pWindow->m_pXWaylandSurface->role.contains("task_dialog") || pWindow->m_pXWaylandSurface->role.contains("pop-up")) + return true; + + if (pWindow->m_pXWaylandSurface->overrideRedirect) + return true; + + const auto SIZEHINTS = pWindow->m_pXWaylandSurface->sizeHints.get(); + if (pWindow->m_pXWaylandSurface->transient || pWindow->m_pXWaylandSurface->parent || + (SIZEHINTS && (SIZEHINTS->min_width == SIZEHINTS->max_width) && (SIZEHINTS->min_height == SIZEHINTS->max_height))) return true; } else { const auto PSTATE = pending ? &pWindow->m_pXDGSurface->toplevel->pending : &pWindow->m_pXDGSurface->toplevel->current; @@ -207,28 +176,14 @@ bool CHyprXWaylandManager::shouldBeFloated(PHLWINDOW pWindow, bool pending) { return false; } -void CHyprXWaylandManager::moveXWaylandWindow(PHLWINDOW pWindow, const Vector2D& pos) { - if (!validMapped(pWindow)) - return; - - if (!pWindow->m_bIsX11) - return; - - wlr_xwayland_surface_configure(pWindow->m_uSurface.xwayland, pos.x, pos.y, pWindow->m_vRealSize.value().x, pWindow->m_vRealSize.value().y); -} - void CHyprXWaylandManager::checkBorders(PHLWINDOW pWindow) { if (!pWindow->m_bIsX11) return; - for (size_t i = 0; i < pWindow->m_uSurface.xwayland->window_type_len; i++) { - if (pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_NOTIFICATION"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_COMBO"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"] || - pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"]) { + for (auto& a : pWindow->m_pXWaylandSurface->atoms) { + if (a == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_NOTIFICATION"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] || + a == HYPRATOMS["_NET_WM_WINDOW_TYPE_COMBO"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] || a == HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"] || + a == HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"]) { pWindow->m_bX11DoesntWantBorders = true; return; @@ -242,7 +197,7 @@ void CHyprXWaylandManager::checkBorders(PHLWINDOW pWindow) { void CHyprXWaylandManager::setWindowFullscreen(PHLWINDOW pWindow, bool fullscreen) { if (pWindow->m_bIsX11) - wlr_xwayland_surface_set_fullscreen(pWindow->m_uSurface.xwayland, fullscreen); + pWindow->m_pXWaylandSurface->setFullscreen(fullscreen); else if (pWindow->m_pXDGSurface && pWindow->m_pXDGSurface->toplevel) pWindow->m_pXDGSurface->toplevel->setFullscreen(fullscreen); } @@ -251,10 +206,10 @@ Vector2D CHyprXWaylandManager::getMaxSizeForWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return Vector2D(99999, 99999); - if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel) || pWindow->m_sAdditionalConfigData.noMaxSize) + if ((pWindow->m_bIsX11 && !pWindow->m_pXWaylandSurface->sizeHints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel) || pWindow->m_sAdditionalConfigData.noMaxSize) return Vector2D(99999, 99999); - auto MAXSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->max_width, pWindow->m_uSurface.xwayland->size_hints->max_height) : + auto MAXSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_pXWaylandSurface->sizeHints->max_width, pWindow->m_pXWaylandSurface->sizeHints->max_height) : pWindow->m_pXDGSurface->toplevel->current.maxSize; if (MAXSIZE.x < 5) @@ -269,10 +224,10 @@ Vector2D CHyprXWaylandManager::getMinSizeForWindow(PHLWINDOW pWindow) { if (!validMapped(pWindow)) return Vector2D(0, 0); - if ((pWindow->m_bIsX11 && !pWindow->m_uSurface.xwayland->size_hints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel)) + if ((pWindow->m_bIsX11 && !pWindow->m_pXWaylandSurface->sizeHints) || (!pWindow->m_bIsX11 && !pWindow->m_pXDGSurface->toplevel)) return Vector2D(0, 0); - auto MINSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_uSurface.xwayland->size_hints->min_width, pWindow->m_uSurface.xwayland->size_hints->min_height) : + auto MINSIZE = pWindow->m_bIsX11 ? Vector2D(pWindow->m_pXWaylandSurface->sizeHints->min_width, pWindow->m_pXWaylandSurface->sizeHints->min_height) : pWindow->m_pXDGSurface->toplevel->current.minSize; MINSIZE = MINSIZE.clamp({1, 1}); diff --git a/src/managers/XWaylandManager.hpp b/src/managers/XWaylandManager.hpp index 8889eb61..a9f95974 100644 --- a/src/managers/XWaylandManager.hpp +++ b/src/managers/XWaylandManager.hpp @@ -5,28 +5,25 @@ class CWindow; // because clangd typedef SP PHLWINDOW; +class CWLSurfaceResource; class CHyprXWaylandManager { public: CHyprXWaylandManager(); ~CHyprXWaylandManager(); - wlr_xwayland* m_sWLRXWayland = nullptr; - - wlr_surface* getWindowSurface(PHLWINDOW); - void activateSurface(wlr_surface*, bool); - void activateWindow(PHLWINDOW, bool); - void getGeometryForWindow(PHLWINDOW, CBox*); - void sendCloseWindow(PHLWINDOW); - void setWindowSize(PHLWINDOW, Vector2D, bool force = false); - void setWindowFullscreen(PHLWINDOW, bool); - wlr_surface* surfaceAt(PHLWINDOW, const Vector2D&, Vector2D&); - bool shouldBeFloated(PHLWINDOW, bool pending = false); - void moveXWaylandWindow(PHLWINDOW, const Vector2D&); - void checkBorders(PHLWINDOW); - Vector2D getMaxSizeForWindow(PHLWINDOW); - Vector2D getMinSizeForWindow(PHLWINDOW); - Vector2D xwaylandToWaylandCoords(const Vector2D&); + SP getWindowSurface(PHLWINDOW); + void activateSurface(SP, bool); + void activateWindow(PHLWINDOW, bool); + void getGeometryForWindow(PHLWINDOW, CBox*); + void sendCloseWindow(PHLWINDOW); + void setWindowSize(PHLWINDOW, Vector2D, bool force = false); + void setWindowFullscreen(PHLWINDOW, bool); + bool shouldBeFloated(PHLWINDOW, bool pending = false); + void checkBorders(PHLWINDOW); + Vector2D getMaxSizeForWindow(PHLWINDOW); + Vector2D getMinSizeForWindow(PHLWINDOW); + Vector2D xwaylandToWaylandCoords(const Vector2D&); }; inline std::unique_ptr g_pXWaylandManager; \ No newline at end of file diff --git a/src/managers/eventLoop/EventLoopManager.cpp b/src/managers/eventLoop/EventLoopManager.cpp index 5910e71a..1193ffb8 100644 --- a/src/managers/eventLoop/EventLoopManager.cpp +++ b/src/managers/eventLoop/EventLoopManager.cpp @@ -13,6 +13,11 @@ CEventLoopManager::CEventLoopManager() { m_sTimers.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); } +CEventLoopManager::~CEventLoopManager() { + if (m_sWayland.eventSource) + wl_event_source_remove(m_sWayland.eventSource); +} + static int timerWrite(int fd, uint32_t mask, void* data) { g_pEventLoopManager->onTimerFire(); return 1; @@ -22,7 +27,7 @@ void CEventLoopManager::enterLoop(wl_display* display, wl_event_loop* wlEventLoo m_sWayland.loop = wlEventLoop; m_sWayland.display = display; - wl_event_loop_add_fd(wlEventLoop, m_sTimers.timerfd, WL_EVENT_READABLE, timerWrite, nullptr); + m_sWayland.eventSource = wl_event_loop_add_fd(wlEventLoop, m_sTimers.timerfd, WL_EVENT_READABLE, timerWrite, nullptr); wl_display_run(display); diff --git a/src/managers/eventLoop/EventLoopManager.hpp b/src/managers/eventLoop/EventLoopManager.hpp index f2ba61a4..7a4fa19e 100644 --- a/src/managers/eventLoop/EventLoopManager.hpp +++ b/src/managers/eventLoop/EventLoopManager.hpp @@ -10,6 +10,7 @@ class CEventLoopManager { public: CEventLoopManager(); + ~CEventLoopManager(); void enterLoop(wl_display* display, wl_event_loop* wlEventLoop); @@ -24,8 +25,9 @@ class CEventLoopManager { private: struct { - wl_event_loop* loop = nullptr; - wl_display* display = nullptr; + wl_event_loop* loop = nullptr; + wl_display* display = nullptr; + wl_event_source* eventSource = nullptr; } m_sWayland; struct { diff --git a/src/managers/input/IdleInhibitor.cpp b/src/managers/input/IdleInhibitor.cpp index a71059e4..a38acdbf 100644 --- a/src/managers/input/IdleInhibitor.cpp +++ b/src/managers/input/IdleInhibitor.cpp @@ -14,7 +14,7 @@ void CInputManager::newIdleInhibitor(std::any inhibitor) { recheckIdleInhibitorStatus(); }); - auto WLSurface = CWLSurface::surfaceFromWlr(PINHIBIT->inhibitor->surface); + auto WLSurface = CWLSurface::fromResource(PINHIBIT->inhibitor->surface.lock()); if (!WLSurface) { Debug::log(LOG, "Inhibitor has no HL Surface attached to it, likely meaning it's a non-desktop element. Assuming it's visible."); @@ -37,7 +37,7 @@ void CInputManager::recheckIdleInhibitorStatus() { return; } - auto WLSurface = CWLSurface::surfaceFromWlr(ii->inhibitor->surface); + auto WLSurface = CWLSurface::fromResource(ii->inhibitor->surface.lock()); if (!WLSurface) continue; diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index f4f97236..ff10f303 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -1,6 +1,7 @@ #include "InputManager.hpp" #include "../../Compositor.hpp" #include "wlr/types/wlr_switch.h" +#include #include #include "../../config/ConfigValue.hpp" #include "../../desktop/Window.hpp" @@ -42,7 +43,7 @@ CInputManager::CInputManager() { Debug::log(LOG, "cursorImage request: shape {} -> {}", (uint32_t)event.shape, event.shapeName); - m_sCursorSurfaceInfo.wlSurface.unassign(); + m_sCursorSurfaceInfo.wlSurface->unassign(); m_sCursorSurfaceInfo.vHotspot = {}; m_sCursorSurfaceInfo.name = event.shapeName; m_sCursorSurfaceInfo.hidden = false; @@ -51,12 +52,18 @@ CInputManager::CInputManager() { g_pHyprRenderer->setCursorFromName(m_sCursorSurfaceInfo.name); }); - m_sListeners.newIdleInhibitor = PROTO::idleInhibit->events.newIdleInhibitor.registerListener([this](std::any data) { this->newIdleInhibitor(data); }); - m_sListeners.newVirtualKeyboard = - PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { this->newVirtualKeyboard(std::any_cast>(data)); }); - m_sListeners.newVirtualMouse = - PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { this->newVirtualMouse(std::any_cast>(data)); }); - m_sListeners.setCursor = g_pSeatManager->events.setCursor.registerListener([this](std::any d) { this->processMouseRequest(d); }); + m_sListeners.newIdleInhibitor = PROTO::idleInhibit->events.newIdleInhibitor.registerListener([this](std::any data) { this->newIdleInhibitor(data); }); + m_sListeners.newVirtualKeyboard = PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { + this->newVirtualKeyboard(std::any_cast>(data)); + updateCapabilities(); + }); + m_sListeners.newVirtualMouse = PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { + this->newVirtualMouse(std::any_cast>(data)); + updateCapabilities(); + }); + m_sListeners.setCursor = g_pSeatManager->events.setCursor.registerListener([this](std::any d) { this->processMouseRequest(d); }); + + m_sCursorSurfaceInfo.wlSurface = CWLSurface::create(); } CInputManager::~CInputManager() { @@ -114,8 +121,8 @@ void CInputManager::sendMotionEventsToFocused() { return; // todo: this sucks ass - const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_pLastFocus); - const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus); + const auto PWINDOW = g_pCompositor->getWindowFromSurface(g_pCompositor->m_pLastFocus.lock()); + const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock()); timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -124,7 +131,7 @@ void CInputManager::sendMotionEventsToFocused() { m_bEmptyFocusCursorSet = false; - g_pSeatManager->setPointerFocus(g_pCompositor->m_pLastFocus, LOCAL); + g_pSeatManager->setPointerFocus(g_pCompositor->m_pLastFocus.lock(), LOCAL); } void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { @@ -140,14 +147,14 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { const auto FOLLOWMOUSE = *PFOLLOWONDND && PROTO::data->dndActive() ? 1 : *PFOLLOWMOUSE; - m_pFoundSurfaceToFocus = nullptr; + m_pFoundSurfaceToFocus.reset(); m_pFoundLSToFocus.reset(); m_pFoundWindowToFocus.reset(); - wlr_surface* foundSurface = nullptr; - Vector2D surfaceCoords; - Vector2D surfacePos = Vector2D(-1337, -1337); - PHLWINDOW pFoundWindow; - PHLLS pFoundLayerSurface; + SP foundSurface; + Vector2D surfaceCoords; + Vector2D surfacePos = Vector2D(-1337, -1337); + PHLWINDOW pFoundWindow; + PHLLS pFoundLayerSurface; if (!g_pCompositor->m_bReadyToProcess || g_pCompositor->m_bIsShuttingDown || g_pCompositor->m_bUnsafeState) return; @@ -190,12 +197,12 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (forcedFocus) { pFoundWindow = forcedFocus; surfacePos = pFoundWindow->m_vRealPosition.value(); - foundSurface = pFoundWindow->m_pWLSurface.wlr(); + foundSurface = pFoundWindow->m_pWLSurface->resource(); } // constraints if (!g_pSeatManager->mouse.expired() && isConstrained()) { - const auto SURF = CWLSurface::surfaceFromWlr(g_pCompositor->m_pLastFocus); + const auto SURF = CWLSurface::fromResource(g_pCompositor->m_pLastFocus.lock()); const auto CONSTRAINT = SURF->constraint(); if (SURF && CONSTRAINT) { @@ -222,7 +229,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { // if we are holding a pointer button, // and we're not dnd-ing, don't refocus. Keep focus on last surface. if (!PROTO::data->dndActive() && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus && g_pSeatManager->state.pointerFocus && !m_bHardInput) { - foundSurface = g_pSeatManager->state.pointerFocus; + foundSurface = g_pSeatManager->state.pointerFocus.lock(); // IME popups aren't desktop-like elements // TODO: make them. @@ -232,7 +239,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { m_bFocusHeldByButtons = true; m_bRefocusHeldByButtons = refocus; } else { - auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface); + auto HLSurface = CWLSurface::fromResource(foundSurface); if (HLSurface) { const auto BOX = HLSurface->getSurfaceBoxGlobal(); @@ -274,7 +281,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (!foundSurface) { auto popup = g_pInputManager->m_sIMERelay.popupFromCoords(mouseCoords); if (popup) { - foundSurface = popup->getWlrSurface(); + foundSurface = popup->getSurface(); surfacePos = popup->globalBox().pos(); } } @@ -305,7 +312,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); surfacePos = Vector2D(-1337, -1337); } else { - foundSurface = pFoundWindow->m_pWLSurface.wlr(); + foundSurface = pFoundWindow->m_pWLSurface->resource(); surfacePos = pFoundWindow->m_vRealPosition.value(); } } @@ -344,7 +351,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { if (!pFoundWindow->m_bIsX11) { foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords); } else { - foundSurface = pFoundWindow->m_pWLSurface.wlr(); + foundSurface = pFoundWindow->m_pWLSurface->resource(); surfacePos = pFoundWindow->m_vRealPosition.value(); } } @@ -368,9 +375,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { return; // setGrab will refocus } else { // we need to grab the last surface. - foundSurface = g_pSeatManager->state.pointerFocus; + foundSurface = g_pSeatManager->state.pointerFocus.lock(); - auto HLSurface = CWLSurface::surfaceFromWlr(foundSurface); + auto HLSurface = CWLSurface::fromResource(foundSurface); if (HLSurface) { const auto BOX = HLSurface->getSurfaceBoxGlobal(); @@ -422,7 +429,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { bool allowKeyboardRefocus = true; if (!refocus && g_pCompositor->m_pLastFocus) { - const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus); + const auto PLS = g_pCompositor->getLayerSurfaceFromSurface(g_pCompositor->m_pLastFocus.lock()); if (PLS && PLS->layerSurface->current.interactivity == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) allowKeyboardRefocus = false; @@ -440,7 +447,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) { return; } - if (pFoundWindow && foundSurface == pFoundWindow->m_pWLSurface.wlr() && !m_bCursorImageOverridden) { + if (pFoundWindow && foundSurface == pFoundWindow->m_pWLSurface->resource() && !m_bCursorImageOverridden) { const auto BOX = pFoundWindow->getWindowMainSurfaceBox(); if (!VECINRECT(mouseCoords, BOX.x, BOX.y, BOX.x + BOX.width, BOX.y + BOX.height)) setCursorImageOverride("left_ptr"); @@ -554,11 +561,11 @@ void CInputManager::processMouseRequest(std::any E) { Debug::log(LOG, "cursorImage request: surface {:x}", (uintptr_t)e.surf); - if (e.surf != m_sCursorSurfaceInfo.wlSurface.wlr()) { - m_sCursorSurfaceInfo.wlSurface.unassign(); + if (e.surf != m_sCursorSurfaceInfo.wlSurface->resource()) { + m_sCursorSurfaceInfo.wlSurface->unassign(); if (e.surf) - m_sCursorSurfaceInfo.wlSurface.assign(e.surf); + m_sCursorSurfaceInfo.wlSurface->assign(e.surf); } if (e.surf) { @@ -572,7 +579,7 @@ void CInputManager::processMouseRequest(std::any E) { m_sCursorSurfaceInfo.name = ""; m_sCursorSurfaceInfo.inUse = true; - g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, e.hotspot.x, e.hotspot.y); + g_pHyprRenderer->setCursorSurface(m_sCursorSurfaceInfo.wlSurface, e.hotspot.x, e.hotspot.y); } void CInputManager::restoreCursorIconToApp() { @@ -585,8 +592,8 @@ void CInputManager::restoreCursorIconToApp() { } if (m_sCursorSurfaceInfo.name.empty()) { - if (m_sCursorSurfaceInfo.wlSurface.exists()) - g_pHyprRenderer->setCursorSurface(&m_sCursorSurfaceInfo.wlSurface, m_sCursorSurfaceInfo.vHotspot.x, m_sCursorSurfaceInfo.vHotspot.y); + if (m_sCursorSurfaceInfo.wlSurface->exists()) + g_pHyprRenderer->setCursorSurface(m_sCursorSurfaceInfo.wlSurface, m_sCursorSurfaceInfo.vHotspot.x, m_sCursorSurfaceInfo.vHotspot.y); } else { g_pHyprRenderer->setCursorFromName(m_sCursorSurfaceInfo.name); } @@ -661,7 +668,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { // clicking on border triggers resize // TODO detect click on LS properly - if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e.state == WL_POINTER_BUTTON_STATE_PRESSED) { + if (*PRESIZEONBORDER && !m_bLastFocusOnLS && e.state == WL_POINTER_BUTTON_STATE_PRESSED && (!w || w->m_iX11Type != 2)) { if (w && !w->m_bIsFullscreen) { const CBox real = {w->m_vRealPosition.value().x, w->m_vRealPosition.value().y, w->m_vRealSize.value().x, w->m_vRealSize.value().y}; const CBox grab = {real.x - BORDER_GRAB_AREA, real.y - BORDER_GRAB_AREA, real.width + 2 * BORDER_GRAB_AREA, real.height + 2 * BORDER_GRAB_AREA}; @@ -674,7 +681,7 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { } switch (e.state) { - case WL_POINTER_BUTTON_STATE_PRESSED: + case WL_POINTER_BUTTON_STATE_PRESSED: { if (*PFOLLOWMOUSE == 3) // don't refocus on full loose break; @@ -692,10 +699,16 @@ void CInputManager::processMouseDownNormal(const IPointer::SButtonEvent& e) { } // if clicked on a floating window make it top - if (g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bIsFloating) - g_pCompositor->changeWindowZOrder(g_pCompositor->m_pLastWindow.lock(), true); + if (!g_pSeatManager->state.pointerFocus) + break; + + auto HLSurf = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock()); + + if (HLSurf && HLSurf->getWindow()) + g_pCompositor->changeWindowZOrder(HLSurf->getWindow(), true); break; + } case WL_POINTER_BUTTON_STATE_RELEASED: break; } @@ -776,8 +789,9 @@ void CInputManager::onMouseWheel(IPointer::SAxisEvent e) { } } } - - g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, std::round(factor * e.deltaDiscrete / 120), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); + double deltaDiscrete = factor * e.deltaDiscrete / std::abs(e.deltaDiscrete); + g_pSeatManager->sendPointerAxis(e.timeMs, e.axis, factor * e.delta, deltaDiscrete > 0 ? std::ceil(deltaDiscrete) : std::floor(deltaDiscrete), + std::round(factor * e.deltaDiscrete), e.source, WL_POINTER_AXIS_RELATIVE_DIRECTION_IDENTICAL); } Vector2D CInputManager::getMouseCoordsInternal() { @@ -842,8 +856,10 @@ void CInputManager::setupKeyboard(SP keeb) { auto PKEEB = ((IKeyboard*)owner)->self.lock(); const auto LAYOUT = PKEEB->getActiveLayout(); - if (PKEEB == g_pSeatManager->keyboard) + if (PKEEB == g_pSeatManager->keyboard) { g_pSeatManager->updateActiveKeyboardData(); + g_pKeybindManager->m_mKeyToCodeCache.clear(); + } g_pEventManager->postEvent(SHyprIPCEvent{"activelayout", PKEEB->hlName + "," + LAYOUT}); EMIT_HOOK_EVENT("activeLayout", (std::vector{PKEEB, LAYOUT})); @@ -1389,7 +1405,7 @@ bool CInputManager::isConstrained() { if (!C) continue; - if (!C->isActive() || C->owner()->wlr() != g_pCompositor->m_pLastFocus) + if (!C->isActive() || C->owner()->resource() != g_pCompositor->m_pLastFocus) continue; return true; @@ -1675,6 +1691,10 @@ void CInputManager::setCursorIconOnBorder(PHLWINDOW w) { return; } + // ignore X11 OR windows, they shouldn't be touched + if (w->m_bIsX11 && w->m_iX11Type == 2) + return; + static auto PEXTENDBORDERGRAB = CConfigValue("general:extend_border_grab_area"); const int BORDERSIZE = w->getRealBorderSize(); const int ROUNDING = w->rounding(); diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index f7d9ae57..9fdf061c 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -44,10 +44,10 @@ enum eBorderIconDirection { }; struct STouchData { - PHLWINDOWREF touchFocusWindow; - PHLLSREF touchFocusLS; - wlr_surface* touchFocusSurface = nullptr; - Vector2D touchSurfaceOrigin; + PHLWINDOWREF touchFocusWindow; + PHLLSREF touchFocusLS; + WP touchFocusSurface; + Vector2D touchSurfaceOrigin; }; // The third row is always 0 0 1 and is not expected by `libinput_device_config_calibration_set_matrix` @@ -236,9 +236,9 @@ class CInputManager { void applyConfigToKeyboard(SP); // this will be set after a refocus() - wlr_surface* m_pFoundSurfaceToFocus = nullptr; - PHLLSREF m_pFoundLSToFocus; - PHLWINDOWREF m_pFoundWindowToFocus; + WP m_pFoundSurfaceToFocus; + PHLLSREF m_pFoundLSToFocus; + PHLWINDOWREF m_pFoundWindowToFocus; // for holding focus on buttons held bool m_bFocusHeldByButtons = false; @@ -268,11 +268,11 @@ class CInputManager { // cursor surface struct cursorSI { - bool hidden = false; // null surface = hidden - CWLSurface wlSurface; - Vector2D vHotspot; - std::string name; // if not empty, means set by name. - bool inUse = false; + bool hidden = false; // null surface = hidden + SP wlSurface; + Vector2D vHotspot; + std::string name; // if not empty, means set by name. + bool inUse = false; } m_sCursorSurfaceInfo; void restoreCursorIconToApp(); // no-op if restored diff --git a/src/managers/input/InputMethodPopup.cpp b/src/managers/input/InputMethodPopup.cpp index 9ff584e6..a8757030 100644 --- a/src/managers/input/InputMethodPopup.cpp +++ b/src/managers/input/InputMethodPopup.cpp @@ -3,22 +3,24 @@ #include "../../Compositor.hpp" #include "../../protocols/FractionalScale.hpp" #include "../../protocols/InputMethodV2.hpp" +#include "../../protocols/core/Compositor.hpp" CInputPopup::CInputPopup(SP popup_) : popup(popup_) { listeners.commit = popup_->events.commit.registerListener([this](std::any d) { onCommit(); }); listeners.map = popup_->events.map.registerListener([this](std::any d) { onMap(); }); listeners.unmap = popup_->events.unmap.registerListener([this](std::any d) { onUnmap(); }); listeners.destroy = popup_->events.destroy.registerListener([this](std::any d) { onDestroy(); }); - surface.assign(popup_->surface()); + surface = CWLSurface::create(); + surface->assign(popup_->surface()); } -CWLSurface* CInputPopup::queryOwner() { +SP CInputPopup::queryOwner() { const auto FOCUSED = g_pInputManager->m_sIMERelay.getFocusedTextInput(); if (!FOCUSED) return nullptr; - return CWLSurface::surfaceFromWlr(FOCUSED->focusedSurface()); + return CWLSurface::fromResource(FOCUSED->focusedSurface()); } void CInputPopup::onDestroy() { @@ -36,7 +38,7 @@ void CInputPopup::onMap() { if (!PMONITOR) return; - PROTO::fractional->sendScale(surface.wlr(), PMONITOR->scale); + PROTO::fractional->sendScale(surface->resource(), PMONITOR->scale); } void CInputPopup::onUnmap() { @@ -69,7 +71,7 @@ void CInputPopup::damageSurface() { } Vector2D pos = globalBox().pos(); - g_pHyprRenderer->damageSurface(surface.wlr(), pos.x, pos.y); + g_pHyprRenderer->damageSurface(surface->resource(), pos.x, pos.y); } void CInputPopup::updateBox() { @@ -98,7 +100,7 @@ void CInputPopup::updateBox() { cursorBoxParent = {0, 0, (int)parentBox.w, (int)parentBox.h}; } - Vector2D currentPopupSize = surface.getViewporterCorrectedSize(); + Vector2D currentPopupSize = surface->getViewporterCorrectedSize(); CMonitor* pMonitor = g_pCompositor->getMonitorFromVector(parentBox.middle()); @@ -127,9 +129,9 @@ void CInputPopup::updateBox() { const auto PML = g_pCompositor->getMonitorFromID(lastMonitor); if (PML) - wlr_surface_send_leave(surface.wlr(), PML->output); + surface->resource()->leave(PML->self.lock()); - wlr_surface_send_enter(surface.wlr(), PM->output); + surface->resource()->enter(PM->self.lock()); lastMonitor = PM->ID; } @@ -151,6 +153,6 @@ bool CInputPopup::isVecInPopup(const Vector2D& point) { return globalBox().containsPoint(point); } -wlr_surface* CInputPopup::getWlrSurface() { - return surface.wlr(); +SP CInputPopup::getSurface() { + return surface->resource(); } diff --git a/src/managers/input/InputMethodPopup.hpp b/src/managers/input/InputMethodPopup.hpp index 61b45d74..9c5491bf 100644 --- a/src/managers/input/InputMethodPopup.hpp +++ b/src/managers/input/InputMethodPopup.hpp @@ -12,18 +12,18 @@ class CInputPopup { public: CInputPopup(SP popup); - void damageEntire(); - void damageSurface(); + void damageEntire(); + void damageSurface(); - bool isVecInPopup(const Vector2D& point); + bool isVecInPopup(const Vector2D& point); - CBox globalBox(); - wlr_surface* getWlrSurface(); + CBox globalBox(); + SP getSurface(); - void onCommit(); + void onCommit(); private: - CWLSurface* queryOwner(); + SP queryOwner(); void updateBox(); void onDestroy(); @@ -31,7 +31,7 @@ class CInputPopup { void onUnmap(); WP popup; - CWLSurface surface; + SP surface; CBox lastBoxLocal; uint64_t lastMonitor = -1; diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp index abf18fba..92ab14d8 100644 --- a/src/managers/input/InputMethodRelay.cpp +++ b/src/managers/input/InputMethodRelay.cpp @@ -3,9 +3,11 @@ #include "../../Compositor.hpp" #include "../../protocols/TextInputV3.hpp" #include "../../protocols/InputMethodV2.hpp" +#include "../../protocols/core/Compositor.hpp" CInputMethodRelay::CInputMethodRelay() { - static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast(param)); }); + static auto P = + g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast>(param)); }); listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); }); listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast>(ime)); }); @@ -54,17 +56,17 @@ void CInputMethodRelay::onNewIME(SP pIME) { return; for (auto& ti : m_vTextInputs) { - if (ti->client() != wl_resource_get_client(g_pCompositor->m_pLastFocus->resource)) + if (ti->client() != g_pCompositor->m_pLastFocus->client()) continue; if (ti->isV3()) - ti->enter(g_pCompositor->m_pLastFocus); + ti->enter(g_pCompositor->m_pLastFocus.lock()); else - ti->onEnabled(g_pCompositor->m_pLastFocus); + ti->onEnabled(g_pCompositor->m_pLastFocus.lock()); } } -void CInputMethodRelay::setIMEPopupFocus(CInputPopup* pPopup, wlr_surface* pSurface) { +void CInputMethodRelay::setIMEPopupFocus(CInputPopup* pPopup, SP pSurface) { pPopup->onCommit(); } @@ -125,7 +127,7 @@ void CInputMethodRelay::commitIMEState(CTextInput* pInput) { pInput->commitStateToIME(m_pIME.lock()); } -void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) { +void CInputMethodRelay::onKeyboardFocus(SP pSurface) { if (m_pIME.expired()) return; @@ -145,7 +147,7 @@ void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) { if (!ti->isV3()) continue; - if (ti->client() != wl_resource_get_client(pSurface->resource)) + if (ti->client() != pSurface->client()) continue; ti->enter(pSurface); @@ -161,9 +163,9 @@ CInputPopup* CInputMethodRelay::popupFromCoords(const Vector2D& point) { return nullptr; } -CInputPopup* CInputMethodRelay::popupFromSurface(const wlr_surface* surface) { +CInputPopup* CInputMethodRelay::popupFromSurface(const SP surface) { for (auto& p : m_vIMEPopups) { - if (p->getWlrSurface() == surface) + if (p->getSurface() == surface) return p.get(); } diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp index 2896e8e8..e942add8 100644 --- a/src/managers/input/InputMethodRelay.hpp +++ b/src/managers/input/InputMethodRelay.hpp @@ -26,15 +26,15 @@ class CInputMethodRelay { void commitIMEState(CTextInput* pInput); void removeTextInput(CTextInput* pInput); - void onKeyboardFocus(wlr_surface*); + void onKeyboardFocus(SP); CTextInput* getFocusedTextInput(); - void setIMEPopupFocus(CInputPopup*, wlr_surface*); + void setIMEPopupFocus(CInputPopup*, SP); void removePopup(CInputPopup*); CInputPopup* popupFromCoords(const Vector2D& point); - CInputPopup* popupFromSurface(const wlr_surface* surface); + CInputPopup* popupFromSurface(const SP surface); void updateAllPopups(); @@ -44,7 +44,7 @@ class CInputMethodRelay { std::vector> m_vTextInputs; std::vector> m_vIMEPopups; - wlr_surface* m_pLastKbFocus = nullptr; + WP m_pLastKbFocus; struct { CHyprSignalListener newTIV3; @@ -57,6 +57,6 @@ class CInputMethodRelay { friend class CHyprRenderer; friend class CInputManager; friend class CTextInputV1ProtocolManager; - friend struct CTextInput; + friend class CTextInput; friend class CHyprRenderer; }; diff --git a/src/managers/input/Swipe.cpp b/src/managers/input/Swipe.cpp index 44623671..a605fea7 100644 --- a/src/managers/input/Swipe.cpp +++ b/src/managers/input/Swipe.cpp @@ -3,13 +3,14 @@ #include "../../config/ConfigValue.hpp" void CInputManager::onSwipeBegin(IPointer::SSwipeBeginEvent e) { - static auto PSWIPE = CConfigValue("gestures:workspace_swipe"); - static auto PSWIPEFINGERS = CConfigValue("gestures:workspace_swipe_fingers"); - static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); + static auto PSWIPE = CConfigValue("gestures:workspace_swipe"); + static auto PSWIPEFINGERS = CConfigValue("gestures:workspace_swipe_fingers"); + static auto PSWIPEMINFINGERS = CConfigValue("gestures:workspace_swipe_min_fingers"); + static auto PSWIPENEW = CConfigValue("gestures:workspace_swipe_create_new"); EMIT_HOOK_EVENT_CANCELLABLE("swipeBegin", e); - if (e.fingers != *PSWIPEFINGERS || *PSWIPE == 0 || g_pSessionLockManager->isSessionLocked()) + if ((!*PSWIPEMINFINGERS && e.fingers != *PSWIPEFINGERS) || (*PSWIPEMINFINGERS && e.fingers < *PSWIPEFINGERS) || *PSWIPE == 0 || g_pSessionLockManager->isSessionLocked()) return; int onMonitor = 0; diff --git a/src/managers/input/Tablets.cpp b/src/managers/input/Tablets.cpp index 56e817ec..f1157e4b 100644 --- a/src/managers/input/Tablets.cpp +++ b/src/managers/input/Tablets.cpp @@ -20,7 +20,7 @@ static void unfocusTool(SP tool) { PROTO::tablet->proximityOut(tool); } -static void focusTool(SP tool, SP tablet, wlr_surface* surf) { +static void focusTool(SP tool, SP tablet, SP surf) { if (tool->getSurface() == surf || !surf) return; @@ -37,7 +37,7 @@ static void focusTool(SP tool, SP tablet, wlr_surface* sur } static void refocusTablet(SP tab, SP tool, bool motion = false) { - const auto LASTHLSURFACE = CWLSurface::surfaceFromWlr(g_pSeatManager->state.pointerFocus); + const auto LASTHLSURFACE = CWLSurface::fromResource(g_pSeatManager->state.pointerFocus.lock()); if (!LASTHLSURFACE || !tool->active) { if (tool->getSurface()) @@ -57,7 +57,7 @@ static void refocusTablet(SP tab, SP tool, bool motion = f const auto CURSORPOS = g_pInputManager->getMouseCoordsInternal(); - focusTool(tool, tab, g_pSeatManager->state.pointerFocus); + focusTool(tool, tab, g_pSeatManager->state.pointerFocus.lock()); if (!motion) return; diff --git a/src/managers/input/TextInput.cpp b/src/managers/input/TextInput.cpp index b3f0b0cb..4c7ffe6e 100644 --- a/src/managers/input/TextInput.cpp +++ b/src/managers/input/TextInput.cpp @@ -5,6 +5,7 @@ #include "../../Compositor.hpp" #include "../../protocols/TextInputV3.hpp" #include "../../protocols/InputMethodV2.hpp" +#include "../../protocols/core/Compositor.hpp" CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) { ti->pTextInput = this; @@ -56,8 +57,8 @@ void CTextInput::initCallbacks() { hyprListener_textInputDestroy.removeCallback(); hyprListener_textInputDisable.removeCallback(); hyprListener_textInputEnable.removeCallback(); - hyprListener_surfaceDestroyed.removeCallback(); - hyprListener_surfaceUnmapped.removeCallback(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); g_pInputManager->m_sIMERelay.removeTextInput(this); }, @@ -65,7 +66,7 @@ void CTextInput::initCallbacks() { } } -void CTextInput::onEnabled(wlr_surface* surfV1) { +void CTextInput::onEnabled(SP surfV1) { Debug::log(LOG, "TI ENABLE"); if (g_pInputManager->m_sIMERelay.m_pIME.expired()) { @@ -75,7 +76,7 @@ void CTextInput::onEnabled(wlr_surface* surfV1) { // v1 only, map surface to PTI if (!isV3()) { - wlr_surface* pSurface = surfV1; + SP pSurface = surfV1; if (g_pCompositor->m_pLastFocus != pSurface || !pV1Input->active) return; @@ -97,8 +98,8 @@ void CTextInput::onDisabled() { if (!isV3()) leave(); - hyprListener_surfaceDestroyed.removeCallback(); - hyprListener_surfaceUnmapped.removeCallback(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); g_pInputManager->m_sIMERelay.deactivateIME(this); } @@ -117,50 +118,44 @@ void CTextInput::onCommit() { g_pInputManager->m_sIMERelay.commitIMEState(this); } -void CTextInput::setFocusedSurface(wlr_surface* pSurface) { +void CTextInput::setFocusedSurface(SP pSurface) { if (pSurface == pFocusedSurface) return; pFocusedSurface = pSurface; - hyprListener_surfaceUnmapped.removeCallback(); - hyprListener_surfaceDestroyed.removeCallback(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); if (!pSurface) return; - hyprListener_surfaceUnmapped.initCallback( - &pSurface->events.unmap, - [this](void* owner, void* data) { - Debug::log(LOG, "Unmap TI owner1"); + listeners.surfaceUnmap = pSurface->events.unmap.registerListener([this](std::any d) { + Debug::log(LOG, "Unmap TI owner1"); - if (enterLocks) - enterLocks--; - pFocusedSurface = nullptr; - hyprListener_surfaceUnmapped.removeCallback(); - hyprListener_surfaceDestroyed.removeCallback(); - }, - this, "CTextInput"); + if (enterLocks) + enterLocks--; + pFocusedSurface.reset(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); + }); - hyprListener_surfaceDestroyed.initCallback( - &pSurface->events.destroy, - [this](void* owner, void* data) { - Debug::log(LOG, "destroy TI owner1"); + listeners.surfaceDestroy = pSurface->events.destroy.registerListener([this](std::any d) { + Debug::log(LOG, "Destroy TI owner1"); - if (enterLocks) - enterLocks--; - pFocusedSurface = nullptr; - hyprListener_surfaceUnmapped.removeCallback(); - hyprListener_surfaceDestroyed.removeCallback(); - }, - this, "CTextInput"); + if (enterLocks) + enterLocks--; + pFocusedSurface.reset(); + listeners.surfaceUnmap.reset(); + listeners.surfaceDestroy.reset(); + }); } bool CTextInput::isV3() { return !pV1Input; } -void CTextInput::enter(wlr_surface* pSurface) { +void CTextInput::enter(SP pSurface) { if (!pSurface || !pSurface->mapped) return; @@ -182,7 +177,7 @@ void CTextInput::enter(wlr_surface* pSurface) { if (isV3()) pV3Input->enter(pSurface); else { - zwp_text_input_v1_send_enter(pV1Input->resourceImpl, pSurface->resource); + zwp_text_input_v1_send_enter(pV1Input->resourceImpl, pSurface->getResource()->resource()); pV1Input->active = true; } @@ -211,8 +206,8 @@ void CTextInput::leave() { g_pInputManager->m_sIMERelay.deactivateIME(this); } -wlr_surface* CTextInput::focusedSurface() { - return pFocusedSurface; +SP CTextInput::focusedSurface() { + return pFocusedSurface.lock(); } wl_client* CTextInput::client() { diff --git a/src/managers/input/TextInput.hpp b/src/managers/input/TextInput.hpp index ff21da95..30fbb4cc 100644 --- a/src/managers/input/TextInput.hpp +++ b/src/managers/input/TextInput.hpp @@ -6,12 +6,12 @@ #include "../../helpers/signal/Listener.hpp" #include -struct wlr_surface; struct wl_client; struct STextInputV1; class CTextInputV3; class CInputMethodV2; +class CWLSurfaceResource; class CTextInput { public: @@ -19,43 +19,43 @@ class CTextInput { CTextInput(STextInputV1* ti); ~CTextInput(); - bool isV3(); - void enter(wlr_surface* pSurface); - void leave(); - void tiV1Destroyed(); - wl_client* client(); - void commitStateToIME(SP ime); - void updateIMEState(SP ime); + bool isV3(); + void enter(SP pSurface); + void leave(); + void tiV1Destroyed(); + wl_client* client(); + void commitStateToIME(SP ime); + void updateIMEState(SP ime); - void onEnabled(wlr_surface* surfV1 = nullptr); - void onDisabled(); - void onCommit(); + void onEnabled(SP surfV1 = nullptr); + void onDisabled(); + void onCommit(); - bool hasCursorRectangle(); - CBox cursorBox(); + bool hasCursorRectangle(); + CBox cursorBox(); - wlr_surface* focusedSurface(); + SP focusedSurface(); private: - void setFocusedSurface(wlr_surface* pSurface); - void initCallbacks(); + void setFocusedSurface(SP pSurface); + void initCallbacks(); - wlr_surface* pFocusedSurface = nullptr; - int enterLocks = 0; - WP pV3Input; - STextInputV1* pV1Input = nullptr; + WP pFocusedSurface; + int enterLocks = 0; + WP pV3Input; + STextInputV1* pV1Input = nullptr; DYNLISTENER(textInputEnable); DYNLISTENER(textInputDisable); DYNLISTENER(textInputCommit); DYNLISTENER(textInputDestroy); - DYNLISTENER(surfaceUnmapped); - DYNLISTENER(surfaceDestroyed); struct { CHyprSignalListener enable; CHyprSignalListener disable; CHyprSignalListener commit; CHyprSignalListener destroy; + CHyprSignalListener surfaceUnmap; + CHyprSignalListener surfaceDestroy; } listeners; }; \ No newline at end of file diff --git a/src/managers/input/Touch.cpp b/src/managers/input/Touch.cpp index 7748813b..a1f949c2 100644 --- a/src/managers/input/Touch.cpp +++ b/src/managers/input/Touch.cpp @@ -77,7 +77,7 @@ void CInputManager::onTouchDown(ITouch::SDownEvent e) { } else return; // oops, nothing found. - g_pSeatManager->sendTouchDown(m_sTouchData.touchFocusSurface, e.timeMs, e.touchID, local); + g_pSeatManager->sendTouchDown(m_sTouchData.touchFocusSurface.lock(), e.timeMs, e.touchID, local); PROTO::idle->onActivity(); } diff --git a/src/meson.build b/src/meson.build index 7a00a5ff..ef00d4e0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -18,6 +18,12 @@ executable('Hyprland', src, dependency('xkbcommon'), dependency('libinput'), xcb_dep, + xcb_composite_dep, + xcb_errors_dep, + xcb_icccm_dep, + xcb_render_dep, + xcb_res_dep, + xcb_xfixes_dep, backtrace_dep, epoll_dep, udis86, diff --git a/src/protocols/AlphaModifier.cpp b/src/protocols/AlphaModifier.cpp index b9b99f69..04dcd0a8 100644 --- a/src/protocols/AlphaModifier.cpp +++ b/src/protocols/AlphaModifier.cpp @@ -2,10 +2,11 @@ #include #include "../desktop/WLSurface.hpp" #include "../render/Renderer.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::alphaModifier->protoLog -CAlphaModifier::CAlphaModifier(SP resource_, wlr_surface* surface_) : resource(resource_), pSurface(surface_) { +CAlphaModifier::CAlphaModifier(SP resource_, SP surface_) : resource(resource_), pSurface(surface_) { if (!resource->resource()) return; @@ -18,8 +19,7 @@ CAlphaModifier::CAlphaModifier(SP resource_, wlr_surf setSurfaceAlpha(1.F); }); - hyprListener_surfaceDestroy.initCallback( - &surface_->events.destroy, [this](void* owner, void* data) { onSurfaceDestroy(); }, this, "CAlphaModifier"); + listeners.destroySurface = pSurface->events.destroy.registerListener([this](std::any d) { onSurfaceDestroy(); }); resource->setSetMultiplier([this](CWpAlphaModifierSurfaceV1* mod, uint32_t alpha) { if (!pSurface) { @@ -35,19 +35,19 @@ CAlphaModifier::CAlphaModifier(SP resource_, wlr_surf } CAlphaModifier::~CAlphaModifier() { - hyprListener_surfaceDestroy.removeCallback(); + ; } bool CAlphaModifier::good() { return resource->resource(); } -wlr_surface* CAlphaModifier::getSurface() { - return pSurface; +SP CAlphaModifier::getSurface() { + return pSurface.lock(); } void CAlphaModifier::setSurfaceAlpha(float a) { - CWLSurface* surf = CWLSurface::surfaceFromWlr(pSurface); + auto surf = CWLSurface::fromResource(pSurface.lock()); if (!surf) { LOGM(ERR, "CAlphaModifier::setSurfaceAlpha: No CWLSurface for given surface??"); @@ -62,8 +62,7 @@ void CAlphaModifier::setSurfaceAlpha(float a) { } void CAlphaModifier::onSurfaceDestroy() { - hyprListener_surfaceDestroy.removeCallback(); - pSurface = nullptr; + pSurface.reset(); } CAlphaModifierProtocol::CAlphaModifierProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { @@ -75,7 +74,7 @@ void CAlphaModifierProtocol::bindManager(wl_client* client, void* data, uint32_t RESOURCE->setOnDestroy([this](CWpAlphaModifierV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpAlphaModifierV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); - RESOURCE->setGetSurface([this](CWpAlphaModifierV1* pMgr, uint32_t id, wl_resource* surface) { this->onGetSurface(pMgr, id, wlr_surface_from_resource(surface)); }); + RESOURCE->setGetSurface([this](CWpAlphaModifierV1* pMgr, uint32_t id, wl_resource* surface) { this->onGetSurface(pMgr, id, CWLSurfaceResource::fromResource(surface)); }); } void CAlphaModifierProtocol::onManagerResourceDestroy(wl_resource* res) { @@ -83,29 +82,11 @@ void CAlphaModifierProtocol::onManagerResourceDestroy(wl_resource* res) { } void CAlphaModifierProtocol::destroyModifier(CAlphaModifier* modifier) { - if (modifier->getSurface()) - m_mAlphaModifiers.erase(modifier->getSurface()); - else { - // find it first - wlr_surface* deadptr = nullptr; - for (auto& [k, v] : m_mAlphaModifiers) { - if (v.get() == modifier) { - deadptr = k; - break; - } - } - - if (!deadptr) { - LOGM(ERR, "CAlphaModifierProtocol::destroyModifier: dead resource but no deadptr???"); - return; - } - - m_mAlphaModifiers.erase(deadptr); - } + std::erase_if(m_mAlphaModifiers, [](const auto& e) { return e.first.expired(); }); } -void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, wlr_surface* surface) { - if (m_mAlphaModifiers.contains(surface)) { +void CAlphaModifierProtocol::onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, SP surface) { + if (std::find_if(m_mAlphaModifiers.begin(), m_mAlphaModifiers.end(), [surface](const auto& e) { return e.first == surface; }) != m_mAlphaModifiers.end()) { LOGM(ERR, "AlphaModifier already present for surface {:x}", (uintptr_t)surface); pMgr->error(WP_ALPHA_MODIFIER_V1_ERROR_ALREADY_CONSTRUCTED, "AlphaModifier already present"); return; diff --git a/src/protocols/AlphaModifier.hpp b/src/protocols/AlphaModifier.hpp index 457d1b4c..d49d1e4e 100644 --- a/src/protocols/AlphaModifier.hpp +++ b/src/protocols/AlphaModifier.hpp @@ -5,23 +5,28 @@ #include #include "WaylandProtocol.hpp" #include "alpha-modifier-v1.hpp" +#include "../helpers/signal/Listener.hpp" + +class CWLSurfaceResource; class CAlphaModifier { public: - CAlphaModifier(SP resource_, wlr_surface* surface); + CAlphaModifier(SP resource_, SP surface); ~CAlphaModifier(); - bool good(); - wlr_surface* getSurface(); - void onSurfaceDestroy(); + bool good(); + SP getSurface(); + void onSurfaceDestroy(); private: SP resource; - wlr_surface* pSurface = nullptr; + WP pSurface; void setSurfaceAlpha(float a); - DYNLISTENER(surfaceDestroy); + struct { + CHyprSignalListener destroySurface; + } listeners; }; class CAlphaModifierProtocol : public IWaylandProtocol { @@ -33,15 +38,15 @@ class CAlphaModifierProtocol : public IWaylandProtocol { private: void onManagerResourceDestroy(wl_resource* res); void destroyModifier(CAlphaModifier* decoration); - void onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, wlr_surface* surface); + void onGetSurface(CWpAlphaModifierV1* pMgr, uint32_t id, SP surface); // - std::vector> m_vManagers; - std::unordered_map> m_mAlphaModifiers; // xdg_toplevel -> deco + std::vector> m_vManagers; + std::unordered_map, UP> m_mAlphaModifiers; // xdg_toplevel -> deco friend class CAlphaModifier; }; namespace PROTO { inline UP alphaModifier; -}; \ No newline at end of file +}; diff --git a/src/protocols/FocusGrab.cpp b/src/protocols/FocusGrab.cpp index 5f0771e0..40f9af44 100644 --- a/src/protocols/FocusGrab.cpp +++ b/src/protocols/FocusGrab.cpp @@ -1,21 +1,21 @@ #include "FocusGrab.hpp" -#include "Compositor.hpp" +#include "../Compositor.hpp" #include #include "../managers/input/InputManager.hpp" #include "../managers/SeatManager.hpp" +#include "core/Compositor.hpp" #include #include #include #define LOGM PROTO::focusGrab->protoLog -CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface) { - hyprListener_surfaceDestroy.initCallback( - &surface->events.destroy, [=](void*, void*) { grab->eraseSurface(surface); }, this, "CFocusGrab"); +CFocusGrabSurfaceState::CFocusGrabSurfaceState(CFocusGrab* grab, SP surface) { + listeners.destroy = surface->events.destroy.registerListener([=](std::any d) { grab->eraseSurface(surface); }); } CFocusGrabSurfaceState::~CFocusGrabSurfaceState() { - hyprListener_surfaceDestroy.removeCallback(); + ; } CFocusGrab::CFocusGrab(SP resource_) : resource(resource_) { @@ -29,8 +29,8 @@ CFocusGrab::CFocusGrab(SP resource_) : resource(resource_) resource->setDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); resource->setOnDestroy([this](CHyprlandFocusGrabV1* pMgr) { PROTO::focusGrab->destroyGrab(this); }); - resource->setAddSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { addSurface(wlr_surface_from_resource(surface)); }); - resource->setRemoveSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { removeSurface(wlr_surface_from_resource(surface)); }); + resource->setAddSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { addSurface(CWLSurfaceResource::fromResource(surface)); }); + resource->setRemoveSurface([this](CHyprlandFocusGrabV1* pMgr, wl_resource* surface) { removeSurface(CWLSurfaceResource::fromResource(surface)); }); resource->setCommit([this](CHyprlandFocusGrabV1* pMgr) { commit(); }); } @@ -42,8 +42,8 @@ bool CFocusGrab::good() { return resource->resource(); } -bool CFocusGrab::isSurfaceComitted(wlr_surface* surface) { - auto iter = m_mSurfaces.find(surface); +bool CFocusGrab::isSurfaceComitted(SP surface) { + auto iter = std::find_if(m_mSurfaces.begin(), m_mSurfaces.end(), [surface](const auto& o) { return o.first == surface; }); if (iter == m_mSurfaces.end()) return false; @@ -77,14 +77,14 @@ void CFocusGrab::finish(bool sendCleared) { } } -void CFocusGrab::addSurface(wlr_surface* surface) { - auto iter = m_mSurfaces.find(surface); +void CFocusGrab::addSurface(SP surface) { + auto iter = std::find_if(m_mSurfaces.begin(), m_mSurfaces.end(), [surface](const auto& e) { return e.first == surface; }); if (iter == m_mSurfaces.end()) { m_mSurfaces.emplace(surface, std::make_unique(this, surface)); } } -void CFocusGrab::removeSurface(wlr_surface* surface) { +void CFocusGrab::removeSurface(SP surface) { auto iter = m_mSurfaces.find(surface); if (iter != m_mSurfaces.end()) { if (iter->second->state == CFocusGrabSurfaceState::PendingAddition) { @@ -94,20 +94,20 @@ void CFocusGrab::removeSurface(wlr_surface* surface) { } } -void CFocusGrab::eraseSurface(wlr_surface* surface) { +void CFocusGrab::eraseSurface(SP surface) { removeSurface(surface); commit(true); } void CFocusGrab::refocusKeyboard() { auto keyboardSurface = g_pSeatManager->state.keyboardFocus; - if (keyboardSurface != nullptr && isSurfaceComitted(keyboardSurface)) + if (keyboardSurface && isSurfaceComitted(keyboardSurface.lock())) return; - wlr_surface* surface = nullptr; + SP surface = nullptr; for (auto& [surf, state] : m_mSurfaces) { if (state->state == CFocusGrabSurfaceState::Comitted) { - surface = surf; + surface = surf.lock(); break; } } @@ -124,14 +124,14 @@ void CFocusGrab::commit(bool removeOnly) { for (auto iter = m_mSurfaces.begin(); iter != m_mSurfaces.end();) { switch (iter->second->state) { case CFocusGrabSurfaceState::PendingRemoval: - grab->remove(iter->first); + grab->remove(iter->first.lock()); iter = m_mSurfaces.erase(iter); surfacesChanged = true; continue; case CFocusGrabSurfaceState::PendingAddition: if (!removeOnly) { iter->second->state = CFocusGrabSurfaceState::Comitted; - grab->add(iter->first); + grab->add(iter->first.lock()); surfacesChanged = true; anyComitted = true; } diff --git a/src/protocols/FocusGrab.hpp b/src/protocols/FocusGrab.hpp index 40922c22..80166f9f 100644 --- a/src/protocols/FocusGrab.hpp +++ b/src/protocols/FocusGrab.hpp @@ -6,13 +6,15 @@ #include #include #include +#include "../helpers/signal/Listener.hpp" class CFocusGrab; class CSeatGrab; +class CWLSurfaceResource; class CFocusGrabSurfaceState { public: - CFocusGrabSurfaceState(CFocusGrab* grab, wlr_surface* surface); + CFocusGrabSurfaceState(CFocusGrab* grab, SP surface); ~CFocusGrabSurfaceState(); enum State { @@ -22,7 +24,9 @@ class CFocusGrabSurfaceState { } state = PendingAddition; private: - DYNLISTENER(surfaceDestroy); + struct { + CHyprSignalListener destroy; + } listeners; }; class CFocusGrab { @@ -31,23 +35,23 @@ class CFocusGrab { ~CFocusGrab(); bool good(); - bool isSurfaceComitted(wlr_surface* surface); + bool isSurfaceComitted(SP surface); void start(); void finish(bool sendCleared); private: - void addSurface(wlr_surface* surface); - void removeSurface(wlr_surface* surface); - void eraseSurface(wlr_surface* surface); - void refocusKeyboard(); - void commit(bool removeOnly = false); + void addSurface(SP surface); + void removeSurface(SP surface); + void eraseSurface(SP surface); + void refocusKeyboard(); + void commit(bool removeOnly = false); - SP resource; - std::unordered_map> m_mSurfaces; - SP grab; + SP resource; + std::unordered_map, UP> m_mSurfaces; + SP grab; - bool m_bGrabActive = false; + bool m_bGrabActive = false; DYNLISTENER(pointerGrabStarted); DYNLISTENER(keyboardGrabStarted); diff --git a/src/protocols/ForeignToplevelWlr.cpp b/src/protocols/ForeignToplevelWlr.cpp index 782fbc0e..8224f495 100644 --- a/src/protocols/ForeignToplevelWlr.cpp +++ b/src/protocols/ForeignToplevelWlr.cpp @@ -1,6 +1,8 @@ #include "ForeignToplevelWlr.hpp" #include #include "../Compositor.hpp" +#include "protocols/core/Output.hpp" +#include "render/Renderer.hpp" #define LOGM PROTO::foreignToplevelWlr->protoLog @@ -36,7 +38,21 @@ CForeignToplevelHandleWlr::CForeignToplevelHandleWlr(SPsetWindowFullscreen(PWINDOW, true); + if (output) { + const auto wpMonitor = CWLOutputResource::fromResource(output)->monitor; + + if (!wpMonitor.expired()) { + const auto monitor = wpMonitor.lock(); + + if (PWINDOW->m_pWorkspace != monitor->activeWorkspace) { + g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, monitor->activeWorkspace); + g_pCompositor->setActiveMonitor(monitor.get()); + } + } + } + + g_pCompositor->setWindowFullscreen(PWINDOW, true, FULLSCREEN_FULL); + g_pHyprRenderer->damageWindow(PWINDOW); }); resource->setUnsetFullscreen([this](CZwlrForeignToplevelHandleV1* p) { @@ -372,4 +388,4 @@ PHLWINDOW CForeignToplevelWlrProtocol::windowFromHandleResource(wl_resource* res } return nullptr; -} \ No newline at end of file +} diff --git a/src/protocols/FractionalScale.cpp b/src/protocols/FractionalScale.cpp index 691ab697..6d225e99 100644 --- a/src/protocols/FractionalScale.cpp +++ b/src/protocols/FractionalScale.cpp @@ -1,13 +1,9 @@ #include "FractionalScale.hpp" +#include +#include "core/Compositor.hpp" #define LOGM PROTO::fractional->protoLog -static void onWlrSurfaceDestroy(void* owner, void* data) { - const auto SURF = (wlr_surface*)owner; - - PROTO::fractional->onSurfaceDestroy(SURF); -} - CFractionalScaleProtocol::CFractionalScaleProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { ; } @@ -18,7 +14,7 @@ void CFractionalScaleProtocol::bindManager(wl_client* client, void* data, uint32 RESOURCE->setDestroy([this](CWpFractionalScaleManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); RESOURCE->setGetFractionalScale( - [this](CWpFractionalScaleManagerV1* pMgr, uint32_t id, wl_resource* surface) { this->onGetFractionalScale(pMgr, id, wlr_surface_from_resource(surface)); }); + [this](CWpFractionalScaleManagerV1* pMgr, uint32_t id, wl_resource* surface) { this->onGetFractionalScale(pMgr, id, CWLSurfaceResource::fromResource(surface)); }); } void CFractionalScaleProtocol::removeAddon(CFractionalScaleAddon* addon) { @@ -29,11 +25,13 @@ void CFractionalScaleProtocol::onManagerResourceDestroy(wl_resource* res) { std::erase_if(m_vManagers, [res](const auto& other) { return other->resource() == res; }); } -void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, wlr_surface* surface) { - if (m_mAddons.contains(surface)) { - LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface); - pMgr->error(WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, "Fractional scale already exists"); - return; +void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, SP surface) { + for (auto& [k, v] : m_mAddons) { + if (k == surface) { + LOGM(ERR, "Surface {:x} already has a fractionalScale addon", (uintptr_t)surface); + pMgr->error(WP_FRACTIONAL_SCALE_MANAGER_V1_ERROR_FRACTIONAL_SCALE_EXISTS, "Fractional scale already exists"); + return; + } } const auto PADDON = @@ -48,35 +46,22 @@ void CFractionalScaleProtocol::onGetFractionalScale(CWpFractionalScaleManagerV1* PADDON->resource->setOnDestroy([this, PADDON](CWpFractionalScaleV1* self) { this->removeAddon(PADDON); }); PADDON->resource->setDestroy([this, PADDON](CWpFractionalScaleV1* self) { this->removeAddon(PADDON); }); - if (!m_mSurfaceScales.contains(surface)) - m_mSurfaceScales[surface] = 1.F; + if (std::find_if(m_mSurfaceScales.begin(), m_mSurfaceScales.end(), [surface](const auto& e) { return e.first == surface; }) == m_mSurfaceScales.end()) + m_mSurfaceScales.emplace(surface, 1.F); - PADDON->setScale(m_mSurfaceScales[surface]); - registerSurface(surface); + PADDON->setScale(m_mSurfaceScales.at(surface)); + + // clean old + std::erase_if(m_mSurfaceScales, [](const auto& e) { return e.first.expired(); }); } -void CFractionalScaleProtocol::sendScale(wlr_surface* surf, const float& scale) { +void CFractionalScaleProtocol::sendScale(SP surf, const float& scale) { m_mSurfaceScales[surf] = scale; if (m_mAddons.contains(surf)) m_mAddons[surf]->setScale(scale); - registerSurface(surf); } -void CFractionalScaleProtocol::registerSurface(wlr_surface* surf) { - if (m_mSurfaceDestroyListeners.contains(surf)) - return; - - m_mSurfaceDestroyListeners[surf].hyprListener_surfaceDestroy.initCallback(&surf->events.destroy, ::onWlrSurfaceDestroy, surf, "FractionalScale"); -} - -void CFractionalScaleProtocol::onSurfaceDestroy(wlr_surface* surf) { - m_mSurfaceDestroyListeners.erase(surf); - m_mSurfaceScales.erase(surf); - if (m_mAddons.contains(surf)) - m_mAddons[surf]->onSurfaceDestroy(); -} - -CFractionalScaleAddon::CFractionalScaleAddon(SP resource_, wlr_surface* surf_) : resource(resource_), surface(surf_) { +CFractionalScaleAddon::CFractionalScaleAddon(SP resource_, SP surf_) : resource(resource_), surface(surf_) { resource->setDestroy([this](CWpFractionalScaleV1* self) { PROTO::fractional->removeAddon(this); }); resource->setOnDestroy([this](CWpFractionalScaleV1* self) { PROTO::fractional->removeAddon(this); }); } @@ -93,6 +78,6 @@ bool CFractionalScaleAddon::good() { return resource->resource(); } -wlr_surface* CFractionalScaleAddon::surf() { - return surface; +SP CFractionalScaleAddon::surf() { + return surface.lock(); } \ No newline at end of file diff --git a/src/protocols/FractionalScale.hpp b/src/protocols/FractionalScale.hpp index 10ebf49a..f6d1f96f 100644 --- a/src/protocols/FractionalScale.hpp +++ b/src/protocols/FractionalScale.hpp @@ -6,19 +6,20 @@ #include "fractional-scale-v1.hpp" class CFractionalScaleProtocol; +class CWLSurfaceResource; class CFractionalScaleAddon { public: - CFractionalScaleAddon(SP resource_, wlr_surface* surf_); + CFractionalScaleAddon(SP resource_, SP surf_); - void setScale(const float& scale); - void onSurfaceDestroy(); + void setScale(const float& scale); + void onSurfaceDestroy(); - bool good(); + bool good(); - wlr_surface* surf(); + SP surf(); - bool operator==(const wl_resource* other) const { + bool operator==(const wl_resource* other) const { return other == resource->resource(); } @@ -28,42 +29,36 @@ class CFractionalScaleAddon { private: SP resource; - float scale = 1.F; - wlr_surface* surface = nullptr; + float scale = 1.F; + WP surface; bool surfaceGone = false; friend class CFractionalScaleProtocol; }; -struct SSurfaceListener { - DYNLISTENER(surfaceDestroy); -}; - class CFractionalScaleProtocol : public IWaylandProtocol { public: CFractionalScaleProtocol(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 onSurfaceDestroy(wlr_surface* surf); - void sendScale(wlr_surface* surf, const float& scale); + void onSurfaceDestroy(SP surf); + void sendScale(SP surf, const float& scale); private: void removeAddon(CFractionalScaleAddon*); - void registerSurface(wlr_surface*); void onManagerResourceDestroy(wl_resource* res); - void onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, wlr_surface* surface); + void onGetFractionalScale(CWpFractionalScaleManagerV1* pMgr, uint32_t id, SP surface); // - std::unordered_map m_mSurfaceDestroyListeners; - std::unordered_map m_mSurfaceScales; - std::unordered_map> m_mAddons; - std::vector> m_vManagers; + std::unordered_map, float> m_mSurfaceScales; + std::unordered_map, UP> m_mAddons; + std::vector> m_vManagers; friend class CFractionalScaleAddon; }; namespace PROTO { inline UP fractional; -}; \ No newline at end of file +}; diff --git a/src/protocols/GammaControl.cpp b/src/protocols/GammaControl.cpp index c8db7100..7c3b39ce 100644 --- a/src/protocols/GammaControl.cpp +++ b/src/protocols/GammaControl.cpp @@ -3,6 +3,7 @@ #include #include "../helpers/Monitor.hpp" #include "../Compositor.hpp" +#include "../protocols/core/Output.hpp" #define LOGM PROTO::gamma->protoLog @@ -10,15 +11,15 @@ CGammaControl::CGammaControl(SP resource_, wl_resource* out if (!resource_->resource()) return; - wlr_output* wlrOutput = wlr_output_from_resource(output); + auto OUTPUTRES = CWLOutputResource::fromResource(output); - if (!wlrOutput) { - LOGM(ERR, "No wlr_output in CGammaControl"); + if (!OUTPUTRES) { + LOGM(ERR, "No output in CGammaControl"); resource->sendFailed(); return; } - pMonitor = g_pCompositor->getRealMonitorFromOutput(wlrOutput); + pMonitor = OUTPUTRES->monitor.get(); if (!pMonitor) { LOGM(ERR, "No CMonitor"); @@ -33,7 +34,7 @@ CGammaControl::CGammaControl(SP resource_, wl_resource* out } } - gammaSize = wlr_output_get_gamma_size(wlrOutput); + gammaSize = wlr_output_get_gamma_size(pMonitor->output); if (gammaSize <= 0) { LOGM(ERR, "Output {} doesn't support gamma", pMonitor->szName); diff --git a/src/protocols/IdleInhibit.cpp b/src/protocols/IdleInhibit.cpp index 0ff11a56..89eb3108 100644 --- a/src/protocols/IdleInhibit.cpp +++ b/src/protocols/IdleInhibit.cpp @@ -1,26 +1,23 @@ #include "IdleInhibit.hpp" +#include "core/Compositor.hpp" -CIdleInhibitor::CIdleInhibitor(SP resource_, wlr_surface* surf_) : resource(resource_), surface(surf_) { +CIdleInhibitor::CIdleInhibitor(SP resource_, SP surf_) : resource(resource_), surface(surf_) { ; } -CIdleInhibitorResource::CIdleInhibitorResource(SP resource_, wlr_surface* surface_) : resource(resource_), surface(surface_) { - hyprListener_surfaceDestroy.initCallback( - &surface->events.destroy, - [this](void* owner, void* data) { - surface = nullptr; - hyprListener_surfaceDestroy.removeCallback(); - destroySent = true; - events.destroy.emit(); - }, - this, "CIdleInhibitorResource"); +CIdleInhibitorResource::CIdleInhibitorResource(SP resource_, SP surface_) : resource(resource_), surface(surface_) { + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { + surface.reset(); + listeners.destroySurface.reset(); + destroySent = true; + events.destroy.emit(); + }); resource->setOnDestroy([this](CZwpIdleInhibitorV1* p) { PROTO::idleInhibit->removeInhibitor(this); }); resource->setDestroy([this](CZwpIdleInhibitorV1* p) { PROTO::idleInhibit->removeInhibitor(this); }); } CIdleInhibitorResource::~CIdleInhibitorResource() { - hyprListener_surfaceDestroy.removeCallback(); if (!destroySent) events.destroy.emit(); } @@ -39,14 +36,14 @@ void CIdleInhibitProtocol::bindManager(wl_client* client, void* data, uint32_t v RESOURCE->setDestroy([this](CZwpIdleInhibitManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); RESOURCE->setCreateInhibitor( - [this](CZwpIdleInhibitManagerV1* pMgr, uint32_t id, wl_resource* surface) { this->onCreateInhibitor(pMgr, id, wlr_surface_from_resource(surface)); }); + [this](CZwpIdleInhibitManagerV1* pMgr, uint32_t id, wl_resource* surface) { this->onCreateInhibitor(pMgr, id, CWLSurfaceResource::fromResource(surface)); }); } void CIdleInhibitProtocol::removeInhibitor(CIdleInhibitorResource* resource) { std::erase_if(m_vInhibitors, [resource](const auto& el) { return el.get() == resource; }); } -void CIdleInhibitProtocol::onCreateInhibitor(CZwpIdleInhibitManagerV1* pMgr, uint32_t id, wlr_surface* surface) { +void CIdleInhibitProtocol::onCreateInhibitor(CZwpIdleInhibitManagerV1* pMgr, uint32_t id, SP surface) { const auto CLIENT = pMgr->client(); const auto RESOURCE = m_vInhibitors.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), surface)); diff --git a/src/protocols/IdleInhibit.hpp b/src/protocols/IdleInhibit.hpp index b59e8789..3cbfd78d 100644 --- a/src/protocols/IdleInhibit.hpp +++ b/src/protocols/IdleInhibit.hpp @@ -7,22 +7,23 @@ #include "../helpers/signal/Signal.hpp" class CIdleInhibitorResource; +class CWLSurfaceResource; class CIdleInhibitor { public: - CIdleInhibitor(SP resource_, wlr_surface* surf_); + CIdleInhibitor(SP resource_, SP surf_); struct { CHyprSignalListener destroy; } listeners; WP resource; - wlr_surface* surface = nullptr; + WP surface; }; class CIdleInhibitorResource { public: - CIdleInhibitorResource(SP resource_, wlr_surface* surface_); + CIdleInhibitorResource(SP resource_, SP surface_); ~CIdleInhibitorResource(); SP inhibitor; @@ -33,10 +34,12 @@ class CIdleInhibitorResource { private: SP resource; - wlr_surface* surface = nullptr; + WP surface; bool destroySent = false; - DYNLISTENER(surfaceDestroy); + struct { + CHyprSignalListener destroySurface; + } listeners; }; class CIdleInhibitProtocol : public IWaylandProtocol { @@ -51,7 +54,7 @@ class CIdleInhibitProtocol : public IWaylandProtocol { private: void onManagerResourceDestroy(wl_resource* res); - void onCreateInhibitor(CZwpIdleInhibitManagerV1* pMgr, uint32_t id, wlr_surface* surface); + void onCreateInhibitor(CZwpIdleInhibitManagerV1* pMgr, uint32_t id, SP surface); void removeInhibitor(CIdleInhibitorResource*); @@ -64,4 +67,4 @@ class CIdleInhibitProtocol : public IWaylandProtocol { namespace PROTO { inline UP idleInhibit; -} \ No newline at end of file +} diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp index 93ca38c0..5d0bd417 100644 --- a/src/protocols/InputMethodV2.cpp +++ b/src/protocols/InputMethodV2.cpp @@ -3,6 +3,7 @@ #include "../managers/SeatManager.hpp" #include "../devices/IKeyboard.hpp" #include +#include "core/Compositor.hpp" #define LOGM PROTO::ime->protoLog @@ -83,51 +84,45 @@ wl_client* CInputMethodKeyboardGrabV2::client() { return resource->client(); } -CInputMethodPopupV2::CInputMethodPopupV2(SP resource_, SP owner_, wlr_surface* wlrSurface) : resource(resource_), owner(owner_) { +CInputMethodPopupV2::CInputMethodPopupV2(SP resource_, SP owner_, SP surface) : resource(resource_), owner(owner_) { if (!resource->resource()) return; resource->setDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); }); resource->setOnDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); }); - pSurface = wlrSurface; + pSurface = surface; - hyprListener_destroySurface.initCallback( - &wlrSurface->events.destroy, - [this](void* owner, void* data) { - if (mapped) - events.unmap.emit(); + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { + if (mapped) + events.unmap.emit(); - hyprListener_commitSurface.removeCallback(); - hyprListener_destroySurface.removeCallback(); + listeners.destroySurface.reset(); + listeners.commitSurface.reset(); - if (g_pCompositor->m_pLastFocus == pSurface) - g_pCompositor->m_pLastFocus = nullptr; + if (g_pCompositor->m_pLastFocus == pSurface) + g_pCompositor->m_pLastFocus.reset(); - pSurface = nullptr; - }, - this, "IMEPopup"); + pSurface.reset(); + }); - hyprListener_commitSurface.initCallback( - &wlrSurface->events.commit, - [this](void* owner, void* data) { - if (pSurface->pending.buffer_width > 0 && pSurface->pending.buffer_height > 0 && !mapped) { - mapped = true; - wlr_surface_map(pSurface); - events.map.emit(); - return; - } + listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { + if (pSurface->current.buffer && !mapped) { + mapped = true; + pSurface->map(); + events.map.emit(); + return; + } - if (pSurface->pending.buffer_width <= 0 && pSurface->pending.buffer_height <= 0 && mapped) { - mapped = false; - wlr_surface_unmap(pSurface); - events.unmap.emit(); - return; - } + if (!pSurface->current.buffer && mapped) { + mapped = false; + pSurface->unmap(); + events.unmap.emit(); + return; + } - events.commit.emit(); - }, - this, "IMEPopup"); + events.commit.emit(); + }); } CInputMethodPopupV2::~CInputMethodPopupV2() { @@ -145,8 +140,8 @@ void CInputMethodPopupV2::sendInputRectangle(const CBox& box) { resource->sendTextInputRectangle(box.x, box.y, box.w, box.h); } -wlr_surface* CInputMethodPopupV2::surface() { - return pSurface; +SP CInputMethodPopupV2::surface() { + return pSurface.lock(); } void CInputMethodV2::SState::reset() { @@ -194,7 +189,7 @@ CInputMethodV2::CInputMethodV2(SP resource_) : resource(resou resource->setGetInputPopupSurface([this](CZwpInputMethodV2* r, uint32_t id, wl_resource* surface) { const auto RESOURCE = PROTO::ime->m_vPopups.emplace_back( - makeShared(makeShared(r->client(), r->version(), id), self.lock(), wlr_surface_from_resource(surface))); + makeShared(makeShared(r->client(), r->version(), id), self.lock(), CWLSurfaceResource::fromResource(surface))); if (!RESOURCE->good()) { r->noMemory(); diff --git a/src/protocols/InputMethodV2.hpp b/src/protocols/InputMethodV2.hpp index 1f5d2598..bc21270c 100644 --- a/src/protocols/InputMethodV2.hpp +++ b/src/protocols/InputMethodV2.hpp @@ -101,12 +101,12 @@ class CInputMethodKeyboardGrabV2 { class CInputMethodPopupV2 { public: - CInputMethodPopupV2(SP resource_, SP owner_, wlr_surface* surface); + CInputMethodPopupV2(SP resource_, SP owner_, SP surface); ~CInputMethodPopupV2(); - bool good(); - void sendInputRectangle(const CBox& box); - wlr_surface* surface(); + bool good(); + void sendInputRectangle(const CBox& box); + SP surface(); struct { CSignal map; @@ -120,10 +120,12 @@ class CInputMethodPopupV2 { private: SP resource; WP owner; - wlr_surface* pSurface = nullptr; + WP pSurface; - DYNLISTENER(commitSurface); - DYNLISTENER(destroySurface); + struct { + CHyprSignalListener destroySurface; + CHyprSignalListener commitSurface; + } listeners; }; class CInputMethodV2Protocol : public IWaylandProtocol { diff --git a/src/protocols/LayerShell.cpp b/src/protocols/LayerShell.cpp index 962b89a3..d4b105cb 100644 --- a/src/protocols/LayerShell.cpp +++ b/src/protocols/LayerShell.cpp @@ -1,6 +1,8 @@ #include "LayerShell.hpp" #include "../Compositor.hpp" #include "XDGShell.hpp" +#include "core/Compositor.hpp" +#include "core/Output.hpp" #define LOGM PROTO::layerShell->protoLog @@ -14,7 +16,7 @@ void CLayerShellResource::SState::reset() { margin = {0, 0, 0, 0}; } -CLayerShellResource::CLayerShellResource(SP resource_, wlr_surface* surf_, std::string namespace_, CMonitor* pMonitor, zwlrLayerShellV1Layer layer) : +CLayerShellResource::CLayerShellResource(SP resource_, SP surf_, std::string namespace_, CMonitor* pMonitor, zwlrLayerShellV1Layer layer) : layerNamespace(namespace_), surface(surf_), resource(resource_) { if (!good()) return; @@ -31,57 +33,52 @@ CLayerShellResource::CLayerShellResource(SP resource_, wlr_ PROTO::layerShell->destroyResource(this); }); - hyprListener_destroySurface.initCallback( - &surf_->events.destroy, - [this](void* owner, void* data) { - events.destroy.emit(); - PROTO::layerShell->destroyResource(this); - }, - this, "CLayerShellResource"); + listeners.destroySurface = surf_->events.destroy.registerListener([this](std::any d) { + events.destroy.emit(); + PROTO::layerShell->destroyResource(this); + }); - hyprListener_commitSurface.initCallback( - &surf_->events.commit, - [this](void* owner, void* data) { - current = pending; - pending.committed = 0; + listeners.commitSurface = surf_->events.commit.registerListener([this](std::any d) { + current = pending; + pending.committed = 0; - bool attachedBuffer = surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0; + bool attachedBuffer = surface->current.buffer; - if (attachedBuffer && !configured) { - wlr_surface_reject_pending(surface, resource->resource(), -1, "layerSurface was not configured, but a buffer was attached"); - return; - } + if (attachedBuffer && !configured) { + surface->error(-1, "layerSurface was not configured, but a buffer was attached"); + return; + } - constexpr uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; - constexpr uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + constexpr uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + constexpr uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; - if (current.desiredSize.x <= 0 && (current.anchor & horiz) != horiz) { - wlr_surface_reject_pending(surface, resource->resource(), -1, "x == 0 but anchor doesn't have left and right"); - return; - } + if (current.desiredSize.x <= 0 && (current.anchor & horiz) != horiz) { + surface->error(-1, "x == 0 but anchor doesn't have left and right"); + return; + } - if (current.desiredSize.y <= 0 && (current.anchor & vert) != vert) { - wlr_surface_reject_pending(surface, resource->resource(), -1, "y == 0 but anchor doesn't have top and bottom"); - return; - } + if (current.desiredSize.y <= 0 && (current.anchor & vert) != vert) { + surface->error(-1, "y == 0 but anchor doesn't have top and bottom"); + return; + } - if (attachedBuffer && !mapped) { - mapped = true; - wlr_surface_map(surface); - events.map.emit(); - return; - } + if (attachedBuffer && !mapped) { + mapped = true; + surface->map(); + events.map.emit(); + return; + } - if (!attachedBuffer && mapped) { - mapped = false; - wlr_surface_unmap(surface); - events.unmap.emit(); - return; - } + if (!attachedBuffer && mapped) { + mapped = false; + surface->unmap(); + events.unmap.emit(); + configured = false; + return; + } - events.commit.emit(); - }, - this, "CLayerShellResource"); + events.commit.emit(); + }); resource->setSetSize([this](CZwlrLayerSurfaceV1* r, uint32_t x, uint32_t y) { pending.committed |= STATE_SIZE; @@ -162,6 +159,12 @@ CLayerShellResource::CLayerShellResource(SP resource_, wlr_ CLayerShellResource::~CLayerShellResource() { events.destroy.emit(); + if (surface) + surface->resetRole(); +} + +eSurfaceRole CLayerShellResource::role() { + return SURFACE_ROLE_LAYER_SHELL; } bool CLayerShellResource::good() { @@ -209,9 +212,20 @@ void CLayerShellProtocol::destroyResource(CLayerShellResource* surf) { void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* output, zwlrLayerShellV1Layer layer, std::string namespace_) { const auto CLIENT = pMgr->client(); - const auto PMONITOR = output ? g_pCompositor->getMonitorFromOutput(wlr_output_from_resource(output)) : nullptr; - const auto RESOURCE = m_vLayers.emplace_back( - makeShared(makeShared(CLIENT, pMgr->version(), id), wlr_surface_from_resource(surface), namespace_, PMONITOR, layer)); + const auto PMONITOR = output ? CWLOutputResource::fromResource(output)->monitor.get() : nullptr; + auto SURF = CWLSurfaceResource::fromResource(surface); + + if (!SURF) { + pMgr->error(-1, "Invalid surface"); + return; + } + + if (SURF->role->role() != SURFACE_ROLE_UNASSIGNED) { + pMgr->error(-1, "Surface already has a different role"); + return; + } + + const auto RESOURCE = m_vLayers.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), SURF, namespace_, PMONITOR, layer)); if (!RESOURCE->good()) { pMgr->noMemory(); @@ -219,6 +233,7 @@ void CLayerShellProtocol::onGetLayerSurface(CZwlrLayerShellV1* pMgr, uint32_t id return; } + SURF->role = RESOURCE; g_pCompositor->m_vLayers.emplace_back(CLayerSurface::create(RESOURCE)); LOGM(LOG, "New wlr_layer_surface {:x}", (uintptr_t)RESOURCE.get()); diff --git a/src/protocols/LayerShell.hpp b/src/protocols/LayerShell.hpp index 9ed6bc66..7fa14447 100644 --- a/src/protocols/LayerShell.hpp +++ b/src/protocols/LayerShell.hpp @@ -8,17 +8,20 @@ #include "wlr-layer-shell-unstable-v1.hpp" #include "../helpers/Vector2D.hpp" #include "../helpers/signal/Signal.hpp" +#include "types/SurfaceRole.hpp" class CMonitor; +class CWLSurfaceResource; -class CLayerShellResource { +class CLayerShellResource : public ISurfaceRole { public: - CLayerShellResource(SP resource_, wlr_surface* surf_, std::string namespace_, CMonitor* pMonitor, zwlrLayerShellV1Layer layer); + CLayerShellResource(SP resource_, SP surf_, std::string namespace_, CMonitor* pMonitor, zwlrLayerShellV1Layer layer); ~CLayerShellResource(); - bool good(); - void configure(const Vector2D& size); - void sendClosed(); + bool good(); + void configure(const Vector2D& size); + void sendClosed(); + virtual eSurfaceRole role(); enum eCommittedState { STATE_SIZE = (1 << 0), @@ -54,18 +57,20 @@ class CLayerShellResource { void reset(); } current, pending; - Vector2D size; - std::string layerNamespace; - std::string monitor = ""; - wlr_surface* surface = nullptr; - bool mapped = false; - bool configured = false; + Vector2D size; + std::string layerNamespace; + std::string monitor = ""; + WP surface; + bool mapped = false; + bool configured = false; private: SP resource; - DYNLISTENER(destroySurface); - DYNLISTENER(commitSurface); + struct { + CHyprSignalListener commitSurface; + CHyprSignalListener destroySurface; + } listeners; bool closed = false; diff --git a/src/protocols/LinuxDMABUF.cpp b/src/protocols/LinuxDMABUF.cpp new file mode 100644 index 00000000..cf8f8730 --- /dev/null +++ b/src/protocols/LinuxDMABUF.cpp @@ -0,0 +1,454 @@ +#include "LinuxDMABUF.hpp" +#include +#include +#include +#include "../helpers/MiscFunctions.hpp" +#include +#include +#include +#include "core/Compositor.hpp" +#include "types/DMABuffer.hpp" +#include "types/WLBuffer.hpp" +#include "../managers/HookSystemManager.hpp" +#include "../render/OpenGL.hpp" +#include "../Compositor.hpp" + +#define LOGM PROTO::linuxDma->protoLog + +static std::optional devIDFromFD(int fd) { + struct stat stat; + if (fstat(fd, &stat) != 0) + return {}; + return stat.st_rdev; +} + +CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector tranches_) { + std::set> formats; + for (auto& t : tranches_) { + for (auto& fmt : t.formats) { + for (auto& mod : fmt.mods) { + formats.insert(std::make_pair<>(fmt.format, mod)); + } + } + } + + tableLen = formats.size() * sizeof(SDMABUFFeedbackTableEntry); + int fds[2] = {0}; + allocateSHMFilePair(tableLen, &fds[0], &fds[1]); + + auto arr = (SDMABUFFeedbackTableEntry*)mmap(nullptr, tableLen, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0); + + if (arr == MAP_FAILED) { + LOGM(ERR, "mmap failed"); + close(fds[0]); + close(fds[1]); + return; + } + + close(fds[0]); + + std::vector> formatsVec; + for (auto& f : formats) { + formatsVec.push_back(f); + } + + size_t i = 0; + for (auto& [fmt, mod] : formatsVec) { + arr[i++] = SDMABUFFeedbackTableEntry{ + .fmt = fmt, + .modifier = mod, + }; + } + + munmap(arr, tableLen); + + mainDevice = device; + tableFD = fds[1]; + tranches = formatsVec; + + // TODO: maybe calculate indices? currently we send all as available which could be wrong? I ain't no kernel dev tho. +} + +CCompiledDMABUFFeedback::~CCompiledDMABUFFeedback() { + close(tableFD); +} + +CLinuxDMABuffer::CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs) { + buffer = makeShared(id, client, attrs); + + buffer->resource->buffer = buffer; + + listeners.bufferResourceDestroy = buffer->events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); + PROTO::linuxDma->destroyResource(this); + }); + + if (!buffer->success) + LOGM(ERR, "Possibly compositor bug: buffer failed to create"); +} + +CLinuxDMABuffer::~CLinuxDMABuffer() { + buffer.reset(); + listeners.bufferResourceDestroy.reset(); +} + +bool CLinuxDMABuffer::good() { + return buffer && buffer->good(); +} + +CLinuxDMABBUFParamsResource::CLinuxDMABBUFParamsResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); }); + + attrs = makeShared(); + + attrs->success = true; + + resource->setAdd([this](CZwpLinuxBufferParamsV1* r, int32_t fd, uint32_t plane, uint32_t offset, uint32_t stride, uint32_t modHi, uint32_t modLo) { + if (used) { + r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "Already used"); + return; + } + + if (plane > 3) { + r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "plane > 3"); + return; + } + + if (attrs->fds.at(plane) != -1) { + r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "plane used"); + return; + } + + attrs->fds[plane] = fd; + attrs->strides[plane] = stride; + attrs->offsets[plane] = offset; + attrs->modifier = ((uint64_t)modHi << 32) | modLo; + }); + + resource->setCreate([this](CZwpLinuxBufferParamsV1* r, int32_t w, int32_t h, uint32_t fmt, zwpLinuxBufferParamsV1Flags flags) { + if (used) { + r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "Already used"); + return; + } + + if (flags > 0) { + r->sendFailed(); + LOGM(ERR, "DMABUF flags are not supported"); + return; + } + + attrs->size = {w, h}; + attrs->format = fmt; + attrs->planes = 4 - std::count(attrs->fds.begin(), attrs->fds.end(), -1); + + create(0); + }); + + resource->setCreateImmed([this](CZwpLinuxBufferParamsV1* r, uint32_t id, int32_t w, int32_t h, uint32_t fmt, zwpLinuxBufferParamsV1Flags flags) { + if (used) { + r->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "Already used"); + return; + } + + if (flags > 0) { + r->sendFailed(); + LOGM(ERR, "DMABUF flags are not supported"); + return; + } + + attrs->size = {w, h}; + attrs->format = fmt; + attrs->planes = 4 - std::count(attrs->fds.begin(), attrs->fds.end(), -1); + + create(id); + }); +} + +CLinuxDMABBUFParamsResource::~CLinuxDMABBUFParamsResource() { + ; +} + +bool CLinuxDMABBUFParamsResource::good() { + return resource->resource(); +} + +void CLinuxDMABBUFParamsResource::create(uint32_t id) { + used = true; + + if (!verify()) { + LOGM(ERR, "Failed creating a dmabuf: verify() said no"); + return; // if verify failed, we errored the resource. + } + + if (!commence()) { + LOGM(ERR, "Failed creating a dmabuf: commence() said no"); + resource->sendFailed(); + return; + } + + LOGM(LOG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs->size, attrs->format, attrs->planes); + for (int i = 0; i < attrs->planes; ++i) { + LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs->modifier, attrs->fds[i], attrs->strides[i], attrs->offsets[i]); + } + + auto buf = PROTO::linuxDma->m_vBuffers.emplace_back(makeShared(id, resource->client(), *attrs)); + + if (!buf->good() || !buf->buffer->success) { + resource->sendFailed(); + return; + } + + if (!id) + resource->sendCreated(PROTO::linuxDma->m_vBuffers.back()->buffer->resource->getResource()); + + createdBuffer = buf; +} + +bool CLinuxDMABBUFParamsResource::commence() { + if (PROTO::linuxDma->mainDeviceFD < 0) + return true; + + for (int i = 0; i < attrs->planes; i++) { + uint32_t handle = 0; + + if (drmPrimeFDToHandle(PROTO::linuxDma->mainDeviceFD, attrs->fds.at(i), &handle)) { + LOGM(ERR, "Failed to import dmabuf fd"); + return false; + } + + if (drmCloseBufferHandle(PROTO::linuxDma->mainDeviceFD, handle)) { + LOGM(ERR, "Failed to close dmabuf handle"); + return false; + } + } + + return true; +} + +bool CLinuxDMABBUFParamsResource::verify() { + if (attrs->planes <= 0) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "No planes added"); + return false; + } + + if (attrs->fds.at(0) < 0) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "No plane 0"); + return false; + } + + bool empty = false; + for (auto& plane : attrs->fds) { + if (empty && plane != -1) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, "Gap in planes"); + return false; + } + + if (plane == -1) { + empty = true; + continue; + } + } + + if (attrs->size.x < 1 || attrs->size.y < 1) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, "x/y < 1"); + return false; + } + + for (size_t i = 0; i < (size_t)attrs->planes; ++i) { + if ((uint64_t)attrs->offsets.at(i) + (uint64_t)attrs->strides.at(i) * attrs->size.y > UINT32_MAX) { + resource->error(ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + std::format("size overflow on plane {}: offset {} + stride {} * height {} = {}, overflows UINT32_MAX", i, (uint64_t)attrs->offsets.at(i), + (uint64_t)attrs->strides.at(i), attrs->size.y, (uint64_t)attrs->offsets.at(i) + (uint64_t)attrs->strides.at(i))); + return false; + } + } + + return true; +} + +CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); }); + + if (surface) + LOGM(ERR, "FIXME: surface feedback stub"); + + auto* feedback = PROTO::linuxDma->defaultFeedback.get(); + + resource->sendFormatTable(feedback->tableFD, feedback->tableLen); + + // send default feedback + struct wl_array deviceArr = { + .size = sizeof(feedback->mainDevice), + .data = (void*)&feedback->mainDevice, + }; + resource->sendMainDevice(&deviceArr); + resource->sendTrancheTargetDevice(&deviceArr); + resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)0); + + wl_array indices; + wl_array_init(&indices); + for (size_t i = 0; i < feedback->tranches.size(); ++i) { + *((uint16_t*)wl_array_add(&indices, sizeof(uint16_t))) = i; + } + resource->sendTrancheFormats(&indices); + wl_array_release(&indices); + resource->sendTrancheDone(); + + resource->sendDone(); +} + +CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() { + ; +} + +bool CLinuxDMABUFFeedbackResource::good() { + return resource->resource(); +} + +CLinuxDMABUFResource::CLinuxDMABUFResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CZwpLinuxDmabufV1* r) { PROTO::linuxDma->destroyResource(this); }); + resource->setDestroy([this](CZwpLinuxDmabufV1* r) { PROTO::linuxDma->destroyResource(this); }); + + resource->setGetDefaultFeedback([](CZwpLinuxDmabufV1* r, uint32_t id) { + const auto RESOURCE = + PROTO::linuxDma->m_vFeedbacks.emplace_back(makeShared(makeShared(r->client(), r->version(), id), nullptr)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::linuxDma->m_vFeedbacks.pop_back(); + return; + } + }); + + resource->setGetSurfaceFeedback([](CZwpLinuxDmabufV1* r, uint32_t id, wl_resource* surf) { + const auto RESOURCE = PROTO::linuxDma->m_vFeedbacks.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surf))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::linuxDma->m_vFeedbacks.pop_back(); + return; + } + }); + + resource->setCreateParams([](CZwpLinuxDmabufV1* r, uint32_t id) { + const auto RESOURCE = PROTO::linuxDma->m_vParams.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::linuxDma->m_vParams.pop_back(); + return; + } + }); + + if (resource->version() < 4) + sendMods(); +} + +bool CLinuxDMABUFResource::good() { + return resource->resource(); +} + +void CLinuxDMABUFResource::sendMods() { + for (auto& [fmt, mod] : PROTO::linuxDma->defaultFeedback->tranches) { + if (resource->version() < 3) { + if (mod == DRM_FORMAT_MOD_INVALID) + resource->sendFormat(fmt); + continue; + } + + // TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 + + resource->sendModifier(fmt, mod >> 32, mod & 0xFFFFFFFF); + } +} + +CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + static auto P = g_pHookSystem->hookDynamic("ready", [this](void* self, SCallbackInfo& info, std::any d) { + int rendererFD = wlr_renderer_get_drm_fd(g_pCompositor->m_sWLRRenderer); + auto dev = devIDFromFD(rendererFD); + + if (!dev.has_value()) { + LOGM(ERR, "failed to get drm dev"); + PROTO::linuxDma.reset(); + return; + } + + mainDevice = *dev; + + auto fmts = g_pHyprOpenGL->getDRMFormats(); + + SDMABufTranche tranche = { + .device = *dev, + .formats = fmts, + }; + + std::vector tches; + tches.push_back(tranche); + + defaultFeedback = std::make_unique(*dev, tches); + + drmDevice* device = nullptr; + if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) { + LOGM(ERR, "failed to get drm dev"); + PROTO::linuxDma.reset(); + return; + } + + if (device->available_nodes & (1 << DRM_NODE_RENDER)) { + const char* name = device->nodes[DRM_NODE_RENDER]; + mainDeviceFD = open(name, O_RDWR | O_CLOEXEC); + drmFreeDevice(&device); + if (mainDeviceFD < 0) { + LOGM(ERR, "failed to open drm dev"); + PROTO::linuxDma.reset(); + return; + } + } else { + LOGM(ERR, "DRM device {} has no render node!!", device->nodes[DRM_NODE_PRIMARY]); + drmFreeDevice(&device); + } + }); +} + +CLinuxDMABufV1Protocol::~CLinuxDMABufV1Protocol() { + if (mainDeviceFD >= 0) + close(mainDeviceFD); +} + +void CLinuxDMABufV1Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABUFResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABUFFeedbackResource* resource) { + std::erase_if(m_vFeedbacks, [&](const auto& other) { return other.get() == resource; }); +} + +void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABBUFParamsResource* resource) { + std::erase_if(m_vParams, [&](const auto& other) { return other.get() == resource; }); +} + +void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) { + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/LinuxDMABUF.hpp b/src/protocols/LinuxDMABUF.hpp new file mode 100644 index 00000000..2b8ce736 --- /dev/null +++ b/src/protocols/LinuxDMABUF.hpp @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "wayland.hpp" +#include "linux-dmabuf-v1.hpp" +#include "../helpers/signal/Signal.hpp" + +class CDMABuffer; +struct SDRMFormat; +struct SDMABUFAttrs; +class CWLSurfaceResource; + +class CLinuxDMABuffer { + public: + CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs); + ~CLinuxDMABuffer(); + + bool good(); + + private: + SP buffer; + + struct { + CHyprSignalListener bufferResourceDestroy; + } listeners; + + friend class CLinuxDMABBUFParamsResource; +}; + +#pragma pack(push, 1) +struct SDMABUFFeedbackTableEntry { + uint32_t fmt = 0; + char pad[4]; + uint64_t modifier = 0; +}; +#pragma pack(pop) + +class SCompiledDMABUFTranche { + dev_t device = 0; + uint32_t flags = 0; + std::vector indices; +}; + +struct SDMABufTranche { + dev_t device = 0; + uint32_t flags = 0; + std::vector formats; +}; + +class CCompiledDMABUFFeedback { + public: + CCompiledDMABUFFeedback(dev_t device, std::vector tranches); + ~CCompiledDMABUFFeedback(); + + dev_t mainDevice = 0; + int tableFD = -1; + size_t tableLen = 0; + std::vector> tranches; +}; + +class CLinuxDMABBUFParamsResource { + public: + CLinuxDMABBUFParamsResource(SP resource_); + ~CLinuxDMABBUFParamsResource(); + + bool good(); + void create(uint32_t id); // 0 means not immed + + SP attrs; + WP createdBuffer; + bool used = false; + + private: + SP resource; + + bool verify(); + bool commence(); +}; + +class CLinuxDMABUFFeedbackResource { + public: + CLinuxDMABUFFeedbackResource(SP resource_, SP surface_); + ~CLinuxDMABUFFeedbackResource(); + + bool good(); + + SP surface; // optional, for surface feedbacks + + private: + SP resource; +}; + +class CLinuxDMABUFResource { + public: + CLinuxDMABUFResource(SP resource_); + + bool good(); + void sendMods(); + + private: + SP resource; +}; + +class CLinuxDMABufV1Protocol : public IWaylandProtocol { + public: + CLinuxDMABufV1Protocol(const wl_interface* iface, const int& ver, const std::string& name); + ~CLinuxDMABufV1Protocol(); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CLinuxDMABUFResource* resource); + void destroyResource(CLinuxDMABUFFeedbackResource* resource); + void destroyResource(CLinuxDMABBUFParamsResource* resource); + void destroyResource(CLinuxDMABuffer* resource); + + // + std::vector> m_vManagers; + std::vector> m_vFeedbacks; + std::vector> m_vParams; + std::vector> m_vBuffers; + + UP defaultFeedback; + dev_t mainDevice; + int mainDeviceFD = -1; + + friend class CLinuxDMABUFResource; + friend class CLinuxDMABUFFeedbackResource; + friend class CLinuxDMABBUFParamsResource; + friend class CLinuxDMABuffer; +}; + +namespace PROTO { + inline UP linuxDma; +}; diff --git a/src/protocols/MesaDRM.cpp b/src/protocols/MesaDRM.cpp new file mode 100644 index 00000000..0bcf4a9c --- /dev/null +++ b/src/protocols/MesaDRM.cpp @@ -0,0 +1,133 @@ +#include "MesaDRM.hpp" +#include +#include +#include "../Compositor.hpp" +#include +#include "types/WLBuffer.hpp" + +#define LOGM PROTO::mesaDRM->protoLog + +CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, SDMABUFAttrs attrs_) { + LOGM(LOG, "Creating a Mesa dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs_.size, attrs_.format, attrs_.planes); + for (int i = 0; i < attrs_.planes; ++i) { + LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs_.modifier, attrs_.fds[i], attrs_.strides[i], attrs_.offsets[i]); + } + + buffer = makeShared(id, client, attrs_); + buffer->resource->buffer = buffer; + + listeners.bufferResourceDestroy = buffer->events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); + PROTO::mesaDRM->destroyResource(this); + }); + + if (!buffer->success) + LOGM(ERR, "Possibly compositor bug: buffer failed to create"); +} + +CMesaDRMBufferResource::~CMesaDRMBufferResource() { + if (buffer && buffer->resource) + buffer->resource->sendRelease(); + buffer.reset(); + listeners.bufferResourceDestroy.reset(); +} + +bool CMesaDRMBufferResource::good() { + return buffer && buffer->good(); +} + +CMesaDRMResource::CMesaDRMResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlDrm* r) { PROTO::mesaDRM->destroyResource(this); }); + + resource->setAuthenticate([this](CWlDrm* r, uint32_t token) { + // we don't need this + resource->sendAuthenticated(); + }); + + resource->setCreateBuffer([](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, uint32_t) { r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead"); }); + + resource->setCreatePlanarBuffer([](CWlDrm* r, uint32_t, uint32_t, int32_t, int32_t, uint32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) { + r->error(WL_DRM_ERROR_INVALID_NAME, "Not supported, use prime instead"); + }); + + resource->setCreatePrimeBuffer( + [this](CWlDrm* r, uint32_t id, int32_t nameFd, int32_t w, int32_t h, uint32_t fmt, int32_t off0, int32_t str0, int32_t off1, int32_t str1, int32_t off2, int32_t str2) { + if (off0 < 0 || w <= 0 || h <= 0) { + r->error(WL_DRM_ERROR_INVALID_FORMAT, "Invalid w, h, or offset"); + return; + } + + SDMABUFAttrs attrs; + attrs.success = true; + attrs.size = {w, h}; + attrs.modifier = DRM_FORMAT_MOD_INVALID; + attrs.planes = 1; + attrs.offsets[0] = off0; + attrs.strides[0] = str0; + attrs.fds[0] = nameFd; + attrs.format = fmt; + + const auto RESOURCE = PROTO::mesaDRM->m_vBuffers.emplace_back(makeShared(id, resource->client(), attrs)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::mesaDRM->m_vBuffers.pop_back(); + return; + } + + // append instance so that buffer knows its owner + RESOURCE->buffer->resource->buffer = RESOURCE->buffer; + }); + + resource->sendDevice(PROTO::mesaDRM->nodeName.c_str()); + resource->sendCapabilities(WL_DRM_CAPABILITY_PRIME); + + auto fmts = g_pHyprOpenGL->getDRMFormats(); + for (auto& fmt : fmts) { + resource->sendFormat(fmt.format); + } +} + +bool CMesaDRMResource::good() { + return resource->resource(); +} + +CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + drmDevice* dev = nullptr; + int drmFD = wlr_renderer_get_drm_fd(g_pCompositor->m_sWLRRenderer); + if (drmGetDevice2(drmFD, 0, &dev) != 0) { + LOGM(ERR, "Failed to get device"); + PROTO::mesaDRM.reset(); + return; + } + + if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { + nodeName = dev->nodes[DRM_NODE_RENDER]; + } else { + ASSERT(dev->available_nodes & (1 << DRM_NODE_PRIMARY)); + LOGM(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]); + nodeName = dev->nodes[DRM_NODE_PRIMARY]; + } + drmFreeDevice(&dev); +} + +void CMesaDRMProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CMesaDRMProtocol::destroyResource(CMesaDRMResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CMesaDRMProtocol::destroyResource(CMesaDRMBufferResource* resource) { + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/MesaDRM.hpp b/src/protocols/MesaDRM.hpp new file mode 100644 index 00000000..ad31a182 --- /dev/null +++ b/src/protocols/MesaDRM.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "wayland-drm.hpp" +#include "types/Buffer.hpp" +#include "types/DMABuffer.hpp" + +class CMesaDRMBufferResource { + public: + CMesaDRMBufferResource(uint32_t id, wl_client* client, SDMABUFAttrs attrs); + ~CMesaDRMBufferResource(); + + bool good(); + + private: + SP buffer; + + struct { + CHyprSignalListener bufferResourceDestroy; + } listeners; + + friend class CMesaDRMResource; +}; + +class CMesaDRMResource { + public: + CMesaDRMResource(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CMesaDRMProtocol : public IWaylandProtocol { + public: + CMesaDRMProtocol(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(CMesaDRMResource* resource); + void destroyResource(CMesaDRMBufferResource* resource); + + // + std::vector> m_vManagers; + std::vector> m_vBuffers; + + std::string nodeName = ""; + + friend class CMesaDRMResource; + friend class CMesaDRMBufferResource; +}; + +namespace PROTO { + inline UP mesaDRM; +}; diff --git a/src/protocols/OutputManagement.cpp b/src/protocols/OutputManagement.cpp index e7779726..68c50193 100644 --- a/src/protocols/OutputManagement.cpp +++ b/src/protocols/OutputManagement.cpp @@ -106,6 +106,9 @@ COutputHead::COutputHead(SP resource_, CMonitor* pMonitor_) : } pMonitor = nullptr; + for (auto& m : PROTO::outputManagement->m_vManagers) { + m->sendDone(); + } }); listeners.monitorModeChange = pMonitor->events.modeChanged.registerListener([this](std::any d) { updateMode(); }); @@ -305,6 +308,8 @@ COutputConfiguration::COutputConfiguration(SP resour LOGM(LOG, "disableHead on {}", PMONITOR->szName); PMONITOR->activeMonitorRule.disabled = true; + if (!g_pConfigManager->replaceMonitorRule(PMONITOR->activeMonitorRule)) + g_pConfigManager->appendMonitorRule(PMONITOR->activeMonitorRule); g_pHyprRenderer->applyMonitorRule(PMONITOR, &PMONITOR->activeMonitorRule, false); }); @@ -356,6 +361,7 @@ bool COutputConfiguration::applyTestConfiguration(bool test) { SMonitorRule newRule = PMONITOR->activeMonitorRule; newRule.name = PMONITOR->szName; + newRule.disabled = false; if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) { newRule.resolution = {head->state.mode->getMode()->width, head->state.mode->getMode()->height}; @@ -380,7 +386,8 @@ bool COutputConfiguration::applyTestConfiguration(bool test) { // reset properties for next set. head->committedProperties = 0; - g_pConfigManager->appendMonitorRule(newRule); + if (!g_pConfigManager->replaceMonitorRule(newRule)) + g_pConfigManager->appendMonitorRule(newRule); g_pConfigManager->m_bWantsMonitorReload = true; } diff --git a/src/protocols/OutputPower.cpp b/src/protocols/OutputPower.cpp index db241048..ef287cfa 100644 --- a/src/protocols/OutputPower.cpp +++ b/src/protocols/OutputPower.cpp @@ -1,5 +1,6 @@ #include "OutputPower.hpp" #include "../Compositor.hpp" +#include "core/Output.hpp" #define LOGM PROTO::outputPower->protoLog @@ -61,15 +62,15 @@ void COutputPowerProtocol::destroyOutputPower(COutputPower* power) { void COutputPowerProtocol::onGetOutputPower(CZwlrOutputPowerManagerV1* pMgr, uint32_t id, wl_resource* output) { - const auto PMONITOR = g_pCompositor->getMonitorFromOutput(wlr_output_from_resource(output)); + const auto OUTPUT = CWLOutputResource::fromResource(output); - if (!PMONITOR) { + if (!OUTPUT) { pMgr->error(0, "Invalid output resource"); return; } const auto CLIENT = pMgr->client(); - const auto RESOURCE = m_vOutputPowers.emplace_back(std::make_unique(makeShared(CLIENT, pMgr->version(), id), PMONITOR)).get(); + const auto RESOURCE = m_vOutputPowers.emplace_back(std::make_unique(makeShared(CLIENT, pMgr->version(), id), OUTPUT->monitor.get())).get(); if (!RESOURCE->good()) { pMgr->noMemory(); diff --git a/src/protocols/PointerConstraints.cpp b/src/protocols/PointerConstraints.cpp index 87abdcdd..c7b78a5b 100644 --- a/src/protocols/PointerConstraints.cpp +++ b/src/protocols/PointerConstraints.cpp @@ -3,10 +3,11 @@ #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" #include "../managers/SeatManager.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::constraints->protoLog -CPointerConstraint::CPointerConstraint(SP resource_, wlr_surface* surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime) : +CPointerConstraint::CPointerConstraint(SP resource_, SP surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime) : resourceL(resource_), locked(true) { if (!resource_->resource()) return; @@ -14,13 +15,13 @@ CPointerConstraint::CPointerConstraint(SP resource_, wlr_su resource_->setOnDestroy([this](CZwpLockedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); resource_->setDestroy([this](CZwpLockedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); - pHLSurface = CWLSurface::surfaceFromWlr(surf); + pHLSurface = CWLSurface::fromResource(surf); if (!pHLSurface) return; if (region_) - region.set(wlr_region_from_resource(region_)); + region.set(CWLRegionResource::fromResource(region_)->region); resource_->setSetRegion([this](CZwpLockedPointerV1* p, wl_resource* region) { onSetRegion(region); }); resource_->setSetCursorPositionHint([this](CZwpLockedPointerV1* p, wl_fixed_t x, wl_fixed_t y) { @@ -45,7 +46,7 @@ CPointerConstraint::CPointerConstraint(SP resource_, wlr_su sharedConstructions(); } -CPointerConstraint::CPointerConstraint(SP resource_, wlr_surface* surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime) : +CPointerConstraint::CPointerConstraint(SP resource_, SP surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime) : resourceC(resource_), locked(false) { if (!resource_->resource()) return; @@ -53,13 +54,13 @@ CPointerConstraint::CPointerConstraint(SP resource_, wlr_ resource_->setOnDestroy([this](CZwpConfinedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); resource_->setDestroy([this](CZwpConfinedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); }); - pHLSurface = CWLSurface::surfaceFromWlr(surf); + pHLSurface = CWLSurface::fromResource(surf); if (!pHLSurface) return; if (region_) - region.set(wlr_region_from_resource(region_)); + region.set(CWLRegionResource::fromResource(region_)->region); resource_->setSetRegion([this](CZwpConfinedPointerV1* p, wl_resource* region) { onSetRegion(region); }); @@ -79,7 +80,7 @@ CPointerConstraint::~CPointerConstraint() { void CPointerConstraint::sharedConstructions() { if (pHLSurface) { listeners.destroySurface = pHLSurface->events.destroy.registerListener([this](std::any d) { - pHLSurface = nullptr; + pHLSurface.reset(); if (active) deactivate(); @@ -92,7 +93,7 @@ void CPointerConstraint::sharedConstructions() { cursorPosOnActivate = g_pInputManager->getMouseCoordsInternal(); - if (g_pCompositor->m_pLastFocus == pHLSurface->wlr()) + if (g_pCompositor->m_pLastFocus == pHLSurface->resource()) activate(); } @@ -126,10 +127,10 @@ void CPointerConstraint::activate() { return; // TODO: hack, probably not a super duper great idea - if (g_pSeatManager->state.pointerFocus != pHLSurface->wlr()) { + if (g_pSeatManager->state.pointerFocus != pHLSurface->resource()) { const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal(); const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{}; - g_pSeatManager->setPointerFocus(pHLSurface->wlr(), LOCAL); + g_pSeatManager->setPointerFocus(pHLSurface->resource(), LOCAL); } if (locked) @@ -152,15 +153,15 @@ void CPointerConstraint::onSetRegion(wl_resource* wlRegion) { return; } - const auto REGION = wlr_region_from_resource(wlRegion); + const auto REGION = region.set(CWLRegionResource::fromResource(wlRegion)->region); region.set(REGION); positionHint = region.closestPoint(positionHint); g_pInputManager->simulateMouseMovement(); // to warp the cursor if anything's amiss } -CWLSurface* CPointerConstraint::owner() { - return pHLSurface; +SP CPointerConstraint::owner() { + return pHLSurface.lock(); } CRegion CPointerConstraint::logicConstraintRegion() { @@ -241,7 +242,7 @@ void CPointerConstraintsProtocol::onLockPointer(CZwpPointerConstraintsV1* pMgr, zwpPointerConstraintsV1Lifetime lifetime) { const auto CLIENT = pMgr->client(); const auto RESOURCE = m_vConstraints.emplace_back( - makeShared(makeShared(CLIENT, pMgr->version(), id), wlr_surface_from_resource(surface), region, lifetime)); + makeShared(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surface), region, lifetime)); onNewConstraint(RESOURCE, pMgr); } @@ -250,7 +251,7 @@ void CPointerConstraintsProtocol::onConfinePointer(CZwpPointerConstraintsV1* pMg zwpPointerConstraintsV1Lifetime lifetime) { const auto CLIENT = pMgr->client(); const auto RESOURCE = m_vConstraints.emplace_back( - makeShared(makeShared(CLIENT, pMgr->version(), id), wlr_surface_from_resource(surface), region, lifetime)); + makeShared(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surface), region, lifetime)); onNewConstraint(RESOURCE, pMgr); } diff --git a/src/protocols/PointerConstraints.hpp b/src/protocols/PointerConstraints.hpp index 93e57c46..06bebb02 100644 --- a/src/protocols/PointerConstraints.hpp +++ b/src/protocols/PointerConstraints.hpp @@ -12,31 +12,32 @@ #include "../helpers/signal/Listener.hpp" class CWLSurface; +class CWLSurfaceResource; class CPointerConstraint { public: - CPointerConstraint(SP resource_, wlr_surface* surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime); - CPointerConstraint(SP resource_, wlr_surface* surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime); + CPointerConstraint(SP resource_, SP surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime); + CPointerConstraint(SP resource_, SP surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime); ~CPointerConstraint(); - bool good(); + bool good(); - void deactivate(); - void activate(); - bool isActive(); + void deactivate(); + void activate(); + bool isActive(); - CWLSurface* owner(); + SP owner(); - CRegion logicConstraintRegion(); - bool isLocked(); - Vector2D logicPositionHint(); + CRegion logicConstraintRegion(); + bool isLocked(); + Vector2D logicPositionHint(); private: SP resourceL; SP resourceC; wl_client* pClient = nullptr; - CWLSurface* pHLSurface = nullptr; + WP pHLSurface; CRegion region; bool hintSet = false; diff --git a/src/protocols/PointerGestures.cpp b/src/protocols/PointerGestures.cpp index 2bfd74da..86510779 100644 --- a/src/protocols/PointerGestures.cpp +++ b/src/protocols/PointerGestures.cpp @@ -2,6 +2,7 @@ #include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "core/Seat.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::pointerGestures->protoLog @@ -116,7 +117,7 @@ void CPointerGesturesProtocol::swipeBegin(uint32_t timeMs, uint32_t fingers) { if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->getResource()->resource(), fingers); } } @@ -162,7 +163,7 @@ void CPointerGesturesProtocol::pinchBegin(uint32_t timeMs, uint32_t fingers) { if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->getResource()->resource(), fingers); } } @@ -208,7 +209,7 @@ void CPointerGesturesProtocol::holdBegin(uint32_t timeMs, uint32_t fingers) { if (sw->resource->client() != FOCUSEDCLIENT) continue; - sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->resource, fingers); + sw->resource->sendBegin(SERIAL, timeMs, g_pSeatManager->state.pointerFocus->getResource()->resource(), fingers); } } diff --git a/src/protocols/PresentationTime.cpp b/src/protocols/PresentationTime.cpp index e21c8403..0275b53f 100644 --- a/src/protocols/PresentationTime.cpp +++ b/src/protocols/PresentationTime.cpp @@ -2,10 +2,11 @@ #include #include "../helpers/Monitor.hpp" #include "../managers/HookSystemManager.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::presentation->protoLog -CQueuedPresentationData::CQueuedPresentationData(wlr_surface* surf) : surface(surf) { +CQueuedPresentationData::CQueuedPresentationData(SP surf) : surface(surf) { ; } @@ -25,7 +26,7 @@ void CQueuedPresentationData::discarded() { wasPresented = false; } -CPresentationFeedback::CPresentationFeedback(SP resource_, wlr_surface* surf) : resource(resource_), surface(surf) { +CPresentationFeedback::CPresentationFeedback(SP resource_, SP surf) : resource(resource_), surface(surf) { if (!good()) return; @@ -69,7 +70,7 @@ void CPresentationFeedback::sendQueued(SP data, timespe CPresentationProtocol::CPresentationProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { static auto P = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) { const auto PMONITOR = std::any_cast(param); - std::erase_if(m_vQueue, [PMONITOR, this](const auto& other) { return !other->surface || other->pMonitor == PMONITOR; }); + std::erase_if(m_vQueue, [PMONITOR](const auto& other) { return !other->surface || other->pMonitor == PMONITOR; }); }); } @@ -92,7 +93,8 @@ void CPresentationProtocol::destroyResource(CPresentationFeedback* feedback) { void CPresentationProtocol::onGetFeedback(CWpPresentation* pMgr, wl_resource* surf, uint32_t id) { const auto CLIENT = pMgr->client(); const auto RESOURCE = - m_vFeedbacks.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), wlr_surface_from_resource(surf))).get(); + m_vFeedbacks.emplace_back(makeShared(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surf))) + .get(); if (!RESOURCE->good()) { pMgr->noMemory(); @@ -116,8 +118,8 @@ void CPresentationProtocol::onPresented(CMonitor* pMonitor, timespec* when, uint } } - std::erase_if(m_vFeedbacks, [pMonitor, this](const auto& other) { return !other->surface || other->done; }); - std::erase_if(m_vQueue, [pMonitor, this](const auto& other) { return !other->surface || other->pMonitor == pMonitor || !other->pMonitor; }); + std::erase_if(m_vFeedbacks, [](const auto& other) { return !other->surface || other->done; }); + std::erase_if(m_vQueue, [pMonitor](const auto& other) { return !other->surface || other->pMonitor == pMonitor || !other->pMonitor; }); } void CPresentationProtocol::queueData(SP data) { diff --git a/src/protocols/PresentationTime.hpp b/src/protocols/PresentationTime.hpp index 2df1c781..2c6ce3e9 100644 --- a/src/protocols/PresentationTime.hpp +++ b/src/protocols/PresentationTime.hpp @@ -7,10 +7,11 @@ #include "presentation-time.hpp" class CMonitor; +class CWLSurfaceResource; class CQueuedPresentationData { public: - CQueuedPresentationData(wlr_surface* surf); + CQueuedPresentationData(SP surf); void setPresentationType(bool zeroCopy); void attachMonitor(CMonitor* pMonitor); @@ -19,10 +20,10 @@ class CQueuedPresentationData { void discarded(); private: - bool wasPresented = false; - bool zeroCopy = false; - CMonitor* pMonitor = nullptr; - wlr_surface* surface = nullptr; // READ-ONLY + bool wasPresented = false; + bool zeroCopy = false; + CMonitor* pMonitor = nullptr; + WP surface; DYNLISTENER(destroySurface); @@ -32,7 +33,7 @@ class CQueuedPresentationData { class CPresentationFeedback { public: - CPresentationFeedback(SP resource_, wlr_surface* surf); + CPresentationFeedback(SP resource_, SP surf); bool good(); @@ -40,8 +41,8 @@ class CPresentationFeedback { private: SP resource; - wlr_surface* surface = nullptr; // READ-ONLY - bool done = false; + WP surface; + bool done = false; friend class CPresentationProtocol; }; diff --git a/src/protocols/Screencopy.cpp b/src/protocols/Screencopy.cpp index 7c25ed3f..0c4eac86 100644 --- a/src/protocols/Screencopy.cpp +++ b/src/protocols/Screencopy.cpp @@ -2,11 +2,13 @@ #include "../Compositor.hpp" #include "../managers/eventLoop/EventLoopManager.hpp" #include "../managers/PointerManager.hpp" +#include "core/Output.hpp" +#include "types/WLBuffer.hpp" +#include "types/Buffer.hpp" +#include "../helpers/Format.hpp" #include -#include "ToplevelExportWlrFuncs.hpp" - #define SCREENCOPY_VERSION 3 static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) { @@ -202,8 +204,8 @@ void CScreencopyProtocolManager::removeFrame(SScreencopyFrame* frame, bool force std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; }); wl_resource_set_user_data(frame->resource, nullptr); - if (frame->buffer && frame->buffer->n_locks > 0) - wlr_buffer_unlock(frame->buffer); + if (frame->buffer && frame->buffer->locked()) + frame->buffer->unlock(); removeClient(frame->client, force); m_lFrames.remove(*frame); } @@ -214,7 +216,7 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r const auto PFRAME = &m_lFrames.emplace_back(); PFRAME->overlayCursor = !!overlay_cursor; PFRAME->resource = wl_resource_create(client, &zwlr_screencopy_frame_v1_interface, wl_resource_get_version(resource), frame); - PFRAME->pMonitor = g_pCompositor->getMonitorFromOutput(wlr_output_from_resource(output)); + PFRAME->pMonitor = CWLOutputResource::fromResource(output)->monitor.get(); if (!PFRAME->pMonitor) { Debug::log(ERR, "client requested sharing of a monitor that doesnt exist"); @@ -256,7 +258,7 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r return; } - const auto PSHMINFO = drm_get_pixel_format_info(PFRAME->shmFormat); + const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(PFRAME->shmFormat); if (!PSHMINFO) { Debug::log(ERR, "No pixel format supported by renderer in capture output"); zwlr_screencopy_frame_v1_send_failed(PFRAME->resource); @@ -279,9 +281,9 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r wlr_output_effective_resolution(PFRAME->pMonitor->output, &ow, &oh); PFRAME->box.transform(PFRAME->pMonitor->transform, ow, oh).scale(PFRAME->pMonitor->scale).round(); - PFRAME->shmStride = pixel_format_info_min_stride(PSHMINFO, PFRAME->box.w); + PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w); - zwlr_screencopy_frame_v1_send_buffer(PFRAME->resource, convert_drm_format_to_wl_shm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride); + zwlr_screencopy_frame_v1_send_buffer(PFRAME->resource, FormatUtils::drmToShm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride); if (wl_resource_get_version(resource) >= 3) { if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) { @@ -307,7 +309,7 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou return; } - const auto PBUFFER = wlr_buffer_try_from_resource(buffer); + const auto PBUFFER = CWLBufferResource::fromResource(buffer); if (!PBUFFER) { Debug::log(ERR, "[sc] invalid buffer in {:x}", (uintptr_t)PFRAME); wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); @@ -315,7 +317,9 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou return; } - if (PBUFFER->width != PFRAME->box.width || PBUFFER->height != PFRAME->box.height) { + PBUFFER->buffer->lock(); + + if (PBUFFER->buffer->size != PFRAME->box.size()) { Debug::log(ERR, "[sc] invalid dimensions in {:x}", (uintptr_t)PFRAME); wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); removeFrame(PFRAME); @@ -329,28 +333,22 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou return; } - wlr_dmabuf_attributes dmabufAttrs; - void* wlrBufferAccessData; - uint32_t wlrBufferAccessFormat; - size_t wlrBufferAccessStride; - if (wlr_buffer_get_dmabuf(PBUFFER, &dmabufAttrs)) { - PFRAME->bufferCap = WLR_BUFFER_CAP_DMABUF; + if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) { + PFRAME->bufferDMA = true; - if (dmabufAttrs.format != PFRAME->dmabufFormat) { + if (attrs.format != PFRAME->dmabufFormat) { Debug::log(ERR, "[sc] invalid buffer dma format in {:x}", (uintptr_t)PFRAME); wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); removeFrame(PFRAME); return; } - } else if (wlr_buffer_begin_data_ptr_access(PBUFFER, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &wlrBufferAccessData, &wlrBufferAccessFormat, &wlrBufferAccessStride)) { - wlr_buffer_end_data_ptr_access(PBUFFER); - - if (wlrBufferAccessFormat != PFRAME->shmFormat) { + } else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) { + if (attrs.format != PFRAME->shmFormat) { Debug::log(ERR, "[sc] invalid buffer shm format in {:x}", (uintptr_t)PFRAME); wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); removeFrame(PFRAME); return; - } else if ((int)wlrBufferAccessStride != PFRAME->shmStride) { + } else if ((int)attrs.stride != PFRAME->shmStride) { Debug::log(ERR, "[sc] invalid buffer shm stride in {:x}", (uintptr_t)PFRAME); wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride"); removeFrame(PFRAME); @@ -363,7 +361,7 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou return; } - PFRAME->buffer = PBUFFER; + PFRAME->buffer = PBUFFER->buffer; m_vFramesAwaitingWrite.emplace_back(PFRAME); @@ -432,7 +430,7 @@ void CScreencopyProtocolManager::shareFrame(SScreencopyFrame* frame) { clock_gettime(CLOCK_MONOTONIC, &now); uint32_t flags = 0; - if (frame->bufferCap == WLR_BUFFER_CAP_DMABUF) { + if (frame->bufferDMA) { if (!copyFrameDmabuf(frame)) { Debug::log(ERR, "[sc] dmabuf copy failed in {:x}", (uintptr_t)frame); zwlr_screencopy_frame_v1_send_failed(frame->resource); @@ -471,7 +469,7 @@ void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) { // std::clamp(RECT.x2 - RECT.x1, 0, frame->buffer->width - RECT.x1), std::clamp(RECT.y2 - RECT.y1, 0, frame->buffer->height - RECT.y1)); // } - zwlr_screencopy_frame_v1_send_damage(frame->resource, 0, 0, frame->buffer->width, frame->buffer->height); + zwlr_screencopy_frame_v1_send_damage(frame->resource, 0, 0, frame->buffer->size.x, frame->buffer->size.y); } bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) { @@ -479,13 +477,10 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* if (!sourceTex) return false; - void* data; - uint32_t format; - size_t stride; - if (!wlr_buffer_begin_data_ptr_access(frame->buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) { - wlr_texture_destroy(sourceTex); - return false; - } + auto TEXTURE = makeShared(sourceTex); + + auto shm = frame->buffer->shm(); + auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; @@ -494,16 +489,15 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* CFramebuffer fb; fb.alloc(frame->box.w, frame->box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : frame->pMonitor->drmFormat); - if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb)) { + if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) { wlr_texture_destroy(sourceTex); - wlr_buffer_end_data_ptr_access(frame->buffer); return false; } CBox monbox = CBox{0, 0, frame->pMonitor->vecTransformedSize.x, frame->pMonitor->vecTransformedSize.y}.translate({-frame->box.x, -frame->box.y}); g_pHyprOpenGL->setMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTexture(sourceTex, &monbox, 1); + g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1); g_pHyprOpenGL->setRenderModifEnabled(true); g_pHyprOpenGL->setMonitorTransformEnabled(false); @@ -513,14 +507,15 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* glBindFramebuffer(GL_FRAMEBUFFER, fb.m_iFb); #endif - const auto PFORMAT = g_pHyprOpenGL->getPixelFormatFromDRM(format); + const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format); if (!PFORMAT) { g_pHyprRenderer->endRender(); wlr_texture_destroy(sourceTex); - wlr_buffer_end_data_ptr_access(frame->buffer); return false; } + auto glFormat = PFORMAT->flipRB ? GL_BGRA_EXT : GL_RGBA; + g_pHyprOpenGL->m_RenderData.blockScreenShader = true; g_pHyprRenderer->endRender(); @@ -530,21 +525,20 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* glPixelStorei(GL_PACK_ALIGNMENT, 1); - const wlr_pixel_format_info* drmFmtWlr = drm_get_pixel_format_info(format); - uint32_t packStride = pixel_format_info_min_stride(drmFmtWlr, frame->box.w); + const auto drmFmt = FormatUtils::getPixelFormatFromDRM(shm.format); + uint32_t packStride = FormatUtils::minStride(drmFmt, frame->box.w); - if (packStride == stride) { - glReadPixels(0, 0, frame->box.w, frame->box.h, PFORMAT->glFormat, PFORMAT->glType, data); + if (packStride == (uint32_t)shm.stride) { + glReadPixels(0, 0, frame->box.w, frame->box.h, glFormat, PFORMAT->glType, pixelData); } else { for (size_t i = 0; i < frame->box.h; ++i) { uint32_t y = i; - glReadPixels(0, y, frame->box.w, 1, PFORMAT->glFormat, PFORMAT->glType, ((unsigned char*)data) + i * stride); + glReadPixels(0, y, frame->box.w, 1, glFormat, PFORMAT->glType, ((unsigned char*)pixelData) + i * shm.stride); } } g_pHyprOpenGL->m_RenderData.pMonitor = nullptr; - wlr_buffer_end_data_ptr_access(frame->buffer); wlr_texture_destroy(sourceTex); return true; @@ -555,9 +549,11 @@ bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) { if (!sourceTex) return false; + auto TEXTURE = makeShared(sourceTex); + CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX}; - if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer)) + if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock(), nullptr, true)) return false; CBox monbox = CBox{0, 0, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y} @@ -565,7 +561,7 @@ bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) { .transform(wlr_output_transform_invert(frame->pMonitor->output->transform), frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y); g_pHyprOpenGL->setMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTexture(sourceTex, &monbox, 1); + g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1); g_pHyprOpenGL->setRenderModifEnabled(true); g_pHyprOpenGL->setMonitorTransformEnabled(false); diff --git a/src/protocols/Screencopy.hpp b/src/protocols/Screencopy.hpp index 1bdf6963..be434285 100644 --- a/src/protocols/Screencopy.hpp +++ b/src/protocols/Screencopy.hpp @@ -10,6 +10,7 @@ #include "../managers/eventLoop/EventLoopTimer.hpp" class CMonitor; +class IWLBuffer; enum eClientOwners { CLIENT_SCREENCOPY = 0, @@ -53,9 +54,9 @@ struct SScreencopyFrame { bool withDamage = false; bool lockedSWCursors = false; - wlr_buffer_cap bufferCap = WLR_BUFFER_CAP_SHM; + bool bufferDMA = false; - wlr_buffer* buffer = nullptr; + WP buffer; CMonitor* pMonitor = nullptr; PHLWINDOWREF pWindow; diff --git a/src/protocols/ServerDecorationKDE.cpp b/src/protocols/ServerDecorationKDE.cpp index d47467b3..42da52a9 100644 --- a/src/protocols/ServerDecorationKDE.cpp +++ b/src/protocols/ServerDecorationKDE.cpp @@ -1,8 +1,9 @@ #include "ServerDecorationKDE.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::serverDecorationKDE->protoLog -CServerDecorationKDE::CServerDecorationKDE(SP resource_, wlr_surface* surf) : resource(resource_) { +CServerDecorationKDE::CServerDecorationKDE(SP resource_, SP surf) : resource(resource_) { if (!good()) return; @@ -42,7 +43,8 @@ void CServerDecorationKDEProtocol::destroyResource(CServerDecorationKDE* hayperl void CServerDecorationKDEProtocol::createDecoration(COrgKdeKwinServerDecorationManager* pMgr, uint32_t id, wl_resource* surf) { const auto CLIENT = pMgr->client(); const auto RESOURCE = - m_vDecos.emplace_back(std::make_unique(makeShared(CLIENT, pMgr->version(), id), wlr_surface_from_resource(surf))).get(); + m_vDecos.emplace_back(std::make_unique(makeShared(CLIENT, pMgr->version(), id), CWLSurfaceResource::fromResource(surf))) + .get(); if (!RESOURCE->good()) { pMgr->noMemory(); diff --git a/src/protocols/ServerDecorationKDE.hpp b/src/protocols/ServerDecorationKDE.hpp index ec7a852f..ab082b17 100644 --- a/src/protocols/ServerDecorationKDE.hpp +++ b/src/protocols/ServerDecorationKDE.hpp @@ -6,9 +6,11 @@ #include "WaylandProtocol.hpp" #include "kde-server-decoration.hpp" +class CWLSurfaceResource; + class CServerDecorationKDE { public: - CServerDecorationKDE(SP resource_, wlr_surface* surf); + CServerDecorationKDE(SP resource_, SP surf); bool good(); diff --git a/src/protocols/SessionLock.cpp b/src/protocols/SessionLock.cpp index fd803eda..ae45b0f1 100644 --- a/src/protocols/SessionLock.cpp +++ b/src/protocols/SessionLock.cpp @@ -2,10 +2,12 @@ #include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "FractionalScale.hpp" +#include "core/Compositor.hpp" +#include "core/Output.hpp" #define LOGM PROTO::sessionLock->protoLog -CSessionLockSurface::CSessionLockSurface(SP resource_, wlr_surface* surface_, CMonitor* pMonitor_, WP owner_) : +CSessionLockSurface::CSessionLockSurface(SP resource_, SP surface_, CMonitor* pMonitor_, WP owner_) : resource(resource_), sessionLock(owner_), pSurface(surface_), pMonitor(pMonitor_) { if (!resource->resource()) return; @@ -21,45 +23,38 @@ CSessionLockSurface::CSessionLockSurface(SP resource_, resource->setAckConfigure([this](CExtSessionLockSurfaceV1* r, uint32_t serial) { ackdConfigure = true; }); - hyprListener_surfaceCommit.initCallback( - &pSurface->events.commit, - [this](void* owner, void* data) { - if (pSurface->pending.buffer_width <= 0 || pSurface->pending.buffer_height <= 0) { - LOGM(ERR, "SessionLock attached a null buffer"); - resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached"); - return; - } + listeners.surfaceCommit = pSurface->events.commit.registerListener([this](std::any d) { + if (!pSurface->current.buffer) { + LOGM(ERR, "SessionLock attached a null buffer"); + resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, "Null buffer attached"); + return; + } - if (!ackdConfigure) { - LOGM(ERR, "SessionLock committed without an ack"); - resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, "Committed surface before first ack"); - return; - } + if (!ackdConfigure) { + LOGM(ERR, "SessionLock committed without an ack"); + resource->error(EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, "Committed surface before first ack"); + return; + } - if (committed) - events.commit.emit(); - else { - wlr_surface_map(pSurface); - events.map.emit(); - } - committed = true; - }, - this, "SessionLockSurface"); + if (committed) + events.commit.emit(); + else { + pSurface->map(); + events.map.emit(); + } + committed = true; + }); - hyprListener_surfaceDestroy.initCallback( - &pSurface->events.destroy, - [this](void* owner, void* data) { - LOGM(WARN, "SessionLockSurface object remains but surface is being destroyed???"); - wlr_surface_unmap(pSurface); - hyprListener_surfaceCommit.removeCallback(); - hyprListener_surfaceDestroy.removeCallback(); + listeners.surfaceDestroy = pSurface->events.destroy.registerListener([this](std::any d) { + LOGM(WARN, "SessionLockSurface object remains but surface is being destroyed???"); + pSurface->unmap(); + listeners.surfaceCommit.reset(); + listeners.surfaceDestroy.reset(); + if (g_pCompositor->m_pLastFocus == pSurface) + g_pCompositor->m_pLastFocus.reset(); - if (g_pCompositor->m_pLastFocus == pSurface) - g_pCompositor->m_pLastFocus = nullptr; - - pSurface = nullptr; - }, - this, "SessionLockSurface"); + pSurface.reset(); + }); PROTO::fractional->sendScale(surface_, pMonitor_->scale); @@ -70,9 +65,9 @@ CSessionLockSurface::CSessionLockSurface(SP resource_, CSessionLockSurface::~CSessionLockSurface() { if (pSurface && pSurface->mapped) - wlr_surface_unmap(pSurface); - hyprListener_surfaceCommit.removeCallback(); - hyprListener_surfaceDestroy.removeCallback(); + pSurface->unmap(); + listeners.surfaceCommit.reset(); + listeners.surfaceDestroy.reset(); events.destroy.emit(); // just in case. } @@ -93,8 +88,8 @@ CMonitor* CSessionLockSurface::monitor() { return pMonitor; } -wlr_surface* CSessionLockSurface::surface() { - return pSurface; +SP CSessionLockSurface::surface() { + return pSurface.lock(); } CSessionLock::CSessionLock(SP resource_) : resource(resource_) { @@ -195,8 +190,8 @@ void CSessionLockProtocol::onLock(CExtSessionLockManagerV1* pMgr, uint32_t id) { void CSessionLockProtocol::onGetLockSurface(CExtSessionLockV1* lock, uint32_t id, wl_resource* surface, wl_resource* output) { LOGM(LOG, "New sessionLockSurface with id {}", id); - auto PSURFACE = wlr_surface_from_resource(surface); - auto PMONITOR = g_pCompositor->getMonitorFromOutput(wlr_output_from_resource(output)); + auto PSURFACE = CWLSurfaceResource::fromResource(surface); + auto PMONITOR = CWLOutputResource::fromResource(output)->monitor.get(); SP sessionLock; for (auto& l : m_vLocks) { diff --git a/src/protocols/SessionLock.hpp b/src/protocols/SessionLock.hpp index 6b0c4e08..a0c67e88 100644 --- a/src/protocols/SessionLock.hpp +++ b/src/protocols/SessionLock.hpp @@ -9,16 +9,17 @@ class CMonitor; class CSessionLock; +class CWLSurfaceResource; class CSessionLockSurface { public: - CSessionLockSurface(SP resource_, wlr_surface* surface_, CMonitor* pMonitor_, WP owner_); + CSessionLockSurface(SP resource_, SP surface_, CMonitor* pMonitor_, WP owner_); ~CSessionLockSurface(); - bool good(); - bool inert(); - CMonitor* monitor(); - wlr_surface* surface(); + bool good(); + bool inert(); + CMonitor* monitor(); + SP surface(); struct { CSignal map; @@ -29,7 +30,7 @@ class CSessionLockSurface { private: SP resource; WP sessionLock; - wlr_surface* pSurface = nullptr; + WP pSurface; CMonitor* pMonitor = nullptr; bool ackdConfigure = false; @@ -37,11 +38,10 @@ class CSessionLockSurface { void sendConfigure(); - DYNLISTENER(surfaceCommit); - DYNLISTENER(surfaceDestroy); - struct { CHyprSignalListener monitorMode; + CHyprSignalListener surfaceCommit; + CHyprSignalListener surfaceDestroy; } listeners; }; diff --git a/src/protocols/ShortcutsInhibit.cpp b/src/protocols/ShortcutsInhibit.cpp index af9724b5..211a7a01 100644 --- a/src/protocols/ShortcutsInhibit.cpp +++ b/src/protocols/ShortcutsInhibit.cpp @@ -1,10 +1,11 @@ #include "ShortcutsInhibit.hpp" #include #include "../Compositor.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::shortcutsInhibit->protoLog -CKeyboardShortcutsInhibitor::CKeyboardShortcutsInhibitor(SP resource_, wlr_surface* surf) : resource(resource_), pSurface(surf) { +CKeyboardShortcutsInhibitor::CKeyboardShortcutsInhibitor(SP resource_, SP surf) : resource(resource_), pSurface(surf) { if (!resource->resource()) return; @@ -16,8 +17,8 @@ CKeyboardShortcutsInhibitor::CKeyboardShortcutsInhibitor(SPsendActive(); } -wlr_surface* CKeyboardShortcutsInhibitor::surface() { - return pSurface; +SP CKeyboardShortcutsInhibitor::surface() { + return pSurface.lock(); } bool CKeyboardShortcutsInhibitor::good() { @@ -46,8 +47,8 @@ void CKeyboardShortcutsInhibitProtocol::destroyInhibitor(CKeyboardShortcutsInhib } void CKeyboardShortcutsInhibitProtocol::onInhibit(CZwpKeyboardShortcutsInhibitManagerV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* seat) { - wlr_surface* surf = wlr_surface_from_resource(surface); - const auto CLIENT = pMgr->client(); + SP surf = CWLSurfaceResource::fromResource(surface); + const auto CLIENT = pMgr->client(); for (auto& in : m_vInhibitors) { if (in->surface() != surf) diff --git a/src/protocols/ShortcutsInhibit.hpp b/src/protocols/ShortcutsInhibit.hpp index 4e06938f..ba1c134c 100644 --- a/src/protocols/ShortcutsInhibit.hpp +++ b/src/protocols/ShortcutsInhibit.hpp @@ -6,17 +6,19 @@ #include "WaylandProtocol.hpp" #include "keyboard-shortcuts-inhibit-unstable-v1.hpp" +class CWLSurfaceResource; + class CKeyboardShortcutsInhibitor { public: - CKeyboardShortcutsInhibitor(SP resource_, wlr_surface* surf); + CKeyboardShortcutsInhibitor(SP resource_, SP surf); // read-only pointer, may be invalid - wlr_surface* surface(); - bool good(); + SP surface(); + bool good(); private: SP resource; - wlr_surface* pSurface = nullptr; + WP pSurface; }; class CKeyboardShortcutsInhibitProtocol : public IWaylandProtocol { diff --git a/src/protocols/Tablet.cpp b/src/protocols/Tablet.cpp index 54c55176..393dfd38 100644 --- a/src/protocols/Tablet.cpp +++ b/src/protocols/Tablet.cpp @@ -3,6 +3,7 @@ #include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "core/Seat.hpp" +#include "core/Compositor.hpp" #include #define LOGM PROTO::tablet->protoLog @@ -160,11 +161,11 @@ CTabletToolV2Resource::CTabletToolV2Resource(SP resource_, SP< resource->setDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); resource->setOnDestroy([this](CZwpTabletToolV2* r) { PROTO::tablet->destroyResource(this); }); - resource->setSetCursor([this](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) { + resource->setSetCursor([](CZwpTabletToolV2* r, uint32_t serial, wl_resource* surf, int32_t hot_x, int32_t hot_y) { if (!g_pSeatManager->state.pointerFocusResource || g_pSeatManager->state.pointerFocusResource->client() != r->client()) return; - g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{surf ? wlr_surface_from_resource(surf) : nullptr, {hot_x, hot_y}}); + g_pInputManager->processMouseRequest(CSeatManager::SSetCursorEvent{surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hot_x, hot_y}}); }); } @@ -448,7 +449,7 @@ void CTabletV2Protocol::recheckRegisteredDevices() { if (t->current) { t->resource->sendProximityOut(); t->sendFrame(); - t->lastSurf = nullptr; + t->lastSurf.reset(); } t->resource->sendRemoved(); @@ -545,9 +546,9 @@ void CTabletV2Protocol::down(SP tool) { } } -void CTabletV2Protocol::proximityIn(SP tool, SP tablet, wlr_surface* surf) { +void CTabletV2Protocol::proximityIn(SP tool, SP tablet, SP surf) { proximityOut(tool); - const auto CLIENT = wl_resource_get_client(surf->resource); + const auto CLIENT = surf->client(); SP toolResource; SP tabletResource; @@ -587,7 +588,7 @@ void CTabletV2Protocol::proximityIn(SP tool, SP tablet, wl toolResource->lastSurf = surf; auto serial = g_pSeatManager->nextSerial(g_pSeatManager->seatResourceForClient(toolResource->resource->client())); - toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->resource); + toolResource->resource->sendProximityIn(serial, tabletResource->resource.get(), surf->getResource()->resource()); toolResource->queueFrame(); LOGM(ERR, "proximityIn: found no resource to send enter"); @@ -598,8 +599,8 @@ void CTabletV2Protocol::proximityOut(SP tool) { if (t->tool != tool || !t->current) continue; - t->current = false; - t->lastSurf = nullptr; + t->current = false; + t->lastSurf.reset(); t->resource->sendProximityOut(); t->sendFrame(); } diff --git a/src/protocols/Tablet.hpp b/src/protocols/Tablet.hpp index 74a45c63..c61395c9 100644 --- a/src/protocols/Tablet.hpp +++ b/src/protocols/Tablet.hpp @@ -12,6 +12,7 @@ class CTabletTool; class CTabletPad; class CEventLoopTimer; class CTabletSeat; +class CWLSurfaceResource; class CTabletPadStripV2Resource { public: @@ -112,19 +113,19 @@ class CTabletToolV2Resource { CTabletToolV2Resource(SP resource_, SP tool_, SP seat_); ~CTabletToolV2Resource(); - bool good(); - void sendData(); - void queueFrame(); - void sendFrame(bool removeSource = true); + bool good(); + void sendData(); + void queueFrame(); + void sendFrame(bool removeSource = true); - bool current = false; - wlr_surface* lastSurf = nullptr; // READ-ONLY + bool current = false; + WP lastSurf; - WP tool; - WP seat; - wl_event_source* frameSource = nullptr; + WP tool; + WP seat; + wl_event_source* frameSource = nullptr; - bool inert = false; // removed was sent + bool inert = false; // removed was sent private: SP resource; @@ -180,7 +181,7 @@ class CTabletV2Protocol : public IWaylandProtocol { void tilt(SP tool, const Vector2D& value); void up(SP tool); void down(SP tool); - void proximityIn(SP tool, SP tablet, wlr_surface* surf); + void proximityIn(SP tool, SP tablet, SP surf); void proximityOut(SP tool); void buttonTool(SP tool, uint32_t button, uint32_t state); void motion(SP tool, const Vector2D& value); diff --git a/src/protocols/TearingControl.cpp b/src/protocols/TearingControl.cpp index df3126fe..7f3c0a18 100644 --- a/src/protocols/TearingControl.cpp +++ b/src/protocols/TearingControl.cpp @@ -2,6 +2,7 @@ #include "../managers/ProtocolManager.hpp" #include "../desktop/Window.hpp" #include "../Compositor.hpp" +#include "core/Compositor.hpp" CTearingControlProtocol::CTearingControlProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { static auto P = @@ -13,15 +14,16 @@ void CTearingControlProtocol::bindManager(wl_client* client, void* data, uint32_ RESOURCE->setOnDestroy([this](CWpTearingControlManagerV1* p) { this->onManagerResourceDestroy(p->resource()); }); RESOURCE->setDestroy([this](CWpTearingControlManagerV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); }); - RESOURCE->setGetTearingControl( - [this](CWpTearingControlManagerV1* pMgr, uint32_t id, wl_resource* surface) { this->onGetController(pMgr->client(), pMgr, id, wlr_surface_from_resource(surface)); }); + RESOURCE->setGetTearingControl([this](CWpTearingControlManagerV1* pMgr, uint32_t id, wl_resource* surface) { + this->onGetController(pMgr->client(), pMgr, id, CWLSurfaceResource::fromResource(surface)); + }); } void CTearingControlProtocol::onManagerResourceDestroy(wl_resource* res) { std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; }); } -void CTearingControlProtocol::onGetController(wl_client* client, CWpTearingControlManagerV1* pMgr, uint32_t id, wlr_surface* surf) { +void CTearingControlProtocol::onGetController(wl_client* client, CWpTearingControlManagerV1* pMgr, uint32_t id, SP surf) { const auto CONTROLLER = m_vTearingControllers.emplace_back(std::make_unique(makeShared(client, pMgr->version(), id), surf)).get(); if (!CONTROLLER->good()) { @@ -44,14 +46,14 @@ void CTearingControlProtocol::onWindowDestroy(PHLWINDOW pWindow) { // -CTearingControl::CTearingControl(SP resource_, wlr_surface* surf_) : resource(resource_) { +CTearingControl::CTearingControl(SP resource_, SP surf_) : resource(resource_) { resource->setData(this); resource->setOnDestroy([this](CWpTearingControlV1* res) { PROTO::tearing->onControllerDestroy(this); }); resource->setDestroy([this](CWpTearingControlV1* res) { PROTO::tearing->onControllerDestroy(this); }); resource->setSetPresentationHint([this](CWpTearingControlV1* res, wpTearingControlV1PresentationHint hint) { this->onHint(hint); }); for (auto& w : g_pCompositor->m_vWindows) { - if (w->m_pWLSurface.wlr() == surf_) { + if (w->m_pWLSurface->resource() == surf_) { pWindow = w; break; } diff --git a/src/protocols/TearingControl.hpp b/src/protocols/TearingControl.hpp index 199397a3..d81a27cd 100644 --- a/src/protocols/TearingControl.hpp +++ b/src/protocols/TearingControl.hpp @@ -6,10 +6,11 @@ class CWindow; class CTearingControlProtocol; +class CWLSurfaceResource; class CTearingControl { public: - CTearingControl(SP resource_, wlr_surface* surf_); + CTearingControl(SP resource_, SP surf_); void onHint(wpTearingControlV1PresentationHint hint_); @@ -42,7 +43,7 @@ class CTearingControlProtocol : public IWaylandProtocol { private: void onManagerResourceDestroy(wl_resource* res); void onControllerDestroy(CTearingControl* control); - void onGetController(wl_client* client, CWpTearingControlManagerV1* pMgr, uint32_t id, wlr_surface* surf); + void onGetController(wl_client* client, CWpTearingControlManagerV1* pMgr, uint32_t id, SP surf); void onWindowDestroy(PHLWINDOW pWindow); // diff --git a/src/protocols/TextInputV1.cpp b/src/protocols/TextInputV1.cpp index 8fff4db5..7c16ef8c 100644 --- a/src/protocols/TextInputV1.cpp +++ b/src/protocols/TextInputV1.cpp @@ -1,6 +1,7 @@ #include "TextInputV1.hpp" #include "../Compositor.hpp" +#include "core/Compositor.hpp" #define TEXT_INPUT_VERSION 1 @@ -168,7 +169,7 @@ void CTextInputV1ProtocolManager::handleActivate(wl_client* client, wl_resource* return; } PTI->active = true; - PTI->pTextInput->onEnabled(wlr_surface_from_resource(surface)); + PTI->pTextInput->onEnabled(CWLSurfaceResource::fromResource(surface)); } void CTextInputV1ProtocolManager::handleDeactivate(wl_client* client, wl_resource* resource, wl_resource* seat) { diff --git a/src/protocols/TextInputV3.cpp b/src/protocols/TextInputV3.cpp index b463c6d6..1302a57f 100644 --- a/src/protocols/TextInputV3.cpp +++ b/src/protocols/TextInputV3.cpp @@ -1,5 +1,6 @@ #include "TextInputV3.hpp" #include +#include "core/Compositor.hpp" #define LOGM PROTO::textInputV3->protoLog @@ -66,12 +67,12 @@ CTextInputV3::~CTextInputV3() { events.destroy.emit(); } -void CTextInputV3::enter(wlr_surface* surf) { - resource->sendEnter(surf->resource); +void CTextInputV3::enter(SP surf) { + resource->sendEnter(surf->getResource()->resource()); } -void CTextInputV3::leave(wlr_surface* surf) { - resource->sendLeave(surf->resource); +void CTextInputV3::leave(SP surf) { + resource->sendLeave(surf->getResource()->resource()); } void CTextInputV3::preeditString(const std::string& text, int32_t cursorBegin, int32_t cursorEnd) { diff --git a/src/protocols/TextInputV3.hpp b/src/protocols/TextInputV3.hpp index 6d4f3d54..3959e72b 100644 --- a/src/protocols/TextInputV3.hpp +++ b/src/protocols/TextInputV3.hpp @@ -9,13 +9,15 @@ #include "../helpers/signal/Signal.hpp" #include "../helpers/Box.hpp" +class CWLSurfaceResource; + class CTextInputV3 { public: CTextInputV3(SP resource_); ~CTextInputV3(); - void enter(wlr_surface* surf); - void leave(wlr_surface* surf); + void enter(SP surf); + void leave(SP surf); void preeditString(const std::string& text, int32_t cursorBegin, int32_t cursorEnd); void commitString(const std::string& text); void deleteSurroundingText(uint32_t beforeLength, uint32_t afterLength); diff --git a/src/protocols/ToplevelExport.cpp b/src/protocols/ToplevelExport.cpp index d3b71a9e..80f9defa 100644 --- a/src/protocols/ToplevelExport.cpp +++ b/src/protocols/ToplevelExport.cpp @@ -2,11 +2,12 @@ #include "../Compositor.hpp" #include "ForeignToplevelWlr.hpp" #include "../managers/PointerManager.hpp" +#include "types/WLBuffer.hpp" +#include "types/Buffer.hpp" +#include "../helpers/Format.hpp" #include -#include "ToplevelExportWlrFuncs.hpp" - #define TOPLEVEL_EXPORT_VERSION 2 static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) { @@ -131,8 +132,8 @@ void CToplevelExportProtocolManager::removeFrame(SScreencopyFrame* frame, bool f std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; }); wl_resource_set_user_data(frame->resource, nullptr); - if (frame->buffer && frame->buffer->n_locks > 0) - wlr_buffer_unlock(frame->buffer); + if (frame->buffer && frame->buffer->locked() > 0) + frame->buffer->unlock(); removeClient(frame->client, force); m_lFrames.remove(*frame); } @@ -184,7 +185,7 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou return; } - const auto PSHMINFO = drm_get_pixel_format_info(PFRAME->shmFormat); + const auto PSHMINFO = FormatUtils::getPixelFormatFromDRM(PFRAME->shmFormat); if (!PSHMINFO) { Debug::log(ERR, "No pixel format supported by renderer in capture toplevel"); hyprland_toplevel_export_frame_v1_send_failed(resource); @@ -203,9 +204,9 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou wlr_output_effective_resolution(PMONITOR->output, &ow, &oh); PFRAME->box.transform(PMONITOR->transform, ow, oh).round(); - PFRAME->shmStride = pixel_format_info_min_stride(PSHMINFO, PFRAME->box.w); + PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w); - hyprland_toplevel_export_frame_v1_send_buffer(PFRAME->resource, convert_drm_format_to_wl_shm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride); + hyprland_toplevel_export_frame_v1_send_buffer(PFRAME->resource, FormatUtils::drmToShm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride); if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) { hyprland_toplevel_export_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height); @@ -238,14 +239,16 @@ void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* r return; } - const auto PBUFFER = wlr_buffer_try_from_resource(buffer); + const auto PBUFFER = CWLBufferResource::fromResource(buffer); if (!PBUFFER) { wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer"); removeFrame(PFRAME); return; } - if (PBUFFER->width != PFRAME->box.width || PBUFFER->height != PFRAME->box.height) { + PBUFFER->buffer->lock(); + + if (PBUFFER->buffer->size != PFRAME->box.size()) { wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions"); removeFrame(PFRAME); return; @@ -257,26 +260,20 @@ void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* r return; } - wlr_dmabuf_attributes dmabufAttrs; - void* wlrBufferAccessData; - uint32_t wlrBufferAccessFormat; - size_t wlrBufferAccessStride; - if (wlr_buffer_get_dmabuf(PBUFFER, &dmabufAttrs)) { - PFRAME->bufferCap = WLR_BUFFER_CAP_DMABUF; + if (auto attrs = PBUFFER->buffer->dmabuf(); attrs.success) { + PFRAME->bufferDMA = true; - if (dmabufAttrs.format != PFRAME->dmabufFormat) { + if (attrs.format != PFRAME->dmabufFormat) { wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); removeFrame(PFRAME); return; } - } else if (wlr_buffer_begin_data_ptr_access(PBUFFER, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &wlrBufferAccessData, &wlrBufferAccessFormat, &wlrBufferAccessStride)) { - wlr_buffer_end_data_ptr_access(PBUFFER); - - if (wlrBufferAccessFormat != PFRAME->shmFormat) { + } else if (auto attrs = PBUFFER->buffer->shm(); attrs.success) { + if (attrs.format != PFRAME->shmFormat) { wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format"); removeFrame(PFRAME); return; - } else if ((int)wlrBufferAccessStride != PFRAME->shmStride) { + } else if ((int)attrs.stride != PFRAME->shmStride) { wl_resource_post_error(PFRAME->resource, HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride"); removeFrame(PFRAME); return; @@ -287,7 +284,7 @@ void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* r return; } - PFRAME->buffer = PBUFFER; + PFRAME->buffer = PBUFFER->buffer; m_vFramesAwaitingWrite.emplace_back(PFRAME); } @@ -338,7 +335,7 @@ void CToplevelExportProtocolManager::shareFrame(SScreencopyFrame* frame) { clock_gettime(CLOCK_MONOTONIC, &now); uint32_t flags = 0; - if (frame->bufferCap == WLR_BUFFER_CAP_DMABUF) { + if (frame->bufferDMA) { if (!copyFrameDmabuf(frame, &now)) { hyprland_toplevel_export_frame_v1_send_failed(frame->resource); return; @@ -363,11 +360,8 @@ void CToplevelExportProtocolManager::sendDamage(SScreencopyFrame* frame) { } bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) { - void* data; - uint32_t format; - size_t stride; - if (!wlr_buffer_begin_data_ptr_access(frame->buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride)) - return false; + auto shm = frame->buffer->shm(); + auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm // render the client const auto PMONITOR = g_pCompositor->getMonitorFromID(frame->pWindow->m_iMonitorID); @@ -378,16 +372,14 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times CFramebuffer outFB; outFB.alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : PMONITOR->drmFormat); - if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &outFB)) { - wlr_buffer_end_data_ptr_access(frame->buffer); - return false; - } - if (frame->overlayCursor) { g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock()); g_pPointerManager->damageCursor(PMONITOR->self.lock()); } + if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &outFB)) + return false; + g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0)); // render client at 0,0 @@ -398,10 +390,9 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times if (frame->overlayCursor) g_pPointerManager->renderSoftwareCursorsFor(PMONITOR->self.lock(), now, fakeDamage, g_pInputManager->getMouseCoordsInternal() - frame->pWindow->m_vRealPosition.value()); - const auto PFORMAT = g_pHyprOpenGL->getPixelFormatFromDRM(format); + const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format); if (!PFORMAT) { g_pHyprRenderer->endRender(); - wlr_buffer_end_data_ptr_access(frame->buffer); return false; } @@ -418,9 +409,7 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(0, 0, frame->box.width, frame->box.height, PFORMAT->glFormat, PFORMAT->glType, data); - - wlr_buffer_end_data_ptr_access(frame->buffer); + glReadPixels(0, 0, frame->box.width, frame->box.height, PFORMAT->glFormat, PFORMAT->glType, pixelData); if (frame->overlayCursor) { g_pPointerManager->unlockSoftwareForMonitor(PMONITOR->self.lock()); @@ -435,7 +424,7 @@ bool CToplevelExportProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame, ti CRegion fakeDamage{0, 0, INT16_MAX, INT16_MAX}; - if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer)) + if (!g_pHyprRenderer->beginRender(PMONITOR, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock())) return false; g_pHyprOpenGL->clear(CColor(0, 0, 0, 1.0)); diff --git a/src/protocols/ToplevelExportWlrFuncs.hpp b/src/protocols/ToplevelExportWlrFuncs.hpp deleted file mode 100644 index b6f387e8..00000000 --- a/src/protocols/ToplevelExportWlrFuncs.hpp +++ /dev/null @@ -1,243 +0,0 @@ -#include - -#ifndef DRM_WLR_FUNCS -#define DRM_WLR_FUNCS - -struct wlr_pixel_format_info { - uint32_t drm_format; - - /* Equivalent of the format if it has an alpha channel, - * DRM_FORMAT_INVALID (0) if NA - */ - uint32_t opaque_substitute; - - /* Bytes per block (including padding) */ - uint32_t bytes_per_block; - /* Size of a block in pixels (zero for 1×1) */ - uint32_t block_width, block_height; - - /* True if the format has an alpha channel */ - bool has_alpha; -}; - -static const struct wlr_pixel_format_info pixel_format_info[] = { - { - .drm_format = DRM_FORMAT_XRGB8888, - .bytes_per_block = 4, - }, - { - .drm_format = DRM_FORMAT_ARGB8888, - .opaque_substitute = DRM_FORMAT_XRGB8888, - .bytes_per_block = 4, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR8888, - .bytes_per_block = 4, - }, - { - .drm_format = DRM_FORMAT_ABGR8888, - .opaque_substitute = DRM_FORMAT_XBGR8888, - .bytes_per_block = 4, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_RGBX8888, - .bytes_per_block = 4, - }, - { - .drm_format = DRM_FORMAT_RGBA8888, - .opaque_substitute = DRM_FORMAT_RGBX8888, - .bytes_per_block = 4, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_BGRX8888, - .bytes_per_block = 4, - }, - { - .drm_format = DRM_FORMAT_BGRA8888, - .opaque_substitute = DRM_FORMAT_BGRX8888, - .bytes_per_block = 4, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_R8, - .bytes_per_block = 1, - }, - { - .drm_format = DRM_FORMAT_GR88, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_RGB888, - .bytes_per_block = 3, - }, - { - .drm_format = DRM_FORMAT_BGR888, - .bytes_per_block = 3, - }, - { - .drm_format = DRM_FORMAT_RGBX4444, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_RGBA4444, - .opaque_substitute = DRM_FORMAT_RGBX4444, - .bytes_per_block = 2, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_BGRX4444, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_BGRA4444, - .opaque_substitute = DRM_FORMAT_BGRX4444, - .bytes_per_block = 2, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_RGBX5551, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_RGBA5551, - .opaque_substitute = DRM_FORMAT_RGBX5551, - .bytes_per_block = 2, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_BGRX5551, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_BGRA5551, - .opaque_substitute = DRM_FORMAT_BGRX5551, - .bytes_per_block = 2, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XRGB1555, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_ARGB1555, - .opaque_substitute = DRM_FORMAT_XRGB1555, - .bytes_per_block = 2, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_RGB565, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_BGR565, - .bytes_per_block = 2, - }, - { - .drm_format = DRM_FORMAT_XRGB2101010, - .bytes_per_block = 4, - }, - { - .drm_format = DRM_FORMAT_ARGB2101010, - .opaque_substitute = DRM_FORMAT_XRGB2101010, - .bytes_per_block = 4, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR2101010, - .bytes_per_block = 4, - }, - { - .drm_format = DRM_FORMAT_ABGR2101010, - .opaque_substitute = DRM_FORMAT_XBGR2101010, - .bytes_per_block = 4, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR16161616F, - .bytes_per_block = 8, - }, - { - .drm_format = DRM_FORMAT_ABGR16161616F, - .opaque_substitute = DRM_FORMAT_XBGR16161616F, - .bytes_per_block = 8, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_XBGR16161616, - .bytes_per_block = 8, - }, - { - .drm_format = DRM_FORMAT_ABGR16161616, - .opaque_substitute = DRM_FORMAT_XBGR16161616, - .bytes_per_block = 8, - .has_alpha = true, - }, - { - .drm_format = DRM_FORMAT_YVYU, - .bytes_per_block = 4, - .block_width = 2, - .block_height = 1, - }, - { - .drm_format = DRM_FORMAT_VYUY, - .bytes_per_block = 4, - .block_width = 2, - .block_height = 1, - }, -}; - -static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]); - -static const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) { - for (size_t i = 0; i < pixel_format_info_size; ++i) { - if (pixel_format_info[i].drm_format == fmt) { - return &pixel_format_info[i]; - } - } - - return NULL; -} - -/*static uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) { - switch (fmt) { - case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888; - case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888; - default: return (uint32_t)fmt; - } -}*/ - -static enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) { - switch (fmt) { - case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888; - case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888; - default: return (enum wl_shm_format)fmt; - } -} - -static uint32_t pixel_format_info_pixels_per_block(const struct wlr_pixel_format_info* info) { - uint32_t pixels = info->block_width * info->block_height; - return pixels > 0 ? pixels : 1; -} - -static int32_t div_round_up(int32_t dividend, int32_t divisor) { - int32_t quotient = dividend / divisor; - if (dividend % divisor != 0) { - quotient++; - } - return quotient; -} - -static int32_t pixel_format_info_min_stride(const wlr_pixel_format_info* fmt, int32_t width) { - int32_t pixels_per_block = (int32_t)pixel_format_info_pixels_per_block(fmt); - int32_t bytes_per_block = (int32_t)fmt->bytes_per_block; - if (width > INT32_MAX / bytes_per_block) { - wlr_log(WLR_DEBUG, "Invalid width %d (overflow)", width); - return 0; - } - return div_round_up(width * bytes_per_block, pixels_per_block); -} - -#endif \ No newline at end of file diff --git a/src/protocols/Viewporter.cpp b/src/protocols/Viewporter.cpp new file mode 100644 index 00000000..8cb69dbe --- /dev/null +++ b/src/protocols/Viewporter.cpp @@ -0,0 +1,126 @@ +#include "Viewporter.hpp" +#include "core/Compositor.hpp" +#include + +#define LOGM PROTO::viewport->protoLog + +CViewportResource::CViewportResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); }); + resource->setOnDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); }); + + resource->setSetDestination([this](CWpViewport* r, int32_t x, int32_t y) { + if (!surface) { + r->error(WP_VIEWPORT_ERROR_NO_SURFACE, "Surface is gone"); + return; + } + + if (x == -1 && y == -1) { + surface->pending.viewport.hasDestination = false; + return; + } + + if (x <= 0 || y <= 0) { + r->error(WP_VIEWPORT_ERROR_BAD_SIZE, "Size was <= 0"); + return; + } + + surface->pending.viewport.hasDestination = true; + surface->pending.viewport.destination = {x, y}; + }); + + resource->setSetSource([this](CWpViewport* r, wl_fixed_t fx, wl_fixed_t fy, wl_fixed_t fw, wl_fixed_t fh) { + if (!surface) { + r->error(WP_VIEWPORT_ERROR_NO_SURFACE, "Surface is gone"); + return; + } + + double x = wl_fixed_to_double(fx), y = wl_fixed_to_double(fy), w = wl_fixed_to_double(fw), h = wl_fixed_to_double(fh); + + if (x == -1 && y == -1 && w == -1 && h == -1) { + surface->pending.viewport.hasSource = false; + return; + } + + if (x < 0 || y < 0) { + r->error(WP_VIEWPORT_ERROR_BAD_SIZE, "Pos was < 0"); + return; + } + + surface->pending.viewport.hasSource = true; + surface->pending.viewport.source = {x, y, w, h}; + }); +} + +CViewportResource::~CViewportResource() { + if (!surface) + return; + + surface->pending.viewport.hasDestination = false; + surface->pending.viewport.hasSource = false; +} + +bool CViewportResource::good() { + return resource->resource(); +} + +void CViewportResource::verify() { + if (!surface) + return; + + if (surface->pending.viewport.hasSource) { + auto& src = surface->pending.viewport.source; + + if (src.w + src.x > surface->pending.size.x || src.h + src.y > surface->pending.size.y) { + resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit"); + return; + } + } +} + +CViewporterResource::CViewporterResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); }); + resource->setOnDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); }); + + resource->setGetViewport([](CWpViewporter* r, uint32_t id, wl_resource* surf) { + const auto RESOURCE = PROTO::viewport->m_vViewports.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surf))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::viewport->m_vViewports.pop_back(); + return; + } + }); +} + +bool CViewporterResource::good() { + return resource->resource(); +} + +CViewporterProtocol::CViewporterProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CViewporterProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CViewporterProtocol::destroyResource(CViewporterResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CViewporterProtocol::destroyResource(CViewportResource* resource) { + std::erase_if(m_vViewports, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/Viewporter.hpp b/src/protocols/Viewporter.hpp new file mode 100644 index 00000000..01278203 --- /dev/null +++ b/src/protocols/Viewporter.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "viewporter.hpp" +#include "../helpers/signal/Signal.hpp" + +class CWLSurfaceResource; + +class CViewportResource { + public: + CViewportResource(SP resource_, SP surface_); + ~CViewportResource(); + + bool good(); + void verify(); + WP surface; + + private: + SP resource; +}; + +class CViewporterResource { + public: + CViewporterResource(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CViewporterProtocol : public IWaylandProtocol { + public: + CViewporterProtocol(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(CViewporterResource* resource); + void destroyResource(CViewportResource* resource); + + // + std::vector> m_vManagers; + std::vector> m_vViewports; + + friend class CViewporterResource; + friend class CViewportResource; +}; + +namespace PROTO { + inline UP viewport; +}; diff --git a/src/protocols/WaylandProtocol.cpp b/src/protocols/WaylandProtocol.cpp index 30120235..23a6721c 100644 --- a/src/protocols/WaylandProtocol.cpp +++ b/src/protocols/WaylandProtocol.cpp @@ -10,6 +10,8 @@ static void displayDestroyInternal(struct wl_listener* listener, void* data) { } void IWaylandProtocol::onDisplayDestroy() { + wl_list_remove(&m_liDisplayDestroy.link); + wl_list_init(&m_liDisplayDestroy.link); wl_global_destroy(m_pGlobal); } @@ -30,3 +32,7 @@ IWaylandProtocol::IWaylandProtocol(const wl_interface* iface, const int& ver, co IWaylandProtocol::~IWaylandProtocol() { onDisplayDestroy(); } + +void IWaylandProtocol::removeGlobal() { + wl_global_remove(m_pGlobal); +} diff --git a/src/protocols/WaylandProtocol.hpp b/src/protocols/WaylandProtocol.hpp index a7487c15..b443e253 100644 --- a/src/protocols/WaylandProtocol.hpp +++ b/src/protocols/WaylandProtocol.hpp @@ -14,9 +14,10 @@ class IWaylandProtocol { public: IWaylandProtocol(const wl_interface* iface, const int& ver, const std::string& name); - ~IWaylandProtocol(); + virtual ~IWaylandProtocol(); virtual void onDisplayDestroy(); + virtual void removeGlobal(); virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) = 0; diff --git a/src/protocols/XDGActivation.cpp b/src/protocols/XDGActivation.cpp index 1d9814ac..40f33f02 100644 --- a/src/protocols/XDGActivation.cpp +++ b/src/protocols/XDGActivation.cpp @@ -1,6 +1,7 @@ #include "XDGActivation.hpp" #include "../managers/TokenManager.hpp" #include "../Compositor.hpp" +#include "core/Compositor.hpp" #include #define LOGM PROTO::activation->protoLog @@ -79,8 +80,8 @@ void CXDGActivationProtocol::bindManager(wl_client* client, void* data, uint32_t // remove token. It's been now spent. m_vSentTokens.erase(TOKEN); - wlr_surface* surf = wlr_surface_from_resource(surface); - const auto PWINDOW = g_pCompositor->getWindowFromSurface(surf); + SP surf = CWLSurfaceResource::fromResource(surface); + const auto PWINDOW = g_pCompositor->getWindowFromSurface(surf); if (!PWINDOW) { LOGM(WARN, "activate event for non-window or gone surface with token {}, ignoring", token); diff --git a/src/protocols/XDGDecoration.cpp b/src/protocols/XDGDecoration.cpp index 5a4cf68d..021a1141 100644 --- a/src/protocols/XDGDecoration.cpp +++ b/src/protocols/XDGDecoration.cpp @@ -26,6 +26,8 @@ CXDGDecoration::CXDGDecoration(SP resource_, wl_resou LOGM(LOG, "unsetMode. Sending MODE_SERVER_SIDE."); resource->sendConfigure(ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); }); + + resource->sendConfigure(ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } bool CXDGDecoration::good() { diff --git a/src/protocols/XDGOutput.cpp b/src/protocols/XDGOutput.cpp index 9d3e2ac9..03e58956 100644 --- a/src/protocols/XDGOutput.cpp +++ b/src/protocols/XDGOutput.cpp @@ -1,6 +1,8 @@ #include "XDGOutput.hpp" #include "../Compositor.hpp" #include "../config/ConfigValue.hpp" +#include "../xwayland/XWayland.hpp" +#include "core/Output.hpp" #define OUTPUT_MANAGER_VERSION 3 #define OUTPUT_DONE_DEPRECATED_SINCE_VERSION 3 @@ -47,15 +49,15 @@ CXDGOutputProtocol::CXDGOutputProtocol(const wl_interface* iface, const int& ver } void CXDGOutputProtocol::onManagerGetXDGOutput(CZxdgOutputManagerV1* mgr, uint32_t id, wl_resource* outputResource) { - const auto OUTPUT = wlr_output_from_resource(outputResource); + const auto OUTPUT = CWLOutputResource::fromResource(outputResource); - const auto PMONITOR = g_pCompositor->getMonitorFromOutput(OUTPUT); + const auto PMONITOR = OUTPUT->monitor.get(); const auto CLIENT = mgr->client(); CXDGOutput* pXDGOutput = m_vXDGOutputs.emplace_back(std::make_unique(makeShared(CLIENT, mgr->version(), id), PMONITOR)).get(); #ifndef NO_XWAYLAND - if (g_pXWaylandManager->m_sWLRXWayland && g_pXWaylandManager->m_sWLRXWayland->server && g_pXWaylandManager->m_sWLRXWayland->server->client == CLIENT) + if (g_pXWayland && g_pXWayland->pServer && g_pXWayland->pServer->xwaylandClient == CLIENT) pXDGOutput->isXWayland = true; #endif pXDGOutput->client = CLIENT; diff --git a/src/protocols/XDGShell.cpp b/src/protocols/XDGShell.cpp index cbb93de9..dcbc6162 100644 --- a/src/protocols/XDGShell.cpp +++ b/src/protocols/XDGShell.cpp @@ -3,6 +3,7 @@ #include "../Compositor.hpp" #include "../managers/SeatManager.hpp" #include "core/Seat.hpp" +#include "core/Compositor.hpp" #define LOGM PROTO::xdgShell->protoLog @@ -288,7 +289,8 @@ void CXDGToplevelResource::close() { resource->sendClose(); } -CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SP owner_, wlr_surface* surface_) : owner(owner_), surface(surface_), resource(resource_) { +CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SP owner_, SP surface_) : + owner(owner_), surface(surface_), resource(resource_) { if (!good()) return; @@ -307,56 +309,50 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SPdestroyResource(this); }); - hyprListener_surfaceDestroy.initCallback( - &surface->events.destroy, - [this](void* owner, void* data) { - LOGM(WARN, "wl_surface destroyed before its xdg_surface role object"); - hyprListener_surfaceDestroy.removeCallback(); - hyprListener_surfaceCommit.removeCallback(); + listeners.surfaceDestroy = surface->events.destroy.registerListener([this](std::any d) { + LOGM(WARN, "wl_surface destroyed before its xdg_surface role object"); + listeners.surfaceDestroy.reset(); + listeners.surfaceCommit.reset(); - if (mapped) - events.unmap.emit(); + if (mapped) + events.unmap.emit(); - mapped = false; - surface = nullptr; - events.destroy.emit(); - }, - nullptr, "CXDGSurfaceResource"); + mapped = false; + surface.reset(); + events.destroy.emit(); + }); - hyprListener_surfaceCommit.initCallback( - &surface->events.commit, - [this](void* owner, void* data) { - current = pending; + listeners.surfaceCommit = surface->events.commit.registerListener([this](std::any d) { + current = pending; + if (toplevel) + toplevel->current = toplevel->pending; + + if (initialCommit && surface->pending.buffer) { + resource->error(-1, "Buffer attached before initial commit"); + return; + } + + if (surface->current.buffer && !mapped) { + // this forces apps to not draw CSD. if (toplevel) - toplevel->current = toplevel->pending; + toplevel->setMaximized(true); - if (initialCommit && surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0) { - resource->error(-1, "Buffer attached before initial commit"); - return; - } + mapped = true; + surface->map(); + events.map.emit(); + return; + } - if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0 && !mapped) { - // this forces apps to not draw CSD. - if (toplevel) - toplevel->setMaximized(true); + if (!surface->current.buffer && mapped) { + mapped = false; + surface->unmap(); + events.unmap.emit(); + return; + } - mapped = true; - wlr_surface_map(surface); - events.map.emit(); - return; - } - - if (surface->pending.buffer_width <= 0 && surface->pending.buffer_height <= 0 && mapped) { - mapped = false; - wlr_surface_unmap(surface); - events.unmap.emit(); - return; - } - - events.commit.emit(); - initialCommit = false; - }, - nullptr, "CXDGSurfaceResource"); + events.commit.emit(); + initialCommit = false; + }); resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) { const auto RESOURCE = PROTO::xdgShell->m_vToplevels.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock())); @@ -407,8 +403,10 @@ CXDGSurfaceResource::CXDGSurfaceResource(SP resource_, SPsetAckConfigure([this](CXdgSurface* r, uint32_t serial) { + if (serial < lastConfigureSerial) + return; + lastConfigureSerial = serial; events.ack.emit(serial); - ; // TODO: verify it }); resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { @@ -421,6 +419,12 @@ CXDGSurfaceResource::~CXDGSurfaceResource() { events.destroy.emit(); if (configureSource) wl_event_source_remove(configureSource); + if (surface) + surface->resetRole(); +} + +eSurfaceRole CXDGSurfaceResource::role() { + return SURFACE_ROLE_XDG_SHELL; } bool CXDGSurfaceResource::good() { @@ -575,11 +579,11 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa if (leftEdgeOut && slideX) test.x = constraint.x + EDGE_PADDING; if (rightEdgeOut && slideX) - test.x = constraint.x + constraint.w - predictedBox.w - EDGE_PADDING; + test.x = std::clamp((double)(constraint.x + constraint.w - test.w), (double)(constraint.x + EDGE_PADDING), (double)INFINITY); if (topEdgeOut && slideY) test.y = constraint.y + EDGE_PADDING; if (bottomEdgeOut && slideY) - test.y = constraint.y + constraint.h - predictedBox.y - EDGE_PADDING; + test.y = std::clamp((double)(constraint.y + constraint.h - test.h), (double)(constraint.y + EDGE_PADDING), (double)INFINITY); success = test.copy().expand(-1).inside(constraint); @@ -602,18 +606,18 @@ CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& pa test.x = constraint.x + EDGE_PADDING; } if (rightEdgeOut && resizeX) - test.w = -(constraint.w + constraint.x - test.w - test.x + EDGE_PADDING); + test.w = constraint.w - (test.x - constraint.w) - EDGE_PADDING; if (topEdgeOut && resizeY) { test.h = test.y + test.h - constraint.y - EDGE_PADDING; test.y = constraint.y + EDGE_PADDING; } if (bottomEdgeOut && resizeY) - test.h = -(constraint.h + constraint.y - test.h - test.y + EDGE_PADDING); + test.h = constraint.h - (test.y - constraint.y) - EDGE_PADDING; success = test.copy().expand(-1).inside(constraint); if (success) - return test.translate(parentCoord - constraint.pos()); + return test.translate(-parentCoord - constraint.pos()); } LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place"); @@ -648,8 +652,19 @@ CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { }); resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) { - const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back( - makeShared(makeShared(r->client(), r->version(), id), self.lock(), wlr_surface_from_resource(surf))); + auto SURF = CWLSurfaceResource::fromResource(surf); + + if (!SURF) { + r->error(-1, "Invalid surface passed"); + return; + } + + if (SURF->role->role() != SURFACE_ROLE_UNASSIGNED) { + r->error(-1, "Surface already has a different role"); + return; + } + + const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), self.lock(), SURF)); if (!RESOURCE->good()) { r->noMemory(); @@ -658,6 +673,7 @@ CXDGWMBase::CXDGWMBase(SP resource_) : resource(resource_) { } RESOURCE->self = RESOURCE; + SURF->role = RESOURCE; surfaces.emplace_back(RESOURCE); @@ -724,9 +740,9 @@ void CXDGShellProtocol::addOrStartGrab(SP popup) { grabOwner = popup; grabbed.clear(); grab->clear(); - grab->add(popup->surface->surface); + grab->add(popup->surface->surface.lock()); if (popup->parent) - grab->add(popup->parent->surface); + grab->add(popup->parent->surface.lock()); g_pSeatManager->setGrab(grab); grabbed.emplace_back(popup); return; @@ -734,10 +750,10 @@ void CXDGShellProtocol::addOrStartGrab(SP popup) { grabbed.emplace_back(popup); - grab->add(popup->surface->surface); + grab->add(popup->surface->surface.lock()); if (popup->parent) - grab->add(popup->parent->surface); + grab->add(popup->parent->surface.lock()); } void CXDGShellProtocol::onPopupDestroy(WP popup) { @@ -752,5 +768,5 @@ void CXDGShellProtocol::onPopupDestroy(WP popup) { std::erase(grabbed, popup); if (popup->surface) - grab->remove(popup->surface->surface); + grab->remove(popup->surface->surface.lock()); } diff --git a/src/protocols/XDGShell.hpp b/src/protocols/XDGShell.hpp index 1dbeb209..d60db86a 100644 --- a/src/protocols/XDGShell.hpp +++ b/src/protocols/XDGShell.hpp @@ -9,6 +9,7 @@ #include "../helpers/Vector2D.hpp" #include "../helpers/Box.hpp" #include "../helpers/signal/Signal.hpp" +#include "types/SurfaceRole.hpp" class CXDGWMBase; class CXDGPositionerResource; @@ -16,6 +17,7 @@ class CXDGSurfaceResource; class CXDGToplevelResource; class CXDGPopupResource; class CSeatGrab; +class CWLSurfaceResource; struct SXDGPositionerState { Vector2D requestedSize; @@ -136,17 +138,19 @@ class CXDGToplevelResource { void applyState(); }; -class CXDGSurfaceResource { +class CXDGSurfaceResource : public ISurfaceRole { public: - CXDGSurfaceResource(SP resource_, SP owner_, wlr_surface* surface_); + CXDGSurfaceResource(SP resource_, SP owner_, SP surface_); ~CXDGSurfaceResource(); static SP fromResource(wl_resource*); + virtual eSurfaceRole role(); + bool good(); WP owner; - wlr_surface* surface = nullptr; + WP surface; WP toplevel; WP popup; @@ -184,8 +188,10 @@ class CXDGSurfaceResource { // std::vector> popups; - DYNLISTENER(surfaceDestroy); - DYNLISTENER(surfaceCommit); + struct { + CHyprSignalListener surfaceDestroy; + CHyprSignalListener surfaceCommit; + } listeners; friend class CXDGPopupResource; friend class CXDGToplevelResource; diff --git a/src/protocols/XWaylandShell.cpp b/src/protocols/XWaylandShell.cpp new file mode 100644 index 00000000..6cc5256f --- /dev/null +++ b/src/protocols/XWaylandShell.cpp @@ -0,0 +1,85 @@ +#include "XWaylandShell.hpp" +#include "core/Compositor.hpp" +#include + +#define LOGM PROTO::xwaylandShell->protoLog + +CXWaylandSurfaceResource::CXWaylandSurfaceResource(SP resource_, SP surface_) : surface(surface_), resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CXwaylandSurfaceV1* r) { + events.destroy.emit(); + PROTO::xwaylandShell->destroyResource(this); + }); + resource->setOnDestroy([this](CXwaylandSurfaceV1* r) { + events.destroy.emit(); + PROTO::xwaylandShell->destroyResource(this); + }); + + pClient = resource->client(); + + resource->setSetSerial([this](CXwaylandSurfaceV1* r, uint32_t lo, uint32_t hi) { + serial = (((uint64_t)hi) << 32) + lo; + PROTO::xwaylandShell->events.newSurface.emit(self.lock()); + }); +} + +CXWaylandSurfaceResource::~CXWaylandSurfaceResource() { + events.destroy.emit(); +} + +bool CXWaylandSurfaceResource::good() { + return resource->resource(); +} + +wl_client* CXWaylandSurfaceResource::client() { + return pClient; +} + +CXWaylandShellResource::CXWaylandShellResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CXwaylandShellV1* r) { PROTO::xwaylandShell->destroyResource(this); }); + resource->setOnDestroy([this](CXwaylandShellV1* r) { PROTO::xwaylandShell->destroyResource(this); }); + + resource->setGetXwaylandSurface([this](CXwaylandShellV1* r, uint32_t id, wl_resource* surface) { + const auto RESOURCE = PROTO::xwaylandShell->m_vSurfaces.emplace_back( + makeShared(makeShared(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surface))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::xwaylandShell->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); +} + +bool CXWaylandShellResource::good() { + return resource->resource(); +} + +CXWaylandShellProtocol::CXWaylandShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CXWaylandShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CXWaylandShellProtocol::destroyResource(CXWaylandShellResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CXWaylandShellProtocol::destroyResource(CXWaylandSurfaceResource* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/XWaylandShell.hpp b/src/protocols/XWaylandShell.hpp new file mode 100644 index 00000000..c8c0c04a --- /dev/null +++ b/src/protocols/XWaylandShell.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include "WaylandProtocol.hpp" +#include "xwayland-shell-v1.hpp" +#include "../helpers/signal/Signal.hpp" + +class CWLSurfaceResource; + +class CXWaylandSurfaceResource { + public: + CXWaylandSurfaceResource(SP resource_, SP surface_); + ~CXWaylandSurfaceResource(); + + bool good(); + wl_client* client(); + + struct { + CSignal destroy; + } events; + + uint64_t serial = 0; + WP surface; + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CXWaylandShellResource { + public: + CXWaylandShellResource(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CXWaylandShellProtocol : public IWaylandProtocol { + public: + CXWaylandShellProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + struct { + CSignal newSurface; // SP. Fired when it sets a serial, otherwise it's useless + } events; + + private: + void destroyResource(CXWaylandSurfaceResource* resource); + void destroyResource(CXWaylandShellResource* resource); + + // + std::vector> m_vManagers; + std::vector> m_vSurfaces; + + friend class CXWaylandSurfaceResource; + friend class CXWaylandShellResource; +}; + +namespace PROTO { + inline UP xwaylandShell; +}; diff --git a/src/protocols/core/Compositor.cpp b/src/protocols/core/Compositor.cpp new file mode 100644 index 00000000..db6e3258 --- /dev/null +++ b/src/protocols/core/Compositor.cpp @@ -0,0 +1,475 @@ +#include "Compositor.hpp" +#include "Output.hpp" +#include "../types/WLBuffer.hpp" +#include +#include +#include "Subcompositor.hpp" +#include "../Viewporter.hpp" +#include "../../helpers/Monitor.hpp" + +#define LOGM PROTO::compositor->protoLog + +class CDefaultSurfaceRole : public ISurfaceRole { + public: + virtual eSurfaceRole role() { + return SURFACE_ROLE_UNASSIGNED; + } +}; + +SP defaultRole = makeShared(); + +CWLCallbackResource::CWLCallbackResource(SP resource_) : resource(resource_) { + ; +} + +bool CWLCallbackResource::good() { + return resource->resource(); +} + +void CWLCallbackResource::send(timespec* now) { + resource->sendDone(now->tv_sec * 1000 + now->tv_nsec / 1000000); +} + +CWLRegionResource::CWLRegionResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); }); + resource->setOnDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); }); + + resource->setAdd([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { region.add(CBox{x, y, w, h}); }); + resource->setSubtract([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { region.subtract(CBox{x, y, w, h}); }); +} + +bool CWLRegionResource::good() { + return resource->resource(); +} + +SP CWLRegionResource::fromResource(wl_resource* res) { + auto data = (CWLRegionResource*)(((CWlRegion*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +CWLSurfaceResource::CWLSurfaceResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setData(this); + + role = defaultRole; + + resource->setDestroy([this](CWlSurface* r) { destroy(); }); + resource->setOnDestroy([this](CWlSurface* r) { destroy(); }); + + resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) { + pending.offset = {x, y}; + + if (!buffer) { + pending.buffer.reset(); + pending.texture.reset(); + } else { + auto res = CWLBufferResource::fromResource(buffer); + pending.buffer = res && res->buffer ? res->buffer.lock() : nullptr; + pending.size = res && res->buffer ? res->buffer->size : Vector2D{}; + pending.texture = res && res->buffer ? res->buffer->texture : nullptr; + } + + Vector2D oldBufSize = current.buffer ? current.buffer->size : Vector2D{}; + Vector2D newBufSize = pending.buffer ? pending.buffer->size : Vector2D{}; + + if (oldBufSize != newBufSize) + pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; + + bufferReleased = false; + }); + + resource->setCommit([this](CWlSurface* r) { + if (pending.buffer) + pending.bufferDamage.intersect(CBox{{}, pending.buffer->size}); + + if (!pending.buffer) + pending.size = {}; + else if (pending.viewport.hasDestination) + pending.size = pending.viewport.destination; + else if (pending.viewport.hasSource) + pending.size = pending.viewport.source.size(); + else { + Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.buffer->size.y, pending.buffer->size.x} : pending.buffer->size; + pending.size = tfs / pending.scale; + } + + if (viewportResource) + viewportResource->verify(); + + pending.damage.intersect(CBox{{}, pending.size}); + + auto previousBuffer = current.buffer; + CRegion previousBufferDamage = accumulateCurrentBufferDamage(); + + current = pending; + pending.damage.clear(); + pending.bufferDamage.clear(); + + if (current.buffer && !bufferReleased) { + // without previous dolphin et al are weird vvv + //CRegion surfaceDamage = + // current.damage.copy().scale(current.scale).transform(current.transform, current.size.x, current.size.y).add(current.bufferDamage).add(previousBufferDamage); + current.buffer->update(CBox{{}, {INT32_MAX, INT32_MAX}}); // FIXME: figure this out to not use this hack. QT apps are wonky without this. + + // release the buffer if it's synchronous as update() has done everything thats needed + // so we can let the app know we're done. + if (current.buffer->isSynchronous()) { + current.buffer->sendRelease(); + bufferReleased = true; + } + } + + // TODO: we should _accumulate_ and not replace above if sync + if (role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = (CWLSubsurfaceResource*)role.get(); + if (subsurface->sync) + return; + + events.commit.emit(); + } else { + // send commit to all synced surfaces in this tree. + breadthfirst( + [](SP surf, const Vector2D& offset, void* data) { + if (surf->role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = (CWLSubsurfaceResource*)surf->role.get(); + if (!subsurface->sync) + return; + } + surf->events.commit.emit(); + }, + nullptr); + } + + // for async buffers, we can only release the buffer once we are unrefing it from current. + if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) { + previousBuffer->sendRelease(); + bufferReleased = true; + } + }); + + resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); }); + resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.bufferDamage.add(CBox{x, y, w, h}); }); + + resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { pending.scale = scale; }); + resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; }); + + resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) { + if (!region) { + pending.input = CBox{{}, {INT32_MAX, INT32_MAX}}; + return; + } + + auto RG = CWLRegionResource::fromResource(region); + pending.input = RG->region; + }); + + resource->setSetOpaqueRegion([this](CWlSurface* r, wl_resource* region) { + if (!region) { + pending.opaque = CBox{{}, {}}; + return; + } + + auto RG = CWLRegionResource::fromResource(region); + pending.opaque = RG->region; + }); + + resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared(makeShared(pClient, 1, id))); }); + + resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { pending.offset = {x, y}; }); +} + +CWLSurfaceResource::~CWLSurfaceResource() { + events.destroy.emit(); +} + +void CWLSurfaceResource::destroy() { + if (mapped) + unmap(); + events.destroy.emit(); + PROTO::compositor->destroyResource(this); +} + +SP CWLSurfaceResource::fromResource(wl_resource* res) { + auto data = (CWLSurfaceResource*)(((CWlSurface*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CWLSurfaceResource::good() { + return resource->resource(); +} + +wl_client* CWLSurfaceResource::client() { + return pClient; +} + +void CWLSurfaceResource::enter(SP monitor) { + if (std::find(enteredOutputs.begin(), enteredOutputs.end(), monitor) != enteredOutputs.end()) + return; + + if (!PROTO::outputs.contains(monitor->szName)) { + // can happen on unplug/replug + LOGM(ERR, "enter() called on a non-existent output global"); + return; + } + + auto output = PROTO::outputs.at(monitor->szName)->outputResourceFrom(pClient); + + if (!output || !output->getResource() || !output->getResource()->resource()) { + LOGM(ERR, "Cannot enter surface {:x} to {}, client hasn't bound the output", (uintptr_t)this, monitor->szName); + return; + } + + enteredOutputs.emplace_back(monitor); + + resource->sendEnter(output->getResource().get()); +} + +void CWLSurfaceResource::leave(SP monitor) { + if (std::find(enteredOutputs.begin(), enteredOutputs.end(), monitor) == enteredOutputs.end()) + return; + + auto output = PROTO::outputs.at(monitor->szName)->outputResourceFrom(pClient); + + if (!output) { + LOGM(ERR, "Cannot leave surface {:x} from {}, client hasn't bound the output", (uintptr_t)this, monitor->szName); + return; + } + + std::erase(enteredOutputs, monitor); + + resource->sendLeave(output->getResource().get()); +} + +void CWLSurfaceResource::sendPreferredTransform(wl_output_transform t) { + if (resource->version() < 6) + return; + resource->sendPreferredBufferTransform(t); +} + +void CWLSurfaceResource::sendPreferredScale(int32_t scale) { + if (resource->version() < 6) + return; + resource->sendPreferredBufferScale(scale); +} + +void CWLSurfaceResource::frame(timespec* now) { + if (callbacks.empty()) + return; + + for (auto& c : callbacks) { + c->send(now); + } + + callbacks.clear(); +} + +void CWLSurfaceResource::resetRole() { + role = defaultRole; +} + +void CWLSurfaceResource::bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data) { + for (auto& n : nodes) { + + Vector2D offset = {}; + if (n->role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = (CWLSubsurfaceResource*)n->role.get(); + offset = subsurface->posRelativeToParent(); + } + + fn(n, offset, data); + } + + std::vector> nodes2; + + for (auto& n : nodes) { + std::erase_if(n->subsurfaces, [](const auto& e) { return e.expired(); }); + for (auto& c : n->subsurfaces) { + nodes2.push_back(c->surface.lock()); + } + } + + if (!nodes2.empty()) + bfHelper(nodes2, fn, data); +} + +void CWLSurfaceResource::breadthfirst(std::function, const Vector2D&, void*)> fn, void* data) { + std::vector> surfs; + surfs.push_back(self.lock()); + bfHelper(surfs, fn, data); +} + +std::pair, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput) { + std::vector, Vector2D>> surfs; + breadthfirst([](SP surf, const Vector2D& offset, + void* data) { ((std::vector, Vector2D>>*)data)->emplace_back(std::make_pair<>(surf, offset)); }, + &surfs); + + for (auto& [surf, pos] : surfs | std::views::reverse) { + if (!allowsInput) { + const auto BOX = CBox{pos, surf->current.size}; + if (BOX.containsPoint(localCoords)) + return {surf, localCoords - pos}; + } else { + const auto REGION = surf->current.input.copy().intersect(CBox{{}, surf->current.size}).translate(pos); + if (REGION.containsPoint(localCoords)) + return {surf, localCoords - pos}; + } + } + + return {nullptr, {}}; +} + +uint32_t CWLSurfaceResource::id() { + return wl_resource_get_id(resource->resource()); +} + +void CWLSurfaceResource::map() { + if (mapped) + return; + + mapped = true; + + events.map.emit(); + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + frame(&now); + + current.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; + pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}}; +} + +void CWLSurfaceResource::unmap() { + if (!mapped) + return; + + mapped = false; + + events.unmap.emit(); +} + +void CWLSurfaceResource::error(int code, const std::string& str) { + resource->error(code, str); +} + +SP CWLSurfaceResource::getResource() { + return resource; +} + +CBox CWLSurfaceResource::extends() { + CRegion full = CBox{{}, current.size}; + breadthfirst( + [](SP surf, const Vector2D& offset, void* d) { + if (surf->role->role() != SURFACE_ROLE_SUBSURFACE) + return; + + ((CRegion*)d)->add(CBox{offset, surf->current.size}); + }, + &full); + return full.getExtents(); +} + +Vector2D CWLSurfaceResource::sourceSize() { + if (!current.buffer) + return {}; + + if (current.viewport.hasSource) + return current.viewport.source.size(); + + Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size; + return trc / current.scale; +} + +CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() { + if (!current.buffer) + return {}; + + CRegion surfaceDamage = current.damage; + if (current.viewport.hasDestination) { + Vector2D scale = sourceSize() / current.viewport.destination; + surfaceDamage.scale(scale); + } + + if (current.viewport.hasSource) + surfaceDamage.translate(current.viewport.source.pos()); + + Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size; + + return surfaceDamage.scale(current.scale).transform(wlr_output_transform_invert(current.transform), trc.x, trc.y).add(current.bufferDamage); +} + +CWLCompositorResource::CWLCompositorResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlCompositor* r) { PROTO::compositor->destroyResource(this); }); + + resource->setCreateSurface([](CWlCompositor* r, uint32_t id) { + const auto RESOURCE = PROTO::compositor->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::compositor->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + LOGM(LOG, "New wl_surface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); + + PROTO::compositor->events.newSurface.emit(RESOURCE); + }); + + resource->setCreateRegion([](CWlCompositor* r, uint32_t id) { + const auto RESOURCE = PROTO::compositor->m_vRegions.emplace_back(makeShared(makeShared(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::compositor->m_vRegions.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + + LOGM(LOG, "New wl_region with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); + }); +} + +bool CWLCompositorResource::good() { + return resource->resource(); +} + +CWLCompositorProtocol::CWLCompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CWLCompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CWLCompositorProtocol::destroyResource(CWLCompositorResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLCompositorProtocol::destroyResource(CWLSurfaceResource* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLCompositorProtocol::destroyResource(CWLRegionResource* resource) { + std::erase_if(m_vRegions, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/core/Compositor.hpp b/src/protocols/core/Compositor.hpp new file mode 100644 index 00000000..f50144bf --- /dev/null +++ b/src/protocols/core/Compositor.hpp @@ -0,0 +1,175 @@ +#pragma once + +/* + Implementations for: + - wl_compositor + - wl_surface + - wl_region + - wl_callback +*/ + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include "wayland.hpp" +#include "../../helpers/signal/Signal.hpp" +#include "../../helpers/Region.hpp" +#include "../types/Buffer.hpp" +#include "../types/SurfaceRole.hpp" + +class CWLOutputResource; +class CMonitor; +class CWLSurface; +class CWLSurfaceResource; +class CWLSubsurfaceResource; +class CViewportResource; + +class CWLCallbackResource { + public: + CWLCallbackResource(SP resource_); + + bool good(); + void send(timespec* now); + + private: + SP resource; +}; + +class CWLRegionResource { + public: + CWLRegionResource(SP resource_); + static SP fromResource(wl_resource* res); + + bool good(); + + CRegion region; + WP self; + + private: + SP resource; +}; + +class CWLSurfaceResource { + public: + CWLSurfaceResource(SP resource_); + ~CWLSurfaceResource(); + + static SP fromResource(wl_resource* res); + + bool good(); + wl_client* client(); + void enter(SP monitor); + void leave(SP monitor); + void sendPreferredTransform(wl_output_transform t); + void sendPreferredScale(int32_t scale); + void frame(timespec* now); + uint32_t id(); + void map(); + void unmap(); + void error(int code, const std::string& str); + SP getResource(); + CBox extends(); + void resetRole(); + Vector2D sourceSize(); + + struct { + CSignal commit; + CSignal map; + CSignal unmap; + CSignal newSubsurface; + CSignal destroy; + } events; + + struct { + CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */; + wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; + int scale = 1; + SP buffer; + SP texture; + Vector2D offset; + Vector2D size; + struct { + bool hasDestination = false; + bool hasSource = false; + Vector2D destination; + CBox source; + } viewport; + + // + void reset() { + damage.clear(); + bufferDamage.clear(); + transform = WL_OUTPUT_TRANSFORM_NORMAL; + scale = 1; + offset = {}; + size = {}; + } + } current, pending; + + std::vector> callbacks; + WP self; + WP hlSurface; + std::vector> enteredOutputs; + bool mapped = false; + std::vector> subsurfaces; + WP role; + WP viewportResource; + + void breadthfirst(std::function, const Vector2D&, void*)> fn, void* data); + CRegion accumulateCurrentBufferDamage(); + + // returns a pair: found surface (null if not found) and surface local coords. + // localCoords param is relative to 0,0 of this surface + std::pair, Vector2D> at(const Vector2D& localCoords, bool allowsInput = false); + + private: + SP resource; + wl_client* pClient = nullptr; + + // tracks whether we should release the buffer + bool bufferReleased = false; + + void destroy(); + void bfHelper(std::vector> nodes, std::function, const Vector2D&, void*)> fn, void* data); +}; + +class CWLCompositorResource { + public: + CWLCompositorResource(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CWLCompositorProtocol : public IWaylandProtocol { + public: + CWLCompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + struct { + CSignal newSurface; // SP + } events; + + private: + void destroyResource(CWLCompositorResource* resource); + void destroyResource(CWLSurfaceResource* resource); + void destroyResource(CWLRegionResource* resource); + + // + std::vector> m_vManagers; + std::vector> m_vSurfaces; + std::vector> m_vRegions; + + friend class CWLSurfaceResource; + friend class CWLCompositorResource; + friend class CWLRegionResource; + friend class CWLCallbackResource; +}; + +namespace PROTO { + inline UP compositor; +}; diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index 354259fe..ca044e93 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -4,6 +4,7 @@ #include "../../managers/PointerManager.hpp" #include "../../Compositor.hpp" #include "Seat.hpp" +#include "Compositor.hpp" #define LOGM PROTO::data->protoLog @@ -233,7 +234,7 @@ CWLDataDeviceResource::CWLDataDeviceResource(SP resource_) : reso source->dnd = true; - PROTO::data->initiateDrag(source, icon ? wlr_surface_from_resource(icon) : nullptr, wlr_surface_from_resource(origin)); + PROTO::data->initiateDrag(source, icon ? CWLSurfaceResource::fromResource(icon) : nullptr, CWLSurfaceResource::fromResource(origin)); }); } @@ -252,8 +253,8 @@ void CWLDataDeviceResource::sendDataOffer(SP offer) { resource->sendDataOfferRaw(nullptr); } -void CWLDataDeviceResource::sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP offer) { - resource->sendEnterRaw(serial, surf->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource()); +void CWLDataDeviceResource::sendEnter(uint32_t serial, SP surf, const Vector2D& local, SP offer) { + resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource()); } void CWLDataDeviceResource::sendLeave() { @@ -454,7 +455,7 @@ void CWLDataDeviceProtocol::onKeyboardFocus() { updateDrag(); } -void CWLDataDeviceProtocol::initiateDrag(WP currentSource, wlr_surface* dragSurface, wlr_surface* origin) { +void CWLDataDeviceProtocol::initiateDrag(WP currentSource, SP dragSurface, SP origin) { if (dnd.currentSource) { LOGM(WARN, "New drag started while old drag still active??"); @@ -472,22 +473,18 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource dnd.originSurface = origin; dnd.dndSurface = dragSurface; if (dragSurface) { - dnd.hyprListener_dndSurfaceDestroy.initCallback( - &dragSurface->events.destroy, [this](void* owner, void* data) { abortDrag(); }, nullptr, "CWLDataDeviceProtocol::drag"); - dnd.hyprListener_dndSurfaceCommit.initCallback( - &dragSurface->events.commit, - [this](void* owner, void* data) { - if (dnd.dndSurface->pending.buffer_width > 0 && dnd.dndSurface->pending.buffer_height > 0 && !dnd.dndSurface->mapped) { - wlr_surface_map(dnd.dndSurface); - return; - } + dnd.dndSurfaceDestroy = dragSurface->events.destroy.registerListener([this](std::any d) { abortDrag(); }); + dnd.dndSurfaceCommit = dragSurface->events.commit.registerListener([this](std::any d) { + if (dnd.dndSurface->current.buffer && !dnd.dndSurface->mapped) { + dnd.dndSurface->map(); + return; + } - if (dnd.dndSurface->pending.buffer_width <= 0 && dnd.dndSurface->pending.buffer_height <= 0 && dnd.dndSurface->mapped) { - wlr_surface_unmap(dnd.dndSurface); - return; - } - }, - nullptr, "CWLDataDeviceProtocol::drag"); + if (dnd.dndSurface->current.buffer <= 0 && dnd.dndSurface->mapped) { + dnd.dndSurface->unmap(); + return; + } + }); } dnd.mouseButton = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any e) { @@ -506,7 +503,7 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource dnd.mouseMove = g_pHookSystem->hookDynamic("mouseMove", [this](void* self, SCallbackInfo& info, std::any e) { auto V = std::any_cast(e); if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) { - auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus); + auto surf = CWLSurface::fromResource(g_pSeatManager->state.keyboardFocus.lock()); if (!surf) return; @@ -524,7 +521,7 @@ void CWLDataDeviceProtocol::initiateDrag(WP currentSource dnd.touchMove = g_pHookSystem->hookDynamic("touchMove", [this](void* self, SCallbackInfo& info, std::any e) { auto E = std::any_cast(e); if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) { - auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus); + auto surf = CWLSurface::fromResource(g_pSeatManager->state.keyboardFocus.lock()); if (!surf) return; @@ -572,14 +569,14 @@ void CWLDataDeviceProtocol::updateDrag() { dnd.focusedDevice->sendDataOffer(OFFER); OFFER->sendData(); - dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.keyboardFocus, - Vector2D{g_pSeatManager->state.keyboardFocus->current.width, g_pSeatManager->state.keyboardFocus->current.height} / 2.F, OFFER); + dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.keyboardFocus.lock(), + g_pSeatManager->state.keyboardFocus->current.size / 2.F, OFFER); } void CWLDataDeviceProtocol::resetDndState() { - dnd.dndSurface = nullptr; - dnd.hyprListener_dndSurfaceDestroy.removeCallback(); - dnd.hyprListener_dndSurfaceCommit.removeCallback(); + dnd.dndSurface.reset(); + dnd.dndSurfaceCommit.reset(); + dnd.dndSurfaceDestroy.reset(); dnd.mouseButton.reset(); dnd.mouseMove.reset(); dnd.touchUp.reset(); @@ -638,20 +635,18 @@ void CWLDataDeviceProtocol::abortDrag() { } void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) { - if (!dnd.dndSurface || !wlr_surface_get_texture(dnd.dndSurface)) + if (!dnd.dndSurface || !dnd.dndSurface->current.buffer || !dnd.dndSurface->current.buffer->texture) return; const auto POS = g_pInputManager->getMouseCoordsInternal(); - CBox box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}} - .translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F) - .scale(pMonitor->scale); - g_pHyprOpenGL->renderTexture(wlr_surface_get_texture(dnd.dndSurface), &box, 1.F); + CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale); + g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.buffer->texture, &box, 1.F); - box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}}.translate(g_pPointerManager->cursorSizeLogical() / 2.F); + box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F); g_pHyprRenderer->damageBox(&box); - wlr_surface_send_frame_done(dnd.dndSurface, when); + dnd.dndSurface->frame(when); } bool CWLDataDeviceProtocol::dndActive() { diff --git a/src/protocols/core/DataDevice.hpp b/src/protocols/core/DataDevice.hpp index 3112b720..f31725ee 100644 --- a/src/protocols/core/DataDevice.hpp +++ b/src/protocols/core/DataDevice.hpp @@ -23,6 +23,7 @@ class CWLDataDeviceManagerResource; class CWLDataSourceResource; class CWLDataOfferResource; +class CWLSurfaceResource; class CMonitor; class CWLDataOfferResource { @@ -92,7 +93,7 @@ class CWLDataDeviceResource { wl_client* client(); void sendDataOffer(SP offer); - void sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP offer); + void sendEnter(uint32_t serial, SP surf, const Vector2D& local, SP offer); void sendLeave(); void sendMotion(uint32_t timeMs, const Vector2D& local); void sendDrop(); @@ -155,11 +156,11 @@ class CWLDataDeviceProtocol : public IWaylandProtocol { struct { WP focusedDevice; WP currentSource; - wlr_surface* dndSurface = nullptr; - wlr_surface* originSurface = nullptr; // READ-ONLY + WP dndSurface; + WP originSurface; bool overriddenCursor = false; - DYNLISTENER(dndSurfaceDestroy); - DYNLISTENER(dndSurfaceCommit); + CHyprSignalListener dndSurfaceDestroy; + CHyprSignalListener dndSurfaceCommit; // for ending a dnd SP mouseMove; @@ -169,7 +170,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol { } dnd; void abortDrag(); - void initiateDrag(WP currentSource, wlr_surface* dragSurface, wlr_surface* origin); + void initiateDrag(WP currentSource, SP dragSurface, SP origin); void updateDrag(); void dropDrag(); void completeDrag(); diff --git a/src/protocols/core/Output.cpp b/src/protocols/core/Output.cpp new file mode 100644 index 00000000..90358fa5 --- /dev/null +++ b/src/protocols/core/Output.cpp @@ -0,0 +1,111 @@ +#include "Output.hpp" +#include "../../helpers/Monitor.hpp" + +CWLOutputResource::CWLOutputResource(SP resource_, SP pMonitor) : monitor(pMonitor), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + pClient = resource->client(); + + resource->setOnDestroy([this](CWlOutput* r) { + if (monitor && PROTO::outputs.contains(monitor->szName)) + PROTO::outputs.at(monitor->szName)->destroyResource(this); + }); + resource->setRelease([this](CWlOutput* r) { + if (monitor && PROTO::outputs.contains(monitor->szName)) + PROTO::outputs.at(monitor->szName)->destroyResource(this); + }); + + resource->sendGeometry(0, 0, monitor->output->phys_width, monitor->output->phys_height, monitor->output->subpixel, monitor->output->make ? monitor->output->make : "null", + monitor->output->model ? monitor->output->model : "null", monitor->transform); + if (resource->version() >= 4) { + resource->sendName(monitor->szName.c_str()); + resource->sendDescription(monitor->szDescription.c_str()); + } + + updateState(); +} + +SP CWLOutputResource::fromResource(wl_resource* res) { + auto data = (CWLOutputResource*)(((CWlOutput*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CWLOutputResource::good() { + return resource->resource(); +} + +wl_client* CWLOutputResource::client() { + return pClient; +} + +SP CWLOutputResource::getResource() { + return resource; +} + +void CWLOutputResource::updateState() { + if (!monitor) + return; + + if (resource->version() >= 2) + resource->sendScale(std::ceil(monitor->scale)); + + resource->sendMode((wl_output_mode)(WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED), monitor->vecPixelSize.x, monitor->vecPixelSize.y, monitor->refreshRate * 1000.0); + + if (resource->version() >= 2) + resource->sendDone(); +} + +CWLOutputProtocol::CWLOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name, SP pMonitor) : + IWaylandProtocol(iface, ver, name), monitor(pMonitor), szName(pMonitor->szName) { + + listeners.modeChanged = monitor->events.modeChanged.registerListener([this](std::any d) { + for (auto& o : m_vOutputs) { + o->updateState(); + } + }); +} + +void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vOutputs.emplace_back(makeShared(makeShared(client, ver, id), monitor.lock())); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vOutputs.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; +} + +void CWLOutputProtocol::destroyResource(CWLOutputResource* resource) { + std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; }); + + if (m_vOutputs.empty() && defunct) + PROTO::outputs.erase(szName); +} + +SP CWLOutputProtocol::outputResourceFrom(wl_client* client) { + for (auto& r : m_vOutputs) { + if (r->client() != client) + continue; + + return r; + } + + return nullptr; +} + +void CWLOutputProtocol::remove() { + if (defunct) + return; + + defunct = true; + removeGlobal(); +} + +bool CWLOutputProtocol::isDefunct() { + return defunct; +} diff --git a/src/protocols/core/Output.hpp b/src/protocols/core/Output.hpp new file mode 100644 index 00000000..b17a8272 --- /dev/null +++ b/src/protocols/core/Output.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include "wayland.hpp" +#include "../../helpers/signal/Listener.hpp" + +class CMonitor; + +class CWLOutputResource { + public: + CWLOutputResource(SP resource_, SP pMonitor); + static SP fromResource(wl_resource*); + + bool good(); + wl_client* client(); + SP getResource(); + void updateState(); + + WP monitor; + + WP self; + + private: + SP resource; + wl_client* pClient = nullptr; +}; + +class CWLOutputProtocol : public IWaylandProtocol { + public: + CWLOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name, SP pMonitor); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + SP outputResourceFrom(wl_client* client); + + WP monitor; + + // will mark the protocol for removal, will be removed when no. of bound outputs is 0 (or when overwritten by a new global) + void remove(); + bool isDefunct(); // true if above was called + + private: + void destroyResource(CWLOutputResource* resource); + + // + std::vector> m_vOutputs; + bool defunct = false; + std::string szName = ""; + + struct { + CHyprSignalListener modeChanged; + } listeners; + + friend class CWLOutputResource; +}; + +namespace PROTO { + inline std::unordered_map> outputs; +}; diff --git a/src/protocols/core/Seat.cpp b/src/protocols/core/Seat.cpp index 489fced7..8bf03909 100644 --- a/src/protocols/core/Seat.cpp +++ b/src/protocols/core/Seat.cpp @@ -1,4 +1,5 @@ #include "Seat.hpp" +#include "Compositor.hpp" #include "../../devices/IKeyboard.hpp" #include "../../managers/SeatManager.hpp" #include "../../config/ConfigValue.hpp" @@ -20,7 +21,7 @@ bool CWLTouchResource::good() { return resource->resource(); } -void CWLTouchResource::sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local) { +void CWLTouchResource::sendDown(SP surface, uint32_t timeMs, int32_t id, const Vector2D& local) { if (!owner) return; @@ -29,15 +30,12 @@ void CWLTouchResource::sendDown(wlr_surface* surface, uint32_t timeMs, int32_t i sendUp(timeMs, id); } - ASSERT(wl_resource_get_client(surface->resource) == owner->client()); + ASSERT(surface->client() == owner->client()); - currentSurface = surface; - hyprListener_surfaceDestroy.initCallback( - &surface->events.destroy, [this, id, timeMs](void* owner, void* data) { sendUp(timeMs + 10 /* hack */, id); }, this, "CWLTouchResource"); + currentSurface = surface; + listeners.destroySurface = surface->events.destroy.registerListener([this, timeMs, id](std::any d) { sendUp(timeMs + 10 /* hack */, id); }); - // FIXME: - // fix this once we get our own wlr_surface, this is horrible - resource->sendDownRaw(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->resource, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); + resource->sendDown(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->getResource().get(), id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) { @@ -45,8 +43,8 @@ void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) { return; resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id); - currentSurface = nullptr; - hyprListener_surfaceDestroy.removeCallback(); + currentSurface.reset(); + listeners.destroySurface.reset(); } void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) { @@ -97,7 +95,7 @@ CWLPointerResource::CWLPointerResource(SP resource_, SPonSetCursor(owner.lock(), serial, surf ? wlr_surface_from_resource(surf) : nullptr, {hotX, hotY}); + g_pSeatManager->onSetCursor(owner.lock(), serial, surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hotX, hotY}); }); } @@ -105,7 +103,7 @@ bool CWLPointerResource::good() { return resource->resource(); } -void CWLPointerResource::sendEnter(wlr_surface* surface, const Vector2D& local) { +void CWLPointerResource::sendEnter(SP surface, const Vector2D& local) { if (!owner || currentSurface == surface) return; @@ -114,22 +112,21 @@ void CWLPointerResource::sendEnter(wlr_surface* surface, const Vector2D& local) sendLeave(); } - ASSERT(wl_resource_get_client(surface->resource) == owner->client()); + ASSERT(surface->client() == owner->client()); - currentSurface = surface; - hyprListener_surfaceDestroy.initCallback( - &surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLPointerResource"); + currentSurface = surface; + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); }); - resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); + resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y)); } void CWLPointerResource::sendLeave() { if (!owner || !currentSurface) return; - resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource); - currentSurface = nullptr; - hyprListener_surfaceDestroy.removeCallback(); + resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get()); + currentSurface.reset(); + listeners.destroySurface.reset(); } void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) { @@ -213,6 +210,9 @@ bool CWLKeyboardResource::good() { } void CWLKeyboardResource::sendKeymap(SP keyboard) { + if (!keyboard) + return; + wl_keyboard_keymap_format format = keyboard ? WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 : WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP; int fd; uint32_t size; @@ -234,7 +234,7 @@ void CWLKeyboardResource::sendKeymap(SP keyboard) { close(fd); } -void CWLKeyboardResource::sendEnter(wlr_surface* surface) { +void CWLKeyboardResource::sendEnter(SP surface) { if (!owner || currentSurface == surface) return; @@ -243,16 +243,15 @@ void CWLKeyboardResource::sendEnter(wlr_surface* surface) { sendLeave(); } - ASSERT(wl_resource_get_client(surface->resource) == owner->client()); + ASSERT(surface->client() == owner->client()); - currentSurface = surface; - hyprListener_surfaceDestroy.initCallback( - &surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLKeyboardResource"); + currentSurface = surface; + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); }); wl_array arr; wl_array_init(&arr); - resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, &arr); + resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), &arr); wl_array_release(&arr); } @@ -261,9 +260,9 @@ void CWLKeyboardResource::sendLeave() { if (!owner || !currentSurface) return; - resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource); - currentSurface = nullptr; - hyprListener_surfaceDestroy.removeCallback(); + resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get()); + currentSurface.reset(); + listeners.destroySurface.reset(); } void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) { diff --git a/src/protocols/core/Seat.hpp b/src/protocols/core/Seat.hpp index 4cd0d124..524783f9 100644 --- a/src/protocols/core/Seat.hpp +++ b/src/protocols/core/Seat.hpp @@ -20,6 +20,7 @@ constexpr const char* HL_SEAT_NAME = "Hyprland"; class IKeyboard; +class CWLSurfaceResource; class CWLPointerResource; class CWLKeyboardResource; @@ -31,7 +32,7 @@ class CWLTouchResource { CWLTouchResource(SP resource_, SP owner_); bool good(); - void sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local); + void sendDown(SP surface, uint32_t timeMs, int32_t id, const Vector2D& local); void sendUp(uint32_t timeMs, int32_t id); void sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local); void sendFrame(); @@ -42,10 +43,12 @@ class CWLTouchResource { WP owner; private: - SP resource; - wlr_surface* currentSurface = nullptr; + SP resource; + WP currentSurface; - DYNLISTENER(surfaceDestroy); + struct { + CHyprSignalListener destroySurface; + } listeners; }; class CWLPointerResource { @@ -53,7 +56,7 @@ class CWLPointerResource { CWLPointerResource(SP resource_, SP owner_); bool good(); - void sendEnter(wlr_surface* surface, const Vector2D& local); + void sendEnter(SP surface, const Vector2D& local); void sendLeave(); void sendMotion(uint32_t timeMs, const Vector2D& local); void sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state); @@ -68,10 +71,12 @@ class CWLPointerResource { WP owner; private: - SP resource; - wlr_surface* currentSurface = nullptr; + SP resource; + WP currentSurface; - DYNLISTENER(surfaceDestroy); + struct { + CHyprSignalListener destroySurface; + } listeners; }; class CWLKeyboardResource { @@ -80,7 +85,7 @@ class CWLKeyboardResource { bool good(); void sendKeymap(SP keeb); - void sendEnter(wlr_surface* surface); + void sendEnter(SP surface); void sendLeave(); void sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group); @@ -89,10 +94,12 @@ class CWLKeyboardResource { WP owner; private: - SP resource; - wlr_surface* currentSurface = nullptr; + SP resource; + WP currentSurface; - DYNLISTENER(surfaceDestroy); + struct { + CHyprSignalListener destroySurface; + } listeners; }; class CWLSeatResource { diff --git a/src/protocols/core/Shm.cpp b/src/protocols/core/Shm.cpp new file mode 100644 index 00000000..4088949f --- /dev/null +++ b/src/protocols/core/Shm.cpp @@ -0,0 +1,218 @@ +#include "Shm.hpp" +#include +#include +#include +#include "../../render/Texture.hpp" +#include "../types/WLBuffer.hpp" +#include "../../Compositor.hpp" +#include "../../helpers/Format.hpp" + +#define LOGM PROTO::shm->protoLog + +CWLSHMBuffer::CWLSHMBuffer(SP pool_, uint32_t id, int32_t offset_, const Vector2D& size_, int32_t stride_, uint32_t fmt_) { + if (!pool_->pool->data) + return; + + g_pHyprRenderer->makeEGLCurrent(); + + size = size_; + pool = pool_->pool; + stride = stride_; + fmt = fmt_; + offset = offset_; + opaque = FormatUtils::isFormatOpaque(FormatUtils::shmToDRM(fmt_)); + + texture = makeShared(FormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, size_); + + resource = CWLBufferResource::create(makeShared(pool_->resource->client(), 1, id)); + + listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) { + listeners.bufferResourceDestroy.reset(); + PROTO::shm->destroyResource(this); + }); + + success = texture->m_iTexID; + + if (!success) + Debug::log(ERR, "Failed creating a shm texture: null texture id"); +} + +CWLSHMBuffer::~CWLSHMBuffer() { + ; +} + +eBufferCapability CWLSHMBuffer::caps() { + return BUFFER_CAPABILITY_DATAPTR; +} + +eBufferType CWLSHMBuffer::type() { + return BUFFER_TYPE_SHM; +} + +bool CWLSHMBuffer::isSynchronous() { + return true; +} + +SSHMAttrs CWLSHMBuffer::shm() { + SSHMAttrs attrs; + attrs.success = true; + attrs.fd = pool->fd; + attrs.format = FormatUtils::shmToDRM(fmt); + attrs.size = size; + attrs.stride = stride; + attrs.offset = offset; + return attrs; +} + +std::tuple CWLSHMBuffer::beginDataPtr(uint32_t flags) { + return {(uint8_t*)pool->data + offset, fmt, size.x * size.y * 4}; +} + +void CWLSHMBuffer::endDataPtr() { + ; +} + +bool CWLSHMBuffer::good() { + return success; +} + +void CWLSHMBuffer::update(const CRegion& damage) { + texture->update(FormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, damage); +} + +CSHMPool::CSHMPool(int fd_, size_t size_) : fd(fd_), size(size_) { + data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); +} + +CSHMPool::~CSHMPool() { + munmap(data, size); + close(fd); +} + +void CSHMPool::resize(size_t size_) { + LOGM(LOG, "Resizing a SHM pool from {} to {}", size, size_); + + if (data) + munmap(data, size); + size = size_; + data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if (data == MAP_FAILED) + LOGM(ERR, "Couldn't mmap {} bytes from fd {} of shm client", size, fd); +} + +CWLSHMPoolResource::CWLSHMPoolResource(SP resource_, int fd_, size_t size_) : resource(resource_) { + if (!good()) + return; + + pool = makeShared(fd_, size_); + + resource->setDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); }); + resource->setOnDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); }); + + resource->setResize([this](CWlShmPool* r, int32_t size_) { + if (size_ < (int32_t)pool->size) { + r->error(-1, "Shrinking a shm pool is illegal"); + return; + } + pool->resize(size_); + }); + + resource->setCreateBuffer([this](CWlShmPool* r, uint32_t id, int32_t offset, int32_t w, int32_t h, int32_t stride, uint32_t fmt) { + if (!pool || !pool->data) { + r->error(-1, "The provided shm pool failed to allocate properly"); + return; + } + + if (std::find(PROTO::shm->shmFormats.begin(), PROTO::shm->shmFormats.end(), fmt) == PROTO::shm->shmFormats.end()) { + r->error(WL_SHM_ERROR_INVALID_FORMAT, "Format invalid"); + return; + } + + if (offset < 0 || w <= 0 || h <= 0 || stride <= 0) { + r->error(WL_SHM_ERROR_INVALID_STRIDE, "Invalid stride, w, h, or offset"); + return; + } + + const auto RESOURCE = PROTO::shm->m_vBuffers.emplace_back(makeShared(self.lock(), id, offset, Vector2D{w, h}, stride, fmt)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::shm->m_vBuffers.pop_back(); + return; + } + + // append instance so that buffer knows its owner + RESOURCE->resource->buffer = RESOURCE; + }); + + if (pool->data == MAP_FAILED) + resource->error(WL_SHM_ERROR_INVALID_FD, "Couldn't mmap from fd"); +} + +bool CWLSHMPoolResource::good() { + return resource->resource(); +} + +CWLSHMResource::CWLSHMResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlShm* r) { PROTO::shm->destroyResource(this); }); + + resource->setCreatePool([](CWlShm* r, uint32_t id, int32_t fd, int32_t size) { + const auto RESOURCE = PROTO::shm->m_vPools.emplace_back(makeShared(makeShared(r->client(), r->version(), id), fd, size)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::shm->m_vPools.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + }); + + // send a few supported formats. No need for any other I think? + for (auto& s : PROTO::shm->shmFormats) { + resource->sendFormat((wl_shm_format)s); + } +} + +bool CWLSHMResource::good() { + return resource->resource(); +} + +CWLSHMProtocol::CWLSHMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CWLSHMProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + if (shmFormats.empty()) { + size_t len = 0; + const uint32_t* formats = wlr_renderer_get_shm_texture_formats(g_pCompositor->m_sWLRRenderer, &len); + + for (size_t i = 0; i < len; ++i) { + shmFormats.push_back(FormatUtils::drmToShm(formats[i])); + } + } + + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CWLSHMProtocol::destroyResource(CWLSHMResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLSHMProtocol::destroyResource(CWLSHMPoolResource* resource) { + std::erase_if(m_vPools, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLSHMProtocol::destroyResource(CWLSHMBuffer* resource) { + std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/core/Shm.hpp b/src/protocols/core/Shm.hpp new file mode 100644 index 00000000..687e2031 --- /dev/null +++ b/src/protocols/core/Shm.hpp @@ -0,0 +1,112 @@ +#pragma once + +/* + Implementations for: + - wl_shm + - wl_shm_pool + - wl_buffer with shm +*/ + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include "wayland.hpp" +#include "../types/Buffer.hpp" +#include "../../helpers/Vector2D.hpp" + +class CWLSHMPoolResource; + +class CSHMPool { + public: + CSHMPool(int fd, size_t size); + ~CSHMPool(); + + int fd = 0; + size_t size = 0; + void* data = nullptr; + + void resize(size_t size); +}; + +class CWLSHMBuffer : public IWLBuffer { + public: + CWLSHMBuffer(SP pool, uint32_t id, int32_t offset, const Vector2D& size, int32_t stride, uint32_t fmt); + virtual ~CWLSHMBuffer(); + + virtual eBufferCapability caps(); + virtual eBufferType type(); + virtual void update(const CRegion& damage); + virtual bool isSynchronous(); + virtual SSHMAttrs shm(); + virtual std::tuple beginDataPtr(uint32_t flags); + virtual void endDataPtr(); + + bool good(); + void updateTexture(); + + int32_t offset = 0, stride = 0; + uint32_t fmt = 0; + SP pool; + + private: + bool success = false; + + struct { + CHyprSignalListener bufferResourceDestroy; + } listeners; +}; + +class CWLSHMPoolResource { + public: + CWLSHMPoolResource(SP resource_, int fd, size_t size); + + bool good(); + + SP pool; + + WP self; + + private: + SP resource; + + friend class CWLSHMBuffer; +}; + +class CWLSHMResource { + public: + CWLSHMResource(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CWLSHMProtocol : public IWaylandProtocol { + public: + CWLSHMProtocol(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(CWLSHMResource* resource); + void destroyResource(CWLSHMPoolResource* resource); + void destroyResource(CWLSHMBuffer* resource); + + // + std::vector> m_vManagers; + std::vector> m_vPools; + std::vector> m_vBuffers; + + // + std::vector shmFormats; + + friend class CWLSHMResource; + friend class CWLSHMPoolResource; + friend class CWLSHMBuffer; +}; + +namespace PROTO { + inline UP shm; +}; diff --git a/src/protocols/core/Subcompositor.cpp b/src/protocols/core/Subcompositor.cpp new file mode 100644 index 00000000..d407636c --- /dev/null +++ b/src/protocols/core/Subcompositor.cpp @@ -0,0 +1,197 @@ +#include "Subcompositor.hpp" +#include "Compositor.hpp" +#include + +#define LOGM PROTO::subcompositor->protoLog + +CWLSubsurfaceResource::CWLSubsurfaceResource(SP resource_, SP surface_, SP parent_) : + surface(surface_), parent(parent_), resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlSubsurface* r) { destroy(); }); + resource->setDestroy([this](CWlSubsurface* r) { destroy(); }); + + resource->setSetPosition([this](CWlSubsurface* r, int32_t x, int32_t y) { position = {x, y}; }); + + resource->setSetDesync([this](CWlSubsurface* r) { sync = false; }); + resource->setSetSync([this](CWlSubsurface* r) { sync = true; }); + + resource->setPlaceAbove([this](CWlSubsurface* r, wl_resource* surf) { + auto SURF = CWLSurfaceResource::fromResource(surf); + + if (!parent) + return; + + std::erase(parent->subsurfaces, self.lock()); + + auto it = std::find(parent->subsurfaces.begin(), parent->subsurfaces.end(), SURF); + + if (it == parent->subsurfaces.end()) { + LOGM(ERR, "Invalid surface reference in placeAbove"); + parent->subsurfaces.emplace_back(self.lock()); + } else + parent->subsurfaces.insert(it, self.lock()); + }); + + resource->setPlaceBelow([this](CWlSubsurface* r, wl_resource* surf) { + auto SURF = CWLSurfaceResource::fromResource(surf); + + if (!parent) + return; + + std::erase(parent->subsurfaces, self.lock()); + + auto it = std::find(parent->subsurfaces.begin(), parent->subsurfaces.end(), SURF); + + if (it == parent->subsurfaces.end()) { + LOGM(ERR, "Invalid surface reference in placeBelow"); + parent->subsurfaces.emplace_back(self.lock()); + } else + parent->subsurfaces.insert(it--, self.lock()); + }); + + listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { + if (surface->current.buffer && !surface->mapped) { + surface->map(); + return; + } + + if (!surface->current.buffer && surface->mapped) { + surface->unmap(); + return; + } + }); +} + +CWLSubsurfaceResource::~CWLSubsurfaceResource() { + events.destroy.emit(); + if (surface) + surface->resetRole(); +} + +void CWLSubsurfaceResource::destroy() { + if (surface && surface->mapped) + surface->unmap(); + events.destroy.emit(); + PROTO::subcompositor->destroyResource(this); +} + +Vector2D CWLSubsurfaceResource::posRelativeToParent() { + Vector2D pos = position; + SP surf = parent.lock(); + + // some apps might create cycles, which I believe _technically_ are not a protocol error + // in some cases, notably firefox likes to do that, so we keep track of what + // surfaces we've visited and if we hit a surface we've visited we bail out. + std::vector> surfacesVisited; + + while (surf->role->role() == SURFACE_ROLE_SUBSURFACE && + std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { + surfacesVisited.emplace_back(surf); + auto subsurface = (CWLSubsurfaceResource*)parent->role.get(); + pos += subsurface->position; + surf = subsurface->parent.lock(); + } + return pos; +} + +bool CWLSubsurfaceResource::good() { + return resource->resource(); +} + +eSurfaceRole CWLSubsurfaceResource::role() { + return SURFACE_ROLE_SUBSURFACE; +} + +SP CWLSubsurfaceResource::t1Parent() { + SP surf = parent.lock(); + std::vector> surfacesVisited; + + while (surf->role->role() == SURFACE_ROLE_SUBSURFACE && + std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) { + surfacesVisited.emplace_back(surf); + auto subsurface = (CWLSubsurfaceResource*)parent->role.get(); + surf = subsurface->parent.lock(); + } + return surf; +} + +CWLSubcompositorResource::CWLSubcompositorResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); + resource->setDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); }); + + resource->setGetSubsurface([](CWlSubcompositor* r, uint32_t id, wl_resource* surface, wl_resource* parent) { + auto SURF = CWLSurfaceResource::fromResource(surface); + auto PARENT = CWLSurfaceResource::fromResource(parent); + + if (!SURF || !PARENT || SURF == PARENT) { + r->error(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Invalid surface/parent"); + return; + } + + if (SURF->role->role() != SURFACE_ROLE_UNASSIGNED) { + r->error(-1, "Surface already has a different role"); + return; + } + + SP t1Parent = nullptr; + + if (PARENT->role->role() == SURFACE_ROLE_SUBSURFACE) { + auto subsurface = (CWLSubsurfaceResource*)PARENT->role.get(); + t1Parent = subsurface->t1Parent(); + } else + t1Parent = PARENT; + + if (t1Parent == SURF) { + r->error(WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, "Bad parent, t1 parent == surf"); + return; + } + + const auto RESOURCE = + PROTO::subcompositor->m_vSurfaces.emplace_back(makeShared(makeShared(r->client(), r->version(), id), SURF, PARENT)); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::subcompositor->m_vSurfaces.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + SURF->role = RESOURCE; + PARENT->subsurfaces.emplace_back(RESOURCE); + + LOGM(LOG, "New wl_subsurface with id {} at {:x}", id, (uintptr_t)RESOURCE.get()); + + PARENT->events.newSubsurface.emit(RESOURCE); + }); +} + +bool CWLSubcompositorResource::good() { + return resource->resource(); +} + +CWLSubcompositorProtocol::CWLSubcompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CWLSubcompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared(makeShared(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } +} + +void CWLSubcompositorProtocol::destroyResource(CWLSubcompositorResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CWLSubcompositorProtocol::destroyResource(CWLSubsurfaceResource* resource) { + std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; }); +} diff --git a/src/protocols/core/Subcompositor.hpp b/src/protocols/core/Subcompositor.hpp new file mode 100644 index 00000000..abcfbf6d --- /dev/null +++ b/src/protocols/core/Subcompositor.hpp @@ -0,0 +1,82 @@ + +#pragma once + +/* + Implementations for: + - wl_subsurface + - wl_subcompositor +*/ + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include "wayland.hpp" +#include "../../helpers/signal/Signal.hpp" +#include "../types/SurfaceRole.hpp" + +class CWLSurfaceResource; + +class CWLSubsurfaceResource : public ISurfaceRole { + public: + CWLSubsurfaceResource(SP resource_, SP surface_, SP parent_); + ~CWLSubsurfaceResource(); + + Vector2D posRelativeToParent(); + bool good(); + virtual eSurfaceRole role(); + SP t1Parent(); + + bool sync = false; + Vector2D position; + + WP surface; + WP parent; + + WP self; + + struct { + CSignal destroy; + } events; + + private: + SP resource; + + void destroy(); + + struct { + CHyprSignalListener commitSurface; + } listeners; +}; + +class CWLSubcompositorResource { + public: + CWLSubcompositorResource(SP resource_); + + bool good(); + + private: + SP resource; +}; + +class CWLSubcompositorProtocol : public IWaylandProtocol { + public: + CWLSubcompositorProtocol(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(CWLSubcompositorResource* resource); + void destroyResource(CWLSubsurfaceResource* resource); + + // + std::vector> m_vManagers; + std::vector> m_vSurfaces; + + friend class CWLSubcompositorResource; + friend class CWLSubsurfaceResource; +}; + +namespace PROTO { + inline UP subcompositor; +}; diff --git a/src/protocols/types/Buffer.cpp b/src/protocols/types/Buffer.cpp new file mode 100644 index 00000000..5ed942e1 --- /dev/null +++ b/src/protocols/types/Buffer.cpp @@ -0,0 +1,41 @@ +#include "Buffer.hpp" +#include "WLBuffer.hpp" + +SDMABUFAttrs IWLBuffer::dmabuf() { + return SDMABUFAttrs{}; +} + +SSHMAttrs IWLBuffer::shm() { + return SSHMAttrs{}; +} + +std::tuple IWLBuffer::beginDataPtr(uint32_t flags) { + return {nullptr, 0, 0}; +} + +void IWLBuffer::endDataPtr() { + ; // empty +} + +void IWLBuffer::sendRelease() { + if (!resource || !resource->resource) + return; + resource->resource->sendRelease(); +} + +void IWLBuffer::lock() { + locks++; +} + +void IWLBuffer::unlock() { + locks--; + + ASSERT(locks >= 0); + + if (locks <= 0) + sendRelease(); +} + +bool IWLBuffer::locked() { + return locks; +} diff --git a/src/protocols/types/Buffer.hpp b/src/protocols/types/Buffer.hpp new file mode 100644 index 00000000..9999a4e9 --- /dev/null +++ b/src/protocols/types/Buffer.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "../../defines.hpp" +#include "../../helpers/signal/Signal.hpp" +#include "../../render/Texture.hpp" + +#include +#include + +enum eBufferCapability { + BUFFER_CAPABILITY_DATAPTR = (1 << 0), +}; + +enum eBufferType { + BUFFER_TYPE_DMABUF = 0, + BUFFER_TYPE_SHM, + BUFFER_TYPE_MISC, +}; + +class CWLBufferResource; + +struct SDMABUFAttrs { + bool success = false; + Vector2D size; + uint32_t format = 0; // fourcc + uint64_t modifier = 0; + + int planes = 1; + std::array offsets = {0}; + std::array strides = {0}; + std::array fds = {-1, -1, -1, -1}; +}; + +struct SSHMAttrs { + bool success = false; + int fd = 0; + uint32_t format = 0; + Vector2D size; + int stride = 0; + int64_t offset = 0; +}; + +class IWLBuffer { + public: + virtual ~IWLBuffer() { + ; + }; + + virtual eBufferCapability caps() = 0; + virtual eBufferType type() = 0; + virtual void update(const CRegion& damage) = 0; + virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu + virtual SDMABUFAttrs dmabuf(); + virtual SSHMAttrs shm(); + virtual std::tuple beginDataPtr(uint32_t flags); + virtual void endDataPtr(); + virtual void sendRelease(); + virtual void lock(); + virtual void unlock(); + virtual bool locked(); + + Vector2D size; + bool opaque = false; + + SP resource; + + SP texture; + + struct { + CSignal destroy; + } events; + + private: + int locks = 0; +}; diff --git a/src/protocols/types/DMABuffer.cpp b/src/protocols/types/DMABuffer.cpp new file mode 100644 index 00000000..f26328e9 --- /dev/null +++ b/src/protocols/types/DMABuffer.cpp @@ -0,0 +1,79 @@ +#include "DMABuffer.hpp" +#include "WLBuffer.hpp" +#include "../../render/Renderer.hpp" +#include "../../helpers/Format.hpp" + +CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs_) : attrs(attrs_) { + g_pHyprRenderer->makeEGLCurrent(); + + listeners.resourceDestroy = events.destroy.registerListener([this](std::any d) { + closeFDs(); + listeners.resourceDestroy.reset(); + }); + + size = attrs.size; + resource = CWLBufferResource::create(makeShared(client, 1, id)); + + auto eglImage = g_pHyprOpenGL->createEGLImage(attrs); + + if (!eglImage) + return; + + texture = makeShared(attrs, eglImage); // texture takes ownership of the eglImage + opaque = FormatUtils::isFormatOpaque(attrs.format); + success = texture->m_iTexID; + + if (!success) + Debug::log(ERR, "Failed to create a dmabuf: texture is null"); +} + +CDMABuffer::~CDMABuffer() { + closeFDs(); +} + +eBufferCapability CDMABuffer::caps() { + return BUFFER_CAPABILITY_DATAPTR; +} + +eBufferType CDMABuffer::type() { + return BUFFER_TYPE_DMABUF; +} + +void CDMABuffer::update(const CRegion& damage) { + ; +} + +bool CDMABuffer::isSynchronous() { + return false; +} + +SDMABUFAttrs CDMABuffer::dmabuf() { + return attrs; +} + +std::tuple CDMABuffer::beginDataPtr(uint32_t flags) { + // FIXME: + return {nullptr, 0, 0}; +} + +void CDMABuffer::endDataPtr() { + // FIXME: +} + +bool CDMABuffer::good() { + return success; +} + +void CDMABuffer::updateTexture() { + ; +} + +void CDMABuffer::closeFDs() { + for (int i = 0; i < attrs.planes; ++i) { + if (attrs.fds[i] == -1) + continue; + close(attrs.fds[i]); + attrs.fds[i] = -1; + } + attrs.planes = 0; +} \ No newline at end of file diff --git a/src/protocols/types/DMABuffer.hpp b/src/protocols/types/DMABuffer.hpp new file mode 100644 index 00000000..dac89493 --- /dev/null +++ b/src/protocols/types/DMABuffer.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Buffer.hpp" + +class CDMABuffer : public IWLBuffer { + public: + CDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs_); + virtual ~CDMABuffer(); + + virtual eBufferCapability caps(); + virtual eBufferType type(); + virtual bool isSynchronous(); + virtual void update(const CRegion& damage); + virtual SDMABUFAttrs dmabuf(); + virtual std::tuple beginDataPtr(uint32_t flags); + virtual void endDataPtr(); + bool good(); + void updateTexture(); + void closeFDs(); + + bool success = false; + + private: + SDMABUFAttrs attrs; + + struct { + CHyprSignalListener resourceDestroy; + } listeners; +}; \ No newline at end of file diff --git a/src/protocols/types/DataDevice.cpp b/src/protocols/types/DataDevice.cpp index 47cbda8b..eb6969cc 100644 --- a/src/protocols/types/DataDevice.cpp +++ b/src/protocols/types/DataDevice.cpp @@ -15,3 +15,7 @@ bool IDataSource::used() { void IDataSource::markUsed() { wasUsed = true; } + +eDataSourceType IDataSource::type() { + return DATA_SOURCE_TYPE_WAYLAND; +} diff --git a/src/protocols/types/DataDevice.hpp b/src/protocols/types/DataDevice.hpp index 98a97c14..948f47a0 100644 --- a/src/protocols/types/DataDevice.hpp +++ b/src/protocols/types/DataDevice.hpp @@ -5,6 +5,11 @@ #include #include "../../helpers/signal/Signal.hpp" +enum eDataSourceType { + DATA_SOURCE_TYPE_WAYLAND = 0, + DATA_SOURCE_TYPE_X11, +}; + class IDataSource { public: IDataSource() {} @@ -19,6 +24,7 @@ class IDataSource { virtual bool used(); virtual void markUsed(); virtual void error(uint32_t code, const std::string& msg) = 0; + virtual eDataSourceType type(); struct { CSignal destroy; diff --git a/src/protocols/types/SurfaceRole.hpp b/src/protocols/types/SurfaceRole.hpp new file mode 100644 index 00000000..05c0ea66 --- /dev/null +++ b/src/protocols/types/SurfaceRole.hpp @@ -0,0 +1,14 @@ +#pragma once + +enum eSurfaceRole { + SURFACE_ROLE_UNASSIGNED = 0, + SURFACE_ROLE_XDG_SHELL, + SURFACE_ROLE_LAYER_SHELL, + SURFACE_ROLE_EASTER_EGG, + SURFACE_ROLE_SUBSURFACE, +}; + +class ISurfaceRole { + public: + virtual eSurfaceRole role() = 0; +}; diff --git a/src/protocols/types/WLBuffer.cpp b/src/protocols/types/WLBuffer.cpp new file mode 100644 index 00000000..e53538cb --- /dev/null +++ b/src/protocols/types/WLBuffer.cpp @@ -0,0 +1,43 @@ +#include "WLBuffer.hpp" +#include "Buffer.hpp" + +CWLBufferResource::CWLBufferResource(SP resource_) : resource(resource_) { + if (!good()) + return; + + resource->setOnDestroy([this](CWlBuffer* r) { + if (buffer.expired()) + return; + buffer->events.destroy.emit(); + }); + resource->setDestroy([this](CWlBuffer* r) { + if (buffer.expired()) + return; + buffer->events.destroy.emit(); + }); + + resource->setData(this); +} + +bool CWLBufferResource::good() { + return resource->resource(); +} + +void CWLBufferResource::sendRelease() { + resource->sendRelease(); +} + +wl_resource* CWLBufferResource::getResource() { + return resource->resource(); +} + +SP CWLBufferResource::fromResource(wl_resource* res) { + auto data = (CWLBufferResource*)(((CWlBuffer*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +SP CWLBufferResource::create(SP resource) { + auto p = SP(new CWLBufferResource(resource)); + p->self = p; + return p; +} diff --git a/src/protocols/types/WLBuffer.hpp b/src/protocols/types/WLBuffer.hpp new file mode 100644 index 00000000..ac177965 --- /dev/null +++ b/src/protocols/types/WLBuffer.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include "../WaylandProtocol.hpp" +#include "wayland.hpp" +#include "../../helpers/signal/Signal.hpp" + +class IWLBuffer; + +class CWLBufferResource { + public: + static SP create(SP resource); + static SP fromResource(wl_resource* res); + + bool good(); + void sendRelease(); + wl_resource* getResource(); + + WP buffer; + + WP self; + + private: + CWLBufferResource(SP resource_); + + SP resource; + + friend class IWLBuffer; +}; diff --git a/src/render/Framebuffer.cpp b/src/render/Framebuffer.cpp index 83325dd7..67629e23 100644 --- a/src/render/Framebuffer.cpp +++ b/src/render/Framebuffer.cpp @@ -1,22 +1,26 @@ #include "Framebuffer.hpp" #include "OpenGL.hpp" +CFramebuffer::CFramebuffer() { + m_cTex = makeShared(); +} + bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { bool firstAlloc = false; RASSERT((w > 1 && h > 1), "cannot alloc a FB with negative / zero size! (attempted {}x{})", w, h); - uint32_t glFormat = drmFormatToGL(drmFormat); - uint32_t glType = glFormatToType(glFormat); + uint32_t glFormat = FormatUtils::drmFormatToGL(drmFormat); + uint32_t glType = FormatUtils::glFormatToType(glFormat); if (m_iFb == (uint32_t)-1) { firstAlloc = true; glGenFramebuffers(1, &m_iFb); } - if (m_cTex.m_iTexID == 0) { + if (m_cTex->m_iTexID == 0) { firstAlloc = true; - glGenTextures(1, &m_cTex.m_iTexID); - glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); + m_cTex->allocate(); + glBindTexture(GL_TEXTURE_2D, m_cTex->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -24,11 +28,11 @@ bool CFramebuffer::alloc(int w, int h, uint32_t drmFormat) { } if (firstAlloc || m_vSize != Vector2D(w, h)) { - glBindTexture(GL_TEXTURE_2D, m_cTex.m_iTexID); + glBindTexture(GL_TEXTURE_2D, m_cTex->m_iTexID); glTexImage2D(GL_TEXTURE_2D, 0, glFormat, w, h, 0, GL_RGBA, glType, 0); glBindFramebuffer(GL_FRAMEBUFFER, m_iFb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_cTex.m_iTexID, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_cTex->m_iTexID, 0); // TODO: Allow this with gles2 #ifndef GLES2 @@ -87,12 +91,9 @@ void CFramebuffer::release() { if (m_iFb != (uint32_t)-1 && m_iFb) glDeleteFramebuffers(1, &m_iFb); - if (m_cTex.m_iTexID) - glDeleteTextures(1, &m_cTex.m_iTexID); - - m_cTex.m_iTexID = 0; - m_iFb = -1; - m_vSize = Vector2D(); + m_cTex->destroyTexture(); + m_iFb = -1; + m_vSize = Vector2D(); } CFramebuffer::~CFramebuffer() { diff --git a/src/render/Framebuffer.hpp b/src/render/Framebuffer.hpp index 22809158..a46a4859 100644 --- a/src/render/Framebuffer.hpp +++ b/src/render/Framebuffer.hpp @@ -5,19 +5,20 @@ class CFramebuffer { public: + CFramebuffer(); ~CFramebuffer(); - bool alloc(int w, int h, uint32_t format = GL_RGBA); - void addStencil(); - void bind(); - void release(); - void reset(); - bool isAllocated(); + bool alloc(int w, int h, uint32_t format = GL_RGBA); + void addStencil(); + void bind(); + void release(); + void reset(); + bool isAllocated(); - Vector2D m_vSize; + Vector2D m_vSize; - CTexture m_cTex; - GLuint m_iFb = -1; + SP m_cTex; + GLuint m_iFb = -1; - CTexture* m_pStencilTex = nullptr; + SP m_pStencilTex; }; \ No newline at end of file diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index a1e6f73e..406ffdd0 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -7,6 +7,8 @@ #include "../config/ConfigValue.hpp" #include "../desktop/LayerSurface.hpp" #include "../protocols/LayerShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include inline void loadGLProc(void* pProc, const char* name) { void* proc = (void*)eglGetProcAddress(name); @@ -21,7 +23,8 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, wlr_egl_get_context(g_pCompositor->m_sWLREGL)), "Couldn't unset current EGL!"); - auto* const EXTENSIONS = (const char*)glGetString(GL_EXTENSIONS); + auto* const EXTENSIONS = (const char*)glGetString(GL_EXTENSIONS); + const std::string EGLEXTENSIONS = (const char*)eglQueryString(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_EXTENSIONS); RASSERT(EXTENSIONS, "Couldn't retrieve openGL extensions!"); @@ -33,12 +36,25 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { Debug::log(LOG, "Using: {}", (char*)glGetString(GL_VERSION)); Debug::log(LOG, "Vendor: {}", (char*)glGetString(GL_VENDOR)); Debug::log(LOG, "Renderer: {}", (char*)glGetString(GL_RENDERER)); - Debug::log(LOG, "Supported extensions size: {}", std::count(m_szExtensions.begin(), m_szExtensions.end(), ' ')); + Debug::log(LOG, "Supported extensions: ({}) {}", std::count(m_szExtensions.begin(), m_szExtensions.end(), ' '), m_szExtensions); loadGLProc(&m_sProc.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); + loadGLProc(&m_sProc.eglCreateImageKHR, "eglCreateImageKHR"); loadGLProc(&m_sProc.eglDestroyImageKHR, "eglDestroyImageKHR"); + loadGLProc(&m_sProc.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT"); + loadGLProc(&m_sProc.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT"); + loadGLProc(&m_sProc.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); - m_sExts.EXT_read_format_bgra = m_szExtensions.contains("GL_EXT_read_format_bgra"); + m_sExts.EXT_read_format_bgra = m_szExtensions.contains("GL_EXT_read_format_bgra"); + m_sExts.EXT_image_dma_buf_import = EGLEXTENSIONS.contains("EXT_image_dma_buf_import"); + m_sExts.EXT_image_dma_buf_import_modifiers = EGLEXTENSIONS.contains("EXT_image_dma_buf_import_modifiers"); + + RASSERT(m_szExtensions.contains("GL_EXT_texture_format_BGRA8888"), "GL_EXT_texture_format_BGRA8888 support by the GPU driver is required"); + + if (!m_sExts.EXT_read_format_bgra) + Debug::log(WARN, "Your GPU does not support GL_EXT_read_format_bgra, this may cause issues with texture importing"); + if (!m_sExts.EXT_image_dma_buf_import || !m_sExts.EXT_image_dma_buf_import_modifiers) + Debug::log(WARN, "Your GPU does not support DMABUFs, this will possibly cause issues and will take a hit on the performance."); #ifdef USE_TRACY_GPU @@ -54,6 +70,8 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { Debug::log(WARN, "!RENDERER: Using the legacy GLES2 renderer!"); #endif + initDRMFormats(); + static auto P = g_pHookSystem->hookDynamic("preRender", [&](void* self, SCallbackInfo& info, std::any data) { preRender(std::any_cast(data)); }); RASSERT(eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), "Couldn't unset current EGL!"); @@ -61,6 +79,175 @@ CHyprOpenGLImpl::CHyprOpenGLImpl() { m_tGlobalTimer.reset(); } +std::vector CHyprOpenGLImpl::getModsForFormat(EGLint format) { + // TODO: return std::expected when clang supports it + + if (!m_sExts.EXT_image_dma_buf_import_modifiers) + return {}; + + EGLint len = 0; + if (!m_sProc.eglQueryDmaBufModifiersEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), format, 0, nullptr, nullptr, &len)) { + Debug::log(ERR, "EGL: Failed to query mods"); + return {}; + } + + if (len <= 0) + return {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID}; // assume the driver can do linear and implicit. + + std::vector mods; + std::vector external; + + mods.resize(len); + external.resize(len); + + m_sProc.eglQueryDmaBufModifiersEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), format, len, mods.data(), external.data(), &len); + + std::vector result; + for (size_t i = 0; i < mods.size(); ++i) { + if (external.at(i)) + continue; + + result.push_back(mods.at(i)); + } + + return result; +} + +void CHyprOpenGLImpl::initDRMFormats() { + const auto DISABLE_MODS = envEnabled("HYPRLAND_EGL_NO_MODIFIERS"); + if (DISABLE_MODS) + Debug::log(WARN, "HYPRLAND_EGL_NO_MODIFIERS set, disabling modifiers"); + + if (!m_sExts.EXT_image_dma_buf_import) { + Debug::log(ERR, "EGL: No dmabuf import, DMABufs will not work."); + return; + } + + std::vector formats; + + if (!m_sExts.EXT_image_dma_buf_import_modifiers || !m_sProc.eglQueryDmaBufFormatsEXT) { + formats.push_back(DRM_FORMAT_ARGB8888); + formats.push_back(DRM_FORMAT_XRGB8888); + Debug::log(WARN, "EGL: No mod support"); + } else { + EGLint len = 0; + m_sProc.eglQueryDmaBufFormatsEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), 0, nullptr, &len); + formats.resize(len); + m_sProc.eglQueryDmaBufFormatsEXT(wlr_egl_get_display(g_pCompositor->m_sWLREGL), len, formats.data(), &len); + } + + if (formats.size() == 0) { + Debug::log(ERR, "EGL: Failed to get formats, DMABufs will not work."); + return; + } + + wlr_log(WLR_DEBUG, "Supported DMA-BUF formats:"); + + std::vector dmaFormats; + + for (auto& fmt : formats) { + std::vector mods; + if (!DISABLE_MODS) + mods = getModsForFormat(fmt); + else + mods = {DRM_FORMAT_MOD_LINEAR}; + + m_bHasModifiers = m_bHasModifiers || mods.size() > 0; + + if (mods.size() == 0) + continue; + + dmaFormats.push_back(SDRMFormat{ + .format = fmt, + .mods = mods, + }); + + std::vector> modifierData; + + auto fmtName = drmGetFormatName(fmt); + Debug::log(LOG, "EGL: GPU Supports Format {} (0x{:x})", fmtName ? fmtName : "?unknown?", fmt); + for (auto& mod : mods) { + auto modName = drmGetFormatModifierName(mod); + modifierData.emplace_back(std::make_pair<>(mod, modName ? modName : "?unknown?")); + free(modName); + } + free(fmtName); + + mods.clear(); + std::sort(modifierData.begin(), modifierData.end(), [](const auto& a, const auto& b) { + if (a.first == 0) + return false; + if (a.second.contains("DCC")) + return false; + return true; + }); + + for (auto& [m, name] : modifierData) { + Debug::log(LOG, "EGL: | with modifier {} (0x{:x})", name, m); + mods.emplace_back(m); + } + } + + Debug::log(LOG, "EGL: {} formats found in total. Some modifiers may be omitted as they are external-only.", dmaFormats.size()); + + if (dmaFormats.size() == 0) + Debug::log(WARN, + "EGL: WARNING: No dmabuf formats were found, dmabuf will be disabled. This will degrade performance, but is most likely a driver issue or a very old GPU."); + + drmFormats = dmaFormats; +} + +EGLImageKHR CHyprOpenGLImpl::createEGLImage(const SDMABUFAttrs& attrs) { + std::vector attribs; + + attribs.push_back(EGL_WIDTH); + attribs.push_back(attrs.size.x); + attribs.push_back(EGL_HEIGHT); + attribs.push_back(attrs.size.y); + attribs.push_back(EGL_LINUX_DRM_FOURCC_EXT); + attribs.push_back(attrs.format); + + struct { + EGLint fd; + EGLint offset; + EGLint pitch; + EGLint modlo; + EGLint modhi; + } attrNames[4] = { + {EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT}, + {EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT}, + {EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT}, + {EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}}; + + for (int i = 0; i < attrs.planes; i++) { + attribs.push_back(attrNames[i].fd); + attribs.push_back(attrs.fds[i]); + attribs.push_back(attrNames[i].offset); + attribs.push_back(attrs.offsets[i]); + attribs.push_back(attrNames[i].pitch); + attribs.push_back(attrs.strides[i]); + if (m_bHasModifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) { + attribs.push_back(attrNames[i].modlo); + attribs.push_back(attrs.modifier & 0xFFFFFFFF); + attribs.push_back(attrNames[i].modhi); + attribs.push_back(attrs.modifier >> 32); + } + } + + attribs.push_back(EGL_IMAGE_PRESERVED_KHR); + attribs.push_back(EGL_TRUE); + + attribs.push_back(EGL_NONE); + + EGLImageKHR image = m_sProc.eglCreateImageKHR(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data()); + if (image == EGL_NO_IMAGE_KHR) { + Debug::log(ERR, "EGL: EGLCreateImageKHR failed: {}", eglGetError()); + return EGL_NO_IMAGE_KHR; + } + + return image; +} + void CHyprOpenGLImpl::logShaderError(const GLuint& shader, bool program) { GLint maxLength = 0; if (program) @@ -339,12 +526,12 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, const CRegion& damage_, CFramebu // ensure a framebuffer for the monitor exists if (m_RenderData.pCurrentMonData->offloadFB.m_vSize != pMonitor->vecPixelSize) { - m_RenderData.pCurrentMonData->stencilTex.allocate(); + m_RenderData.pCurrentMonData->stencilTex->allocate(); - m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; - m_RenderData.pCurrentMonData->mirrorFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; - m_RenderData.pCurrentMonData->mirrorSwapFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; - m_RenderData.pCurrentMonData->offMainFB.m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; + m_RenderData.pCurrentMonData->offloadFB.m_pStencilTex = m_RenderData.pCurrentMonData->stencilTex; + m_RenderData.pCurrentMonData->mirrorFB.m_pStencilTex = m_RenderData.pCurrentMonData->stencilTex; + m_RenderData.pCurrentMonData->mirrorSwapFB.m_pStencilTex = m_RenderData.pCurrentMonData->stencilTex; + m_RenderData.pCurrentMonData->offMainFB.m_pStencilTex = m_RenderData.pCurrentMonData->stencilTex; m_RenderData.pCurrentMonData->offloadFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); m_RenderData.pCurrentMonData->mirrorFB.alloc(pMonitor->vecPixelSize.x, pMonitor->vecPixelSize.y, pMonitor->drmFormat); @@ -381,8 +568,8 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, const CRegion& damage_, CFramebu // we can render to the rbo / fbo (fake) directly const auto PFBO = fb ? fb : PRBO->getFB(); m_RenderData.currentFB = PFBO; - if (PFBO->m_pStencilTex != &m_RenderData.pCurrentMonData->stencilTex) { - PFBO->m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; + if (PFBO->m_pStencilTex != m_RenderData.pCurrentMonData->stencilTex) { + PFBO->m_pStencilTex = m_RenderData.pCurrentMonData->stencilTex; PFBO->addStencil(); } PFBO->bind(); @@ -863,19 +1050,7 @@ void CHyprOpenGLImpl::renderRectWithDamage(CBox* box, const CColor& col, CRegion scissor((CBox*)nullptr); } -void CHyprOpenGLImpl::renderTexture(wlr_texture* tex, CBox* pBox, float alpha, int round, bool allowCustomUV) { - RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); - - renderTexture(CTexture(tex), pBox, alpha, round, false, allowCustomUV); -} - -void CHyprOpenGLImpl::renderTextureWithDamage(wlr_texture* tex, CBox* pBox, CRegion* damage, float alpha, int round, bool allowCustomUV) { - RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); - - renderTextureWithDamage(CTexture(tex), pBox, damage, alpha, round, false, allowCustomUV); -} - -void CHyprOpenGLImpl::renderTexture(const CTexture& tex, CBox* pBox, float alpha, int round, bool discardActive, bool allowCustomUV) { +void CHyprOpenGLImpl::renderTexture(SP tex, CBox* pBox, float alpha, int round, bool discardActive, bool allowCustomUV) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); renderTextureInternalWithDamage(tex, pBox, alpha, &m_RenderData.damage, round, discardActive, false, allowCustomUV, true); @@ -883,7 +1058,7 @@ void CHyprOpenGLImpl::renderTexture(const CTexture& tex, CBox* pBox, float alpha scissor((CBox*)nullptr); } -void CHyprOpenGLImpl::renderTextureWithDamage(const CTexture& tex, CBox* pBox, CRegion* damage, float alpha, int round, bool discardActive, bool allowCustomUV) { +void CHyprOpenGLImpl::renderTextureWithDamage(SP tex, CBox* pBox, CRegion* damage, float alpha, int round, bool discardActive, bool allowCustomUV) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); renderTextureInternalWithDamage(tex, pBox, alpha, damage, round, discardActive, false, allowCustomUV, true); @@ -891,10 +1066,10 @@ void CHyprOpenGLImpl::renderTextureWithDamage(const CTexture& tex, CBox* pBox, C scissor((CBox*)nullptr); } -void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* pBox, float alpha, CRegion* damage, int round, bool discardActive, bool noAA, bool allowCustomUV, +void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP tex, CBox* pBox, float alpha, CRegion* damage, int round, bool discardActive, bool noAA, bool allowCustomUV, bool allowDim) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex.m_iTexID > 0), "Attempted to draw NULL texture!"); + RASSERT((tex->m_iTexID > 0), "Attempted to draw NULL texture!"); TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); @@ -934,11 +1109,11 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* shader = &m_RenderData.pCurrentMonData->m_shPASSTHRURGBA; usingFinalShader = true; } else { - switch (tex.m_iType) { + switch (tex->m_iType) { case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break; case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break; case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; - default: RASSERT(false, "tex.m_iTarget unsupported!"); + default: RASSERT(false, "tex->m_iTarget unsupported!"); } } } @@ -947,14 +1122,14 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* shader = &m_RenderData.pCurrentMonData->m_shRGBX; glActiveTexture(GL_TEXTURE0); - glBindTexture(tex.m_iTarget, tex.m_iTexID); + glBindTexture(tex->m_iTarget, tex->m_iTexID); if (m_RenderData.useNearestNeighbor) { - glTexParameteri(tex.m_iTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(tex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } else { - glTexParameteri(tex.m_iTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(tex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(tex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } glUseProgram(shader->program); @@ -1057,12 +1232,12 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, CBox* glDisableVertexAttribArray(shader->posAttrib); glDisableVertexAttribArray(shader->texAttrib); - glBindTexture(tex.m_iTarget, 0); + glBindTexture(tex->m_iTarget, 0); } -void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) { +void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, CBox* pBox) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex.m_iTexID > 0), "Attempted to draw NULL texture!"); + RASSERT((tex->m_iTexID > 0), "Attempted to draw NULL texture!"); TRACY_GPU_ZONE("RenderTexturePrimitive"); @@ -1083,7 +1258,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) { CShader* shader = &m_RenderData.pCurrentMonData->m_shPASSTHRURGBA; glActiveTexture(GL_TEXTURE0); - glBindTexture(tex.m_iTarget, tex.m_iTexID); + glBindTexture(tex->m_iTarget, tex->m_iTexID); glUseProgram(shader->program); @@ -1111,12 +1286,12 @@ void CHyprOpenGLImpl::renderTexturePrimitive(const CTexture& tex, CBox* pBox) { glDisableVertexAttribArray(shader->posAttrib); glDisableVertexAttribArray(shader->texAttrib); - glBindTexture(tex.m_iTarget, 0); + glBindTexture(tex->m_iTarget, 0); } -void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFramebuffer& matte) { +void CHyprOpenGLImpl::renderTextureMatte(SP tex, CBox* pBox, CFramebuffer& matte) { RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex.m_iTexID > 0), "Attempted to draw NULL texture!"); + RASSERT((tex->m_iTexID > 0), "Attempted to draw NULL texture!"); TRACY_GPU_ZONE("RenderTextureMatte"); @@ -1148,10 +1323,10 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame glUniform1i(shader->alphaMatte, 1); glActiveTexture(GL_TEXTURE0); - glBindTexture(tex.m_iTarget, tex.m_iTexID); + glBindTexture(tex->m_iTarget, tex->m_iTexID); glActiveTexture(GL_TEXTURE0 + 1); - glBindTexture(matte.m_cTex.m_iTarget, matte.m_cTex.m_iTexID); + glBindTexture(matte.m_cTex->m_iTarget, matte.m_cTex->m_iTexID); glVertexAttribPointer(shader->posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); glVertexAttribPointer(shader->texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts); @@ -1169,7 +1344,7 @@ void CHyprOpenGLImpl::renderTextureMatte(const CTexture& tex, CBox* pBox, CFrame glDisableVertexAttribArray(shader->posAttrib); glDisableVertexAttribArray(shader->texAttrib); - glBindTexture(tex.m_iTarget, 0); + glBindTexture(tex->m_iTarget, 0); } // This probably isn't the fastest @@ -1221,9 +1396,9 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o glActiveTexture(GL_TEXTURE0); - glBindTexture(m_RenderData.currentFB->m_cTex.m_iTarget, m_RenderData.currentFB->m_cTex.m_iTexID); + glBindTexture(m_RenderData.currentFB->m_cTex->m_iTarget, m_RenderData.currentFB->m_cTex->m_iTexID); - glTexParameteri(m_RenderData.currentFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(m_RenderData.currentFB->m_cTex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glUseProgram(m_RenderData.pCurrentMonData->m_shBLURPREPARE.program); @@ -1265,9 +1440,9 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o glActiveTexture(GL_TEXTURE0); - glBindTexture(currentRenderToFB->m_cTex.m_iTarget, currentRenderToFB->m_cTex.m_iTexID); + glBindTexture(currentRenderToFB->m_cTex->m_iTarget, currentRenderToFB->m_cTex->m_iTexID); - glTexParameteri(currentRenderToFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(currentRenderToFB->m_cTex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glUseProgram(pShader->program); @@ -1315,7 +1490,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o // draw the things. // first draw is swap -> mirr PMIRRORFB->bind(); - glBindTexture(PMIRRORSWAPFB->m_cTex.m_iTarget, PMIRRORSWAPFB->m_cTex.m_iTexID); + glBindTexture(PMIRRORSWAPFB->m_cTex->m_iTarget, PMIRRORSWAPFB->m_cTex->m_iTexID); // damage region will be scaled, make a temp CRegion tempDamage{damage}; @@ -1343,9 +1518,9 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o glActiveTexture(GL_TEXTURE0); - glBindTexture(currentRenderToFB->m_cTex.m_iTarget, currentRenderToFB->m_cTex.m_iTexID); + glBindTexture(currentRenderToFB->m_cTex->m_iTarget, currentRenderToFB->m_cTex->m_iTexID); - glTexParameteri(currentRenderToFB->m_cTex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(currentRenderToFB->m_cTex->m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glUseProgram(m_RenderData.pCurrentMonData->m_shBLURFINISH.program); @@ -1383,7 +1558,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, CRegion* o } // finish - glBindTexture(PMIRRORFB->m_cTex.m_iTarget, 0); + glBindTexture(PMIRRORFB->m_cTex->m_iTarget, 0); blend(BLENDBEFORE); @@ -1417,23 +1592,23 @@ void CHyprOpenGLImpl::preRender(CMonitor* pMonitor) { if (pWindow->m_sAdditionalConfigData.forceNoBlur) return false; - if (pWindow->m_pWLSurface.small() && !pWindow->m_pWLSurface.m_bFillIgnoreSmall) + if (pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall) return true; - const auto PSURFACE = pWindow->m_pWLSurface.wlr(); + const auto PSURFACE = pWindow->m_pWLSurface->resource(); const auto PWORKSPACE = pWindow->m_pWorkspace; const float A = pWindow->m_fAlpha.value() * pWindow->m_fActiveInactiveAlpha.value() * PWORKSPACE->m_fAlpha.value(); if (A >= 1.f) { - if (PSURFACE->opaque) - return false; + // if (PSURFACE->opaque) + // return false; CRegion inverseOpaque; - pixman_box32_t surfbox = {0, 0, PSURFACE->current.width, PSURFACE->current.height}; - CRegion opaqueRegion{&PSURFACE->current.opaque}; - inverseOpaque.set(opaqueRegion).invert(&surfbox).intersect(0, 0, PSURFACE->current.width, PSURFACE->current.height); + pixman_box32_t surfbox = {0, 0, PSURFACE->current.size.x, PSURFACE->current.size.y}; + CRegion opaqueRegion{PSURFACE->current.opaque}; + inverseOpaque.set(opaqueRegion).invert(&surfbox).intersect(0, 0, PSURFACE->current.size.x, PSURFACE->current.size.y); if (inverseOpaque.empty()) return false; @@ -1461,8 +1636,8 @@ void CHyprOpenGLImpl::preRender(CMonitor* pMonitor) { if (!ls->layerSurface || ls->xray != 1) continue; - if (ls->layerSurface->surface->opaque && ls->alpha.value() >= 1.f) - continue; + // if (ls->layerSurface->surface->opaque && ls->alpha.value() >= 1.f) + // continue; hasWindows = true; break; @@ -1527,7 +1702,7 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWin static auto PBLURNEWOPTIMIZE = CConfigValue("decoration:blur:new_optimizations"); static auto PBLURXRAY = CConfigValue("decoration:blur:xray"); - if (!m_RenderData.pCurrentMonData->blurFB.m_cTex.m_iTexID) + if (!m_RenderData.pCurrentMonData->blurFB.m_cTex->m_iTexID) return false; if (pWindow && pWindow->m_sAdditionalConfigData.xray.toUnderlying() == 0) @@ -1545,7 +1720,7 @@ bool CHyprOpenGLImpl::shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWin return false; } -void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, float a, wlr_surface* pSurface, int round, bool blockBlurOptimization, float blurA) { +void CHyprOpenGLImpl::renderTextureWithBlur(SP tex, CBox* pBox, float a, SP pSurface, int round, bool blockBlurOptimization, float blurA) { RASSERT(m_RenderData.pMonitor, "Tried to render texture with blur without begin()!"); static auto PBLURENABLED = CConfigValue("decoration:blur:enabled"); @@ -1570,11 +1745,11 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, CBox* pBox, flo // amazing hack: the surface has an opaque region! CRegion inverseOpaque; - if (a >= 1.f && std::round(pSurface->current.width * m_RenderData.pMonitor->scale) == pBox->w && - std::round(pSurface->current.height * m_RenderData.pMonitor->scale) == pBox->h) { - pixman_box32_t surfbox = {0, 0, pSurface->current.width * pSurface->current.scale, pSurface->current.height * pSurface->current.scale}; - inverseOpaque = &pSurface->current.opaque; - inverseOpaque.invert(&surfbox).intersect(0, 0, pSurface->current.width * pSurface->current.scale, pSurface->current.height * pSurface->current.scale); + if (a >= 1.f && std::round(pSurface->current.size.x * m_RenderData.pMonitor->scale) == pBox->w && + std::round(pSurface->current.size.y * m_RenderData.pMonitor->scale) == pBox->h) { + pixman_box32_t surfbox = {0, 0, pSurface->current.size.x * pSurface->current.scale, pSurface->current.size.y * pSurface->current.scale}; + inverseOpaque = pSurface->current.opaque; + inverseOpaque.invert(&surfbox).intersect(0, 0, pSurface->current.size.x * pSurface->current.scale, pSurface->current.size.y * pSurface->current.scale); if (inverseOpaque.empty()) { renderTexture(tex, pBox, a, round, false, true); @@ -1765,7 +1940,7 @@ void CHyprOpenGLImpl::makeRawWindowSnapshot(PHLWINDOW pWindow, CFramebuffer* pFr g_pHyprRenderer->makeEGLCurrent(); - pFramebuffer->m_pStencilTex = &m_RenderData.pCurrentMonData->stencilTex; + pFramebuffer->m_pStencilTex = m_RenderData.pCurrentMonData->stencilTex; pFramebuffer->alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, PMONITOR->drmFormat); @@ -1903,7 +2078,7 @@ void CHyprOpenGLImpl::renderSnapshot(PHLWINDOW pWindow) { const auto FBDATA = &m_mWindowFramebuffers.at(ref); - if (!FBDATA->m_cTex.m_iTexID) + if (!FBDATA->m_cTex->m_iTexID) return; const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); @@ -1942,7 +2117,7 @@ void CHyprOpenGLImpl::renderSnapshot(PHLLS pLayer) { const auto FBDATA = &m_mLayerFramebuffers.at(pLayer); - if (!FBDATA->m_cTex.m_iTexID) + if (!FBDATA->m_cTex->m_iTexID) return; const auto PMONITOR = g_pCompositor->getMonitorFromID(pLayer->monitorID); @@ -2077,7 +2252,7 @@ void CHyprOpenGLImpl::renderMirrored() { monbox.y = (monitor->vecTransformedSize.y - monbox.h) / 2; const auto PFB = &m_mMonitorRenderResources[mirrored].monitorMirrorFB; - if (!PFB->isAllocated() || PFB->m_cTex.m_iTexID <= 0) + if (!PFB->isAllocated() || PFB->m_cTex->m_iTexID <= 0) return; // replace monitor projection to undo the mirrored monitor's projection @@ -2179,12 +2354,12 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { } // create a new one with cairo - CTexture tex; + SP tex = makeShared(); - const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str()); - const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE); + const auto CAIROISURFACE = cairo_image_surface_create_from_png(texPath.c_str()); + const auto CAIROFORMAT = cairo_image_surface_get_format(CAIROISURFACE); - tex.allocate(); + tex->allocate(); const Vector2D IMAGESIZE = {cairo_image_surface_get_width(CAIROISURFACE), cairo_image_surface_get_height(CAIROISURFACE)}; // calc the target box @@ -2220,8 +2395,8 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { cairo_surface_flush(CAIROSURFACE); - CBox box = {origin.x, origin.y, IMAGESIZE.x * scale, IMAGESIZE.y * scale}; - tex.m_vSize = IMAGESIZE * scale; + CBox box = {origin.x, origin.y, IMAGESIZE.x * scale, IMAGESIZE.y * scale}; + tex->m_vSize = IMAGESIZE * scale; // copy the data to an OpenGL texture we have const GLint glIFormat = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? @@ -2235,7 +2410,7 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { const GLint glType = CAIROFORMAT == CAIRO_FORMAT_RGB96F ? GL_FLOAT : GL_UNSIGNED_BYTE; const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); #ifndef GLES2 @@ -2244,7 +2419,7 @@ void CHyprOpenGLImpl::createBGTextureForMonitor(CMonitor* pMonitor) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); } #endif - glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex.m_vSize.x, tex.m_vSize.y, 0, glFormat, glType, DATA); + glTexImage2D(GL_TEXTURE_2D, 0, glIFormat, tex->m_vSize.x, tex->m_vSize.y, 0, glFormat, glType, DATA); cairo_surface_destroy(CAIROSURFACE); cairo_surface_destroy(CAIROISURFACE); @@ -2293,7 +2468,7 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) { RESIT->second.monitorMirrorFB.release(); RESIT->second.blurFB.release(); RESIT->second.offMainFB.release(); - RESIT->second.stencilTex.destroyTexture(); + RESIT->second.stencilTex->destroyTexture(); g_pHyprOpenGL->m_mMonitorRenderResources.erase(RESIT); } @@ -2343,109 +2518,6 @@ void CHyprOpenGLImpl::setRenderModifEnabled(bool enabled) { m_RenderData.renderModif.enabled = enabled; } -inline const SGLPixelFormat GLES2_FORMATS[] = { - { - .drmFormat = DRM_FORMAT_ARGB8888, - .glFormat = GL_BGRA_EXT, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = true, - }, - { - .drmFormat = DRM_FORMAT_XRGB8888, - .glFormat = GL_BGRA_EXT, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_XBGR8888, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_ABGR8888, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = true, - }, - { - .drmFormat = DRM_FORMAT_BGR888, - .glFormat = GL_RGB, - .glType = GL_UNSIGNED_BYTE, - .withAlpha = false, - }, -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - { - .drmFormat = DRM_FORMAT_RGBX4444, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_4_4_4_4, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_RGBA4444, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_4_4_4_4, - .withAlpha = true, - }, - { - .drmFormat = DRM_FORMAT_RGBX5551, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_5_5_5_1, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_RGBA5551, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT_5_5_5_1, - .withAlpha = true, - }, - { - .drmFormat = DRM_FORMAT_RGB565, - .glFormat = GL_RGB, - .glType = GL_UNSIGNED_SHORT_5_6_5, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_XBGR2101010, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_ABGR2101010, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_INT_2_10_10_10_REV_EXT, - .withAlpha = true, - }, - { - .drmFormat = DRM_FORMAT_XBGR16161616F, - .glFormat = GL_RGBA, - .glType = GL_HALF_FLOAT_OES, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_ABGR16161616F, - .glFormat = GL_RGBA, - .glType = GL_HALF_FLOAT_OES, - .withAlpha = true, - }, - { - .drmFormat = DRM_FORMAT_XBGR16161616, - .glInternalFormat = GL_RGBA16_EXT, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT, - .withAlpha = false, - }, - { - .drmFormat = DRM_FORMAT_ABGR16161616, - .glInternalFormat = GL_RGBA16_EXT, - .glFormat = GL_RGBA, - .glType = GL_UNSIGNED_SHORT, - .withAlpha = true, - }, -#endif -}; - uint32_t CHyprOpenGLImpl::getPreferredReadFormat(CMonitor* pMonitor) { GLint glf = -1, glt = -1, as = 0; /*glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &glf); @@ -2453,14 +2525,12 @@ uint32_t CHyprOpenGLImpl::getPreferredReadFormat(CMonitor* pMonitor) { glGetIntegerv(GL_ALPHA_BITS, &as);*/ if (glf == 0 || glt == 0) { - glf = drmFormatToGL(pMonitor->drmFormat); - glt = glFormatToType(glf); + glf = FormatUtils::drmFormatToGL(pMonitor->drmFormat); + glt = FormatUtils::glFormatToType(glf); } - for (auto& fmt : GLES2_FORMATS) { - if (fmt.glFormat == glf && fmt.glType == glt && fmt.withAlpha == (as > 0)) - return fmt.drmFormat; - } + if (const auto FMT = FormatUtils::getPixelFormatFromGL(glf, glt, as > 0); FMT) + return FMT->drmFormat; if (m_sExts.EXT_read_format_bgra) return DRM_FORMAT_XRGB8888; @@ -2468,13 +2538,8 @@ uint32_t CHyprOpenGLImpl::getPreferredReadFormat(CMonitor* pMonitor) { return DRM_FORMAT_XBGR8888; } -const SGLPixelFormat* CHyprOpenGLImpl::getPixelFormatFromDRM(uint32_t drmFormat) { - for (auto& fmt : GLES2_FORMATS) { - if (fmt.drmFormat == drmFormat) - return &fmt; - } - - return nullptr; +std::vector CHyprOpenGLImpl::getDRMFormats() { + return drmFormats; } void SRenderModifData::applyToBox(CBox& box) { diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index ca9eecc6..db0f8ea1 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -5,6 +5,7 @@ #include "../helpers/Color.hpp" #include "../helpers/Timer.hpp" #include "../helpers/Region.hpp" +#include "../helpers/Format.hpp" #include #include #include @@ -54,14 +55,6 @@ struct SRenderModifData { bool enabled = true; }; -struct SGLPixelFormat { - uint32_t drmFormat = DRM_FORMAT_INVALID; - GLint glInternalFormat = 0; - GLint glFormat = 0; - GLint glType = 0; - bool withAlpha = false; -}; - struct SMonitorRenderData { CFramebuffer offloadFB; CFramebuffer mirrorFB; // these are used for some effects, @@ -70,7 +63,7 @@ struct SMonitorRenderData { CFramebuffer monitorMirrorFB; // used for mirroring outputs, does not contain artifacts like offloadFB - CTexture stencilTex; + SP stencilTex = makeShared(); CFramebuffer blurFB; bool blurFBDirty = true; @@ -132,74 +125,73 @@ class CHyprOpenGLImpl { public: CHyprOpenGLImpl(); - void begin(CMonitor*, const CRegion& damage, CFramebuffer* fb = nullptr, std::optional finalDamage = {}); - void beginSimple(CMonitor*, const CRegion& damage, CRenderbuffer* rb = nullptr, CFramebuffer* fb = nullptr); - void end(); + void begin(CMonitor*, const CRegion& damage, CFramebuffer* fb = nullptr, std::optional finalDamage = {}); + void beginSimple(CMonitor*, const CRegion& damage, CRenderbuffer* rb = nullptr, CFramebuffer* fb = nullptr); + void end(); - void renderRect(CBox*, const CColor&, int round = 0); - void renderRectWithBlur(CBox*, const CColor&, int round = 0, float blurA = 1.f, bool xray = false); - void renderRectWithDamage(CBox*, const CColor&, CRegion* damage, int round = 0); - void renderTexture(wlr_texture*, CBox*, float a, int round = 0, bool allowCustomUV = false); - void renderTextureWithDamage(wlr_texture*, CBox*, CRegion* damage, float a, int round = 0, bool allowCustomUV = false); - void renderTexture(const CTexture&, CBox*, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false); - void renderTextureWithDamage(const CTexture&, CBox*, CRegion* damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false); - void renderTextureWithBlur(const CTexture&, CBox*, float a, wlr_surface* pSurface, int round = 0, bool blockBlurOptimization = false, float blurA = 1.f); - void renderRoundedShadow(CBox*, int round, int range, const CColor& color, float a = 1.0); - void renderBorder(CBox*, const CGradientValueData&, int round, int borderSize, float a = 1.0, int outerRound = -1 /* use round */); - void renderTextureMatte(const CTexture& tex, CBox* pBox, CFramebuffer& matte); + void renderRect(CBox*, const CColor&, int round = 0); + void renderRectWithBlur(CBox*, const CColor&, int round = 0, float blurA = 1.f, bool xray = false); + void renderRectWithDamage(CBox*, const CColor&, CRegion* damage, int round = 0); + void renderTexture(SP, CBox*, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false); + void renderTextureWithDamage(SP, CBox*, CRegion* damage, float a, int round = 0, bool discardActive = false, bool allowCustomUV = false); + void renderTextureWithBlur(SP, CBox*, float a, SP pSurface, int round = 0, bool blockBlurOptimization = false, float blurA = 1.f); + void renderRoundedShadow(CBox*, int round, int range, const CColor& color, float a = 1.0); + void renderBorder(CBox*, const CGradientValueData&, int round, int borderSize, float a = 1.0, int outerRound = -1 /* use round */); + void renderTextureMatte(SP tex, CBox* pBox, CFramebuffer& matte); - void setMonitorTransformEnabled(bool enabled); - void setRenderModifEnabled(bool enabled); + void setMonitorTransformEnabled(bool enabled); + void setRenderModifEnabled(bool enabled); - void saveMatrix(); - void setMatrixScaleTranslate(const Vector2D& translate, const float& scale); - void restoreMatrix(); + void saveMatrix(); + void setMatrixScaleTranslate(const Vector2D& translate, const float& scale); + void restoreMatrix(); - void blend(bool enabled); + void blend(bool enabled); - void makeWindowSnapshot(PHLWINDOW); - void makeRawWindowSnapshot(PHLWINDOW, CFramebuffer*); - void makeLayerSnapshot(PHLLS); - void renderSnapshot(PHLWINDOW); - void renderSnapshot(PHLLS); - bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); + void makeWindowSnapshot(PHLWINDOW); + void makeRawWindowSnapshot(PHLWINDOW, CFramebuffer*); + void makeLayerSnapshot(PHLLS); + void renderSnapshot(PHLWINDOW); + void renderSnapshot(PHLLS); + bool shouldUseNewBlurOptimizations(PHLLS pLayer, PHLWINDOW pWindow); - void clear(const CColor&); - void clearWithTex(); - void scissor(const CBox*, bool transform = true); - void scissor(const pixman_box32*, bool transform = true); - void scissor(const int x, const int y, const int w, const int h, bool transform = true); + void clear(const CColor&); + void clearWithTex(); + void scissor(const CBox*, bool transform = true); + void scissor(const pixman_box32*, bool transform = true); + void scissor(const int x, const int y, const int w, const int h, bool transform = true); - void destroyMonitorResources(CMonitor*); + void destroyMonitorResources(CMonitor*); - void markBlurDirtyForMonitor(CMonitor*); + void markBlurDirtyForMonitor(CMonitor*); - void preWindowPass(); - bool preBlurQueued(); - void preRender(CMonitor*); + void preWindowPass(); + bool preBlurQueued(); + void preRender(CMonitor*); - void saveBufferForMirror(CBox*); - void renderMirrored(); + void saveBufferForMirror(CBox*); + void renderMirrored(); - void applyScreenShader(const std::string& path); + void applyScreenShader(const std::string& path); - void bindOffMain(); - void renderOffToMain(CFramebuffer* off); - void bindBackOnMain(); + void bindOffMain(); + void renderOffToMain(CFramebuffer* off); + void bindBackOnMain(); - void setDamage(const CRegion& damage, std::optional finalDamage = {}); + void setDamage(const CRegion& damage, std::optional finalDamage = {}); - uint32_t getPreferredReadFormat(CMonitor* pMonitor); - const SGLPixelFormat* getPixelFormatFromDRM(uint32_t drmFormat); + uint32_t getPreferredReadFormat(CMonitor* pMonitor); + std::vector getDRMFormats(); + EGLImageKHR createEGLImage(const SDMABUFAttrs& attrs); - SCurrentRenderData m_RenderData; + SCurrentRenderData m_RenderData; - GLint m_iCurrentOutputFb = 0; + GLint m_iCurrentOutputFb = 0; - bool m_bReloadScreenShader = true; // at launch it can be set + bool m_bReloadScreenShader = true; // at launch it can be set - PHLWINDOWREF m_pCurrentWindow; // hack to get the current rendered window - PHLLS m_pCurrentLayer; // hack to get the current rendered layer + PHLWINDOWREF m_pCurrentWindow; // hack to get the current rendered window + PHLLS m_pCurrentLayer; // hack to get the current rendered layer std::map m_mWindowFramebuffers; std::map m_mLayerFramebuffers; @@ -208,41 +200,52 @@ class CHyprOpenGLImpl { struct { PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr; + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr; + PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = nullptr; PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = nullptr; + PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT = nullptr; + PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT = nullptr; } m_sProc; struct { - bool EXT_read_format_bgra = false; + bool EXT_read_format_bgra = false; + bool EXT_image_dma_buf_import = false; + bool EXT_image_dma_buf_import_modifiers = false; } m_sExts; private: - std::list m_lBuffers; - std::list m_lTextures; + std::list m_lBuffers; + std::list m_lTextures; - int m_iDRMFD; - std::string m_szExtensions; + std::vector drmFormats; + bool m_bHasModifiers = false; - bool m_bFakeFrame = false; - bool m_bEndFrame = false; - bool m_bApplyFinalShader = false; - bool m_bBlend = false; - bool m_bOffloadedFramebuffer = false; + int m_iDRMFD; + std::string m_szExtensions; - CShader m_sFinalScreenShader; - CTimer m_tGlobalTimer; + bool m_bFakeFrame = false; + bool m_bEndFrame = false; + bool m_bApplyFinalShader = false; + bool m_bBlend = false; + bool m_bOffloadedFramebuffer = false; - void logShaderError(const GLuint&, bool program = false); - GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); - GLuint compileShader(const GLuint&, std::string, bool dynamic = false); - void createBGTextureForMonitor(CMonitor*); - void initShaders(); + CShader m_sFinalScreenShader; + CTimer m_tGlobalTimer; + + void logShaderError(const GLuint&, bool program = false); + GLuint createProgram(const std::string&, const std::string&, bool dynamic = false); + GLuint compileShader(const GLuint&, std::string, bool dynamic = false); + void createBGTextureForMonitor(CMonitor*); + void initShaders(); + void initDRMFormats(); + std::vector getModsForFormat(EGLint format); // returns the out FB, can be either Mirror or MirrorSwap CFramebuffer* blurMainFramebufferWithDamage(float a, CRegion* damage); - void renderTextureInternalWithDamage(const CTexture&, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false, + void renderTextureInternalWithDamage(SP, CBox* pBox, float a, CRegion* damage, int round = 0, bool discardOpaque = false, bool noAA = false, bool allowCustomUV = false, bool allowDim = false); - void renderTexturePrimitive(const CTexture& tex, CBox* pBox); + void renderTexturePrimitive(SP tex, CBox* pBox); void renderSplash(cairo_t* const, cairo_surface_t* const, double offset, const Vector2D& size); void preBlurForCurrentMonitor(); diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp index 5003f600..694485c2 100644 --- a/src/render/Renderbuffer.cpp +++ b/src/render/Renderbuffer.cpp @@ -1,6 +1,7 @@ #include "Renderbuffer.hpp" #include "OpenGL.hpp" #include "../Compositor.hpp" +#include "../protocols/types/Buffer.hpp" #include @@ -61,6 +62,29 @@ CRenderbuffer::CRenderbuffer(wlr_buffer* buffer, uint32_t format) : m_pWlrBuffer &buffer->events.destroy, [this](void* owner, void* data) { g_pHyprRenderer->onRenderbufferDestroy(this); }, this, "CRenderbuffer"); } +CRenderbuffer::CRenderbuffer(SP buffer, uint32_t format) : m_pHLBuffer(buffer), m_uDrmFormat(format) { + auto dma = buffer->dmabuf(); + + m_iImage = g_pHyprOpenGL->createEGLImage(dma); + if (m_iImage == EGL_NO_IMAGE_KHR) + throw std::runtime_error("createEGLImage failed"); + + glGenRenderbuffers(1, &m_iRBO); + glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO); + g_pHyprOpenGL->m_sProc.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)m_iImage); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &m_sFramebuffer.m_iFb); + m_sFramebuffer.m_vSize = buffer->size; + m_sFramebuffer.bind(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_iRBO); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + throw std::runtime_error("rbo: glCheckFramebufferStatus failed"); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + void CRenderbuffer::bind() { glBindRenderbuffer(GL_RENDERBUFFER, m_iRBO); bindFB(); diff --git a/src/render/Renderbuffer.hpp b/src/render/Renderbuffer.hpp index 2a8bf250..ed7050c5 100644 --- a/src/render/Renderbuffer.hpp +++ b/src/render/Renderbuffer.hpp @@ -3,10 +3,12 @@ #include "Framebuffer.hpp" class CMonitor; +class IWLBuffer; class CRenderbuffer { public: CRenderbuffer(wlr_buffer* buffer, uint32_t format); + CRenderbuffer(SP buffer, uint32_t format); ~CRenderbuffer(); void bind(); @@ -16,6 +18,7 @@ class CRenderbuffer { uint32_t getFormat(); wlr_buffer* m_pWlrBuffer = nullptr; + WP m_pHLBuffer = {}; DYNLISTENER(destroyBuffer); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index c5c87ea1..8fac5869 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1,6 +1,5 @@ #include "Renderer.hpp" #include "../Compositor.hpp" -#include "linux-dmabuf-unstable-v1-protocol.h" #include "../helpers/Region.hpp" #include #include "../config/ConfigValue.hpp" @@ -13,6 +12,7 @@ #include "../protocols/XDGShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/core/DataDevice.hpp" +#include "../protocols/core/Compositor.hpp" extern "C" { #include @@ -90,19 +90,24 @@ CHyprRenderer::CHyprRenderer() { wl_event_source_timer_update(m_pCursorTicker, 500); } -static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) { - const auto TEXTURE = wlr_surface_get_texture(surface); - const auto RDATA = (SRenderData*)data; - const auto INTERACTIVERESIZEINPROGRESS = RDATA->pWindow && g_pInputManager->currentlyDraggedWindow.lock() == RDATA->pWindow && g_pInputManager->dragMode == MBIND_RESIZE; +static void renderSurface(SP surface, int x, int y, void* data) { + if (!surface->current.buffer || !surface->current.buffer->texture) + return; - if (!TEXTURE) + const auto& TEXTURE = surface->current.buffer->texture; + const auto RDATA = (SRenderData*)data; + const auto INTERACTIVERESIZEINPROGRESS = RDATA->pWindow && g_pInputManager->currentlyDraggedWindow.lock() == RDATA->pWindow && g_pInputManager->dragMode == MBIND_RESIZE; + + // this is bad, probably has been logged elsewhere. Means the texture failed + // uploading to the GPU. + if (!TEXTURE->m_iTexID) return; TRACY_GPU_ZONE("RenderSurface"); double outputX = -RDATA->pMonitor->vecPosition.x, outputY = -RDATA->pMonitor->vecPosition.y; - auto* const PSURFACE = CWLSurface::surfaceFromWlr(surface); + auto PSURFACE = CWLSurface::fromResource(surface); const float ALPHA = RDATA->alpha * RDATA->fadeAlpha * (PSURFACE ? PSURFACE->m_pAlphaModifier : 1.F); @@ -140,7 +145,7 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) } } else { // here we clamp to 2, these might be some tiny specks - windowBox = {(int)outputX + RDATA->x + x, (int)outputY + RDATA->y + y, std::max(surface->current.width, 2), std::max(surface->current.height, 2)}; + windowBox = {(int)outputX + RDATA->x + x, (int)outputY + RDATA->y + y, std::max((float)surface->current.size.x, 2.F), std::max((float)surface->current.size.y, 2.F)}; if (RDATA->pWindow && RDATA->pWindow->m_vRealSize.isBeingAnimated() && RDATA->surface && RDATA->surface != surface && RDATA->squishOversized /* subsurface */) { // adjust subsurfaces to the window windowBox.width = (windowBox.width / RDATA->pWindow->m_vReportedSize.x) * RDATA->pWindow->m_vRealSize.value().x; @@ -155,15 +160,23 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) windowBox.height = RDATA->h - y; } - if (windowBox.width <= 1 || windowBox.height <= 1) + if (windowBox.width <= 1 || windowBox.height <= 1) { + if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) { + surface->frame(RDATA->when); + auto FEEDBACK = makeShared(surface); + FEEDBACK->attachMonitor(RDATA->pMonitor); + FEEDBACK->discarded(); + PROTO::presentation->queueData(FEEDBACK); + } return; // invisible + } windowBox.scale(RDATA->pMonitor->scale); windowBox.round(); const bool MISALIGNEDFSV1 = std::floor(RDATA->pMonitor->scale) != RDATA->pMonitor->scale /* Fractional */ && surface->current.scale == 1 /* fs protocol */ && - windowBox.size() != Vector2D{surface->current.buffer_width, surface->current.buffer_height} /* misaligned */ && - DELTALESSTHAN(windowBox.width, surface->current.buffer_width, 3) && DELTALESSTHAN(windowBox.height, surface->current.buffer_height, 3) /* off by one-or-two */ && + windowBox.size() != surface->current.buffer->size /* misaligned */ && DELTALESSTHAN(windowBox.width, surface->current.buffer->size.x, 3) && + DELTALESSTHAN(windowBox.height, surface->current.buffer->size.y, 3) /* off by one-or-two */ && (!RDATA->pWindow || (!RDATA->pWindow->m_vRealSize.isBeingAnimated() && !INTERACTIVERESIZEINPROGRESS)) /* not window or not animated/resizing */; g_pHyprRenderer->calculateUVForSurface(RDATA->pWindow, surface, RDATA->surface == surface, windowBox.size(), MISALIGNEDFSV1); @@ -183,8 +196,8 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) if (RDATA->dontRound) rounding = 0; - const bool WINDOWOPAQUE = RDATA->pWindow && RDATA->pWindow->m_pWLSurface.wlr() == surface ? RDATA->pWindow->opaque() : false; - const bool CANDISABLEBLEND = ALPHA >= 1.f && rounding == 0 && (WINDOWOPAQUE || surface->opaque); + const bool WINDOWOPAQUE = RDATA->pWindow && RDATA->pWindow->m_pWLSurface->resource() == surface ? RDATA->pWindow->opaque() : false; + const bool CANDISABLEBLEND = ALPHA >= 1.f && rounding == 0 && WINDOWOPAQUE; if (CANDISABLEBLEND) g_pHyprOpenGL->blend(false); @@ -192,23 +205,19 @@ static void renderSurface(struct wlr_surface* surface, int x, int y, void* data) g_pHyprOpenGL->blend(true); if (RDATA->surface && surface == RDATA->surface) { - if (wlr_xwayland_surface_try_from_wlr_surface(surface) && !wlr_xwayland_surface_try_from_wlr_surface(surface)->has_alpha && ALPHA == 1.f) { - g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, true); - } else { - if (RDATA->blur) - g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, RDATA->blockBlurOptimization, RDATA->fadeAlpha); - else - g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, true); - } + if (RDATA->blur) + g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, RDATA->blockBlurOptimization, RDATA->fadeAlpha); + else + g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true); } else { if (RDATA->blur && RDATA->popup) g_pHyprOpenGL->renderTextureWithBlur(TEXTURE, &windowBox, ALPHA, surface, rounding, true, RDATA->fadeAlpha); else - g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, true); + g_pHyprOpenGL->renderTexture(TEXTURE, &windowBox, ALPHA, rounding, false, true); } if (!g_pHyprRenderer->m_bBlockSurfaceFeedback) { - wlr_surface_send_frame_done(surface, RDATA->when); + surface->frame(RDATA->when); auto FEEDBACK = makeShared(surface); FEEDBACK->attachMonitor(RDATA->pMonitor); FEEDBACK->presented(); @@ -510,7 +519,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec if (ignoreAllGeometry) decorate = false; - renderdata.surface = pWindow->m_pWLSurface.wlr(); + renderdata.surface = pWindow->m_pWLSurface->resource(); renderdata.dontRound = (pWindow->m_bIsFullscreen && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) || (!pWindow->m_sSpecialRenderData.rounding); renderdata.fadeAlpha = pWindow->m_fAlpha.value() * (pWindow->m_bPinned ? 1.f : PWORKSPACE->m_fAlpha.value()); renderdata.alpha = pWindow->m_fActiveInactiveAlpha.value(); @@ -580,7 +589,7 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec if ((pWindow->m_bIsX11 && *PXWLUSENN) || pWindow->m_sAdditionalConfigData.nearestNeighbor.toUnderlying()) g_pHyprOpenGL->m_RenderData.useNearestNeighbor = true; - if (!pWindow->m_sAdditionalConfigData.forceNoBlur && pWindow->m_pWLSurface.small() && !pWindow->m_pWLSurface.m_bFillIgnoreSmall && renderdata.blur && *PBLUR) { + if (!pWindow->m_sAdditionalConfigData.forceNoBlur && pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall && renderdata.blur && *PBLUR) { CBox wb = {renderdata.x - pMonitor->vecPosition.x, renderdata.y - pMonitor->vecPosition.y, renderdata.w, renderdata.h}; wb.scale(pMonitor->scale).round(); g_pHyprOpenGL->renderRectWithBlur(&wb, CColor(0, 0, 0, 0), renderdata.dontRound ? 0 : renderdata.rounding - 1, renderdata.fadeAlpha, @@ -588,7 +597,8 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec renderdata.blur = false; } - wlr_surface_for_each_surface(pWindow->m_pWLSurface.wlr(), renderSurface, &renderdata); + pWindow->m_pWLSurface->resource()->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, + &renderdata); g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false; @@ -645,14 +655,15 @@ void CHyprRenderer::renderWindow(PHLWINDOW pWindow, CMonitor* pMonitor, timespec pWindow->m_pPopupHead->breadthfirst( [](CPopup* popup, void* data) { - if (!popup->m_sWLSurface.wlr()) + if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource()) return; auto pos = popup->coordsRelativeToParent(); auto rd = (SRenderData*)data; Vector2D oldPos = {rd->x, rd->y}; rd->x += pos.x; rd->y += pos.y; - wlr_surface_for_each_surface(popup->m_sWLSurface.wlr(), renderSurface, rd); + popup->m_pWLSurface->resource()->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, + data); rd->x = oldPos.x; rd->y = oldPos.y; }, @@ -702,7 +713,7 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, CMonitor* pMonitor, timespec* time SRenderData renderdata = {pMonitor, time, REALPOS.x, REALPOS.y}; renderdata.fadeAlpha = pLayer->alpha.value(); renderdata.blur = pLayer->forceBlur; - renderdata.surface = pLayer->layerSurface->surface; + renderdata.surface = pLayer->surface->resource(); renderdata.decorate = false; renderdata.w = REALSIZ.x; renderdata.h = REALSIZ.y; @@ -721,7 +732,7 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, CMonitor* pMonitor, timespec* time } if (!popups) - wlr_surface_for_each_surface(pLayer->layerSurface->surface, renderSurface, &renderdata); + pLayer->surface->resource()->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata); renderdata.squishOversized = false; // don't squish popups renderdata.dontRound = true; @@ -730,11 +741,11 @@ void CHyprRenderer::renderLayer(PHLLS pLayer, CMonitor* pMonitor, timespec* time if (popups) { pLayer->popupHead->breadthfirst( [](CPopup* popup, void* data) { - if (!popup->m_sWLSurface.wlr()) + if (!popup->m_pWLSurface || !popup->m_pWLSurface->resource()) return; Vector2D pos = popup->coordsRelativeToParent(); - renderSurface(popup->m_sWLSurface.wlr(), pos.x, pos.y, data); + renderSurface(popup->m_pWLSurface->resource(), pos.x, pos.y, data); }, &renderdata); } @@ -750,15 +761,15 @@ void CHyprRenderer::renderIMEPopup(CInputPopup* pPopup, CMonitor* pMonitor, time SRenderData renderdata = {pMonitor, time, POS.x, POS.y}; - const auto SURF = pPopup->getWlrSurface(); + const auto SURF = pPopup->getSurface(); renderdata.blur = false; renderdata.surface = SURF; renderdata.decorate = false; - renderdata.w = SURF->current.width; - renderdata.h = SURF->current.height; + renderdata.w = SURF->current.size.x; + renderdata.h = SURF->current.size.y; - wlr_surface_for_each_surface(SURF, renderSurface, &renderdata); + SURF->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata); } void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, CMonitor* pMonitor, timespec* time) { @@ -770,7 +781,7 @@ void CHyprRenderer::renderSessionLockSurface(SSessionLockSurface* pSurface, CMon renderdata.w = pMonitor->vecSize.x; renderdata.h = pMonitor->vecSize.y; - wlr_surface_for_each_surface(pSurface->surface->surface(), renderSurface, &renderdata); + renderdata.surface->breadthfirst([](SP s, const Vector2D& offset, void* data) { renderSurface(s, offset.x, offset.y, data); }, &renderdata); } void CHyprRenderer::renderAllClientsForWorkspace(CMonitor* pMonitor, PHLWORKSPACE pWorkspace, timespec* time, const Vector2D& translate, const float& scale) { @@ -970,17 +981,15 @@ void CHyprRenderer::renderLockscreen(CMonitor* pMonitor, timespec* now, const CB } } -void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurface, bool main, const Vector2D& projSize, bool fixMisalignedFSV1) { +void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP pSurface, bool main, const Vector2D& projSize, bool fixMisalignedFSV1) { if (!pWindow || !pWindow->m_bIsX11) { Vector2D uvTL; Vector2D uvBR = Vector2D(1, 1); - if (pSurface->current.viewport.has_src) { + if (pSurface->current.viewport.hasSource) { // we stretch it to dest. if no dest, to 1,1 - wlr_fbox bufferSource; - wlr_surface_get_buffer_source_box(pSurface, &bufferSource); - - Vector2D bufferSize = Vector2D(pSurface->buffer->texture->width, pSurface->buffer->texture->height); + Vector2D bufferSize = pSurface->current.buffer->size; + auto bufferSource = pSurface->current.viewport.source; // calculate UV for the basic src_box. Assume dest == size. Scale to dest later uvTL = Vector2D(bufferSource.x / bufferSize.x, bufferSource.y / bufferSize.y); @@ -995,8 +1004,8 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa if (projSize != Vector2D{} && fixMisalignedFSV1) { // instead of nearest_neighbor (we will repeat / skip) // just cut off / expand surface - const Vector2D PIXELASUV = Vector2D{1, 1} / Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height}; - const Vector2D MISALIGNMENT = Vector2D{pSurface->buffer->texture->width, pSurface->buffer->texture->height} - projSize; + const Vector2D PIXELASUV = Vector2D{1, 1} / pSurface->current.buffer->size; + const Vector2D MISALIGNMENT = pSurface->current.buffer->size - projSize; if (MISALIGNMENT != Vector2D{}) uvBR -= MISALIGNMENT * PIXELASUV; } @@ -1017,10 +1026,10 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa // ignore X and Y, adjust uv if (geom.x != 0 || geom.y != 0 || geom.width > pWindow->m_vRealSize.value().x || geom.height > pWindow->m_vRealSize.value().y) { - const auto XPERC = (double)geom.x / (double)pSurface->current.width; - const auto YPERC = (double)geom.y / (double)pSurface->current.height; - const auto WPERC = (double)(geom.x + geom.width) / (double)pSurface->current.width; - const auto HPERC = (double)(geom.y + geom.height) / (double)pSurface->current.height; + const auto XPERC = (double)geom.x / (double)pSurface->current.size.x; + const auto YPERC = (double)geom.y / (double)pSurface->current.size.y; + const auto WPERC = (double)(geom.x + geom.width) / (double)pSurface->current.size.x; + const auto HPERC = (double)(geom.y + geom.height) / (double)pSurface->current.size.y; const auto TOADDTL = Vector2D(XPERC * (uvBR.x - uvTL.x), YPERC * (uvBR.y - uvTL.y)); uvBR = uvBR - Vector2D(1.0 - WPERC * (uvBR.x - uvTL.x), 1.0 - HPERC * (uvBR.y - uvTL.y)); @@ -1029,8 +1038,8 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa // TODO: make this passed to the func. Might break in the future. auto maxSize = pWindow->m_vRealSize.value(); - if (pWindow->m_pWLSurface.small() && !pWindow->m_pWLSurface.m_bFillIgnoreSmall) - maxSize = pWindow->m_pWLSurface.getViewporterCorrectedSize(); + if (pWindow->m_pWLSurface->small() && !pWindow->m_pWLSurface->m_bFillIgnoreSmall) + maxSize = pWindow->m_pWLSurface->getViewporterCorrectedSize(); if (geom.width > maxSize.x) uvBR.x = uvBR.x * (maxSize.x / geom.width); @@ -1052,53 +1061,51 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, wlr_surface* pSurfa } } -void countSubsurfacesIter(wlr_surface* pSurface, int x, int y, void* data) { - *(int*)data += 1; -} - bool CHyprRenderer::attemptDirectScanout(CMonitor* pMonitor) { - if (!pMonitor->mirrors.empty() || pMonitor->isMirror() || m_bDirectScanoutBlocked) - return false; // do not DS if this monitor is being mirrored. Will break the functionality. + return false; // FIXME: fix when we move to new lib for backend. - if (!wlr_output_is_direct_scanout_allowed(pMonitor->output)) - return false; + // if (!pMonitor->mirrors.empty() || pMonitor->isMirror() || m_bDirectScanoutBlocked) + // return false; // do not DS if this monitor is being mirrored. Will break the functionality. - const auto PCANDIDATE = pMonitor->solitaryClient.lock(); + // if (!wlr_output_is_direct_scanout_allowed(pMonitor->output)) + // return false; - if (!PCANDIDATE) - return false; + // const auto PCANDIDATE = pMonitor->solitaryClient.lock(); - const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE); + // if (!PCANDIDATE) + // return false; - if (!PSURFACE || PSURFACE->current.scale != pMonitor->output->scale || PSURFACE->current.transform != pMonitor->output->transform) - return false; + // const auto PSURFACE = g_pXWaylandManager->getWindowSurface(PCANDIDATE); - // finally, we should be GTG. - wlr_output_state_set_buffer(pMonitor->state.wlr(), &PSURFACE->buffer->base); + // if (!PSURFACE || PSURFACE->current.scale != pMonitor->output->scale || PSURFACE->current.transform != pMonitor->output->transform) + // return false; - if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) - return false; + // // finally, we should be GTG. + // wlr_output_state_set_buffer(pMonitor->state.wlr(), &PSURFACE->buffer->base); - timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(PSURFACE, &now); - auto FEEDBACK = makeShared(PSURFACE); - FEEDBACK->attachMonitor(pMonitor); - FEEDBACK->presented(); - FEEDBACK->setPresentationType(true); - PROTO::presentation->queueData(FEEDBACK); + // if (!wlr_output_test_state(pMonitor->output, pMonitor->state.wlr())) + // return false; - if (pMonitor->state.commit()) { - if (m_pLastScanout.expired()) { - m_pLastScanout = PCANDIDATE; - Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle); - } - } else { - m_pLastScanout.reset(); - return false; - } + // timespec now; + // clock_gettime(CLOCK_MONOTONIC, &now); + // PSURFACE->frame(&now); + // auto FEEDBACK = makeShared(PSURFACE); + // FEEDBACK->attachMonitor(pMonitor); + // FEEDBACK->presented(); + // FEEDBACK->setPresentationType(true); + // PROTO::presentation->queueData(FEEDBACK); - return true; + // if (pMonitor->state.commit()) { + // if (m_pLastScanout.expired()) { + // m_pLastScanout = PCANDIDATE; + // Debug::log(LOG, "Entered a direct scanout to {:x}: \"{}\"", (uintptr_t)PCANDIDATE.get(), PCANDIDATE->m_szTitle); + // } + // } else { + // m_pLastScanout.reset(); + // return false; + // } + + // return true; } void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { @@ -1346,7 +1353,7 @@ void CHyprRenderer::renderMonitor(CMonitor* pMonitor) { } } else g_pHyprRenderer->renderWindow(pMonitor->solitaryClient.lock(), pMonitor, &now, false, RENDER_PASS_MAIN /* solitary = no popups */); - } else { + } else if (!pMonitor->isMirror()) { sendFrameEventsToWorkspace(pMonitor, pMonitor->activeWorkspace, &now); if (pMonitor->activeSpecialWorkspace) sendFrameEventsToWorkspace(pMonitor, pMonitor->activeSpecialWorkspace, &now); @@ -1434,53 +1441,52 @@ void CHyprRenderer::renderWorkspace(CMonitor* pMonitor, PHLWORKSPACE pWorkspace, void CHyprRenderer::sendFrameEventsToWorkspace(CMonitor* pMonitor, PHLWORKSPACE pWorkspace, timespec* now) { for (auto& w : g_pCompositor->m_vWindows) { - if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || !w->m_pWLSurface.wlr()) + if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || !w->m_pWLSurface->resource()) continue; if (!shouldRenderWindow(w, pMonitor)) continue; - wlr_surface_for_each_surface( - w->m_pWLSurface.wlr(), [](wlr_surface* s, int x, int y, void* data) { wlr_surface_send_frame_done(s, (timespec*)data); }, now); + w->m_pWLSurface->resource()->breadthfirst([now](SP r, const Vector2D& offset, void* d) { r->frame(now); }, nullptr); } for (auto& lsl : pMonitor->m_aLayerSurfaceLayers) { for (auto& ls : lsl) { - if (ls->fadingOut || !ls->surface.wlr()) + if (ls->fadingOut || !ls->surface->resource()) continue; - wlr_surface_for_each_surface( - ls->surface.wlr(), [](wlr_surface* s, int x, int y, void* data) { wlr_surface_send_frame_done(s, (timespec*)data); }, now); + ls->surface->resource()->breadthfirst([now](SP r, const Vector2D& offset, void* d) { r->frame(now); }, nullptr); } } } void CHyprRenderer::setWindowScanoutMode(PHLWINDOW pWindow) { - if (!g_pCompositor->m_sWLRLinuxDMABuf || g_pSessionLockManager->isSessionLocked()) - return; + // FIXME: fix when moved to new impl + // if (!g_pCompositor->m_sWLRLinuxDMABuf || g_pSessionLockManager->isSessionLocked()) + // return; - if (!pWindow->m_bIsFullscreen) { - wlr_linux_dmabuf_v1_set_surface_feedback(g_pCompositor->m_sWLRLinuxDMABuf, pWindow->m_pWLSurface.wlr(), nullptr); - Debug::log(LOG, "Scanout mode OFF set for {}", pWindow); - return; - } + // if (!pWindow->m_bIsFullscreen) { + // wlr_linux_dmabuf_v1_set_surface_feedback(g_pCompositor->m_sWLRLinuxDMABuf, pWindow->m_pWLSurface->resource(), nullptr); + // Debug::log(LOG, "Scanout mode OFF set for {}", pWindow); + // return; + // } - const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + // const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); - const wlr_linux_dmabuf_feedback_v1_init_options INIT_OPTIONS = { - .main_renderer = g_pCompositor->m_sWLRRenderer, - .scanout_primary_output = PMONITOR->output, - }; + // const wlr_linux_dmabuf_feedback_v1_init_options INIT_OPTIONS = { + // .main_renderer = g_pCompositor->m_sWLRRenderer, + // .scanout_primary_output = PMONITOR->output, + // }; - wlr_linux_dmabuf_feedback_v1 feedback = {0}; + // wlr_linux_dmabuf_feedback_v1 feedback = {0}; - if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &INIT_OPTIONS)) - return; + // if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &INIT_OPTIONS)) + // return; - wlr_linux_dmabuf_v1_set_surface_feedback(g_pCompositor->m_sWLRLinuxDMABuf, pWindow->m_pWLSurface.wlr(), &feedback); - wlr_linux_dmabuf_feedback_v1_finish(&feedback); + // wlr_linux_dmabuf_v1_set_surface_feedback(g_pCompositor->m_sWLRLinuxDMABuf, pWindow->m_pWLSurface->resource(), &feedback); + // wlr_linux_dmabuf_feedback_v1_finish(&feedback); - Debug::log(LOG, "Scanout mode ON set for {}", pWindow); + // Debug::log(LOG, "Scanout mode ON set for {}", pWindow); } // taken from Sway. @@ -1666,26 +1672,25 @@ void CHyprRenderer::arrangeLayersForMonitor(const int& monitor) { g_pLayoutManager->getCurrentLayout()->recalculateMonitor(monitor); } -void CHyprRenderer::damageSurface(wlr_surface* pSurface, double x, double y, double scale) { +void CHyprRenderer::damageSurface(SP pSurface, double x, double y, double scale) { if (!pSurface) return; // wut? if (g_pCompositor->m_bUnsafeState) return; - const auto WLSURF = CWLSurface::surfaceFromWlr(pSurface); + const auto WLSURF = CWLSurface::fromResource(pSurface); CRegion damageBox = WLSURF ? WLSURF->logicalDamage() : CRegion{}; if (!WLSURF) { Debug::log(ERR, "BUG THIS: No CWLSurface for surface in damageSurface!!!"); - wlr_surface_get_effective_damage(pSurface, damageBox.pixman()); + return; } if (scale != 1.0) damageBox.scale(scale); // schedule frame events - if (!wl_list_empty(&pSurface->current.frame_callback_list)) - g_pCompositor->scheduleFrameForMonitor(g_pCompositor->getMonitorFromVector(Vector2D(x, y))); + g_pCompositor->scheduleFrameForMonitor(g_pCompositor->getMonitorFromVector(Vector2D(x, y))); if (damageBox.empty()) return; @@ -1840,7 +1845,6 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR pMonitor->onDisconnect(); pMonitor->events.modeChanged.emit(); - pMonitor->updateGlobal(); return true; } @@ -1865,6 +1869,9 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR !memcmp(&pMonitor->customDrmMode, &RULE->drmMode, sizeof(pMonitor->customDrmMode))) { Debug::log(LOG, "Not applying a new rule to {} because it's already applied!", pMonitor->szName); + + pMonitor->setMirror(RULE->mirrorOf); + return true; } @@ -2246,12 +2253,11 @@ bool CHyprRenderer::applyMonitorRule(CMonitor* pMonitor, SMonitorRule* pMonitorR EMIT_HOOK_EVENT("monitorLayoutChanged", nullptr); pMonitor->events.modeChanged.emit(); - pMonitor->updateGlobal(); return true; } -void CHyprRenderer::setCursorSurface(CWLSurface* surf, int hotspotX, int hotspotY, bool force) { +void CHyprRenderer::setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force) { m_bCursorHasSurface = surf; m_sLastCursorData.name = ""; @@ -2468,8 +2474,8 @@ bool CHyprRenderer::canSkipBackBufferClear(CMonitor* pMonitor) { continue; // TODO: cache maybe? - CRegion opaque = &ls->layerSurface->surface->opaque_region; - CBox lsbox = {0, 0, ls->layerSurface->surface->current.buffer_width, ls->layerSurface->surface->current.buffer_height}; + CRegion opaque = ls->layerSurface->surface->current.opaque; + CBox lsbox = {{}, ls->layerSurface->surface->current.size}; opaque.invert(lsbox); if (!opaque.empty()) @@ -2548,6 +2554,15 @@ CRenderbuffer* CHyprRenderer::getOrCreateRenderbuffer(wlr_buffer* buffer, uint32 return m_vRenderbuffers.emplace_back(std::make_unique(buffer, fmt)).get(); } +CRenderbuffer* CHyprRenderer::getOrCreateRenderbuffer(SP buffer, uint32_t fmt) { + auto it = std::find_if(m_vRenderbuffers.begin(), m_vRenderbuffers.end(), [&](const auto& other) { return other->m_pHLBuffer == buffer; }); + + if (it != m_vRenderbuffers.end()) + return it->get(); + + return m_vRenderbuffers.emplace_back(std::make_unique(buffer, fmt)).get(); +} + void CHyprRenderer::makeEGLCurrent() { if (!g_pCompositor) return; @@ -2560,7 +2575,7 @@ void CHyprRenderer::unsetEGL() { eglMakeCurrent(wlr_egl_get_display(g_pCompositor->m_sWLREGL), EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } -bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode, wlr_buffer* buffer, CFramebuffer* fb, bool simple) { +bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode, SP buffer, CFramebuffer* fb, bool simple) { makeEGLCurrent(); @@ -2590,10 +2605,13 @@ bool CHyprRenderer::beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode return false; } } else - m_pCurrentWlrBuffer = wlr_buffer_lock(buffer); + m_pCurrentHLBuffer = buffer; try { - m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentWlrBuffer, pMonitor->drmFormat); + if (m_pCurrentWlrBuffer) + m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentWlrBuffer, pMonitor->drmFormat); + else + m_pCurrentRenderbuffer = getOrCreateRenderbuffer(m_pCurrentHLBuffer.lock(), pMonitor->drmFormat); } catch (std::exception& e) { Debug::log(ERR, "getOrCreateRenderbuffer failed for {}", pMonitor->szName); wlr_buffer_unlock(m_pCurrentWlrBuffer); diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index da38fed2..f88bfebf 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -12,6 +12,7 @@ struct SMonitorRule; class CWorkspace; class CWindow; class CInputPopup; +class IWLBuffer; // TODO: add fuller damage tracking for updating only parts of a window enum DAMAGETRACKINGMODES { @@ -44,7 +45,7 @@ class CHyprRenderer { void renderMonitor(CMonitor* pMonitor); void arrangeLayersForMonitor(const int&); - void damageSurface(wlr_surface*, double, double, double scale = 1.0); + void damageSurface(SP, double, double, double scale = 1.0); void damageWindow(PHLWINDOW, bool forceFull = false); void damageBox(CBox*); void damageBox(const int& x, const int& y, const int& w, const int& h); @@ -57,14 +58,14 @@ class CHyprRenderer { void ensureCursorRenderingMode(); bool shouldRenderCursor(); void setCursorHidden(bool hide); - void calculateUVForSurface(PHLWINDOW, wlr_surface*, bool main = false, const Vector2D& projSize = {}, bool fixMisalignedFSV1 = false); + void calculateUVForSurface(PHLWINDOW, SP, bool main = false, const Vector2D& projSize = {}, bool fixMisalignedFSV1 = false); std::tuple getRenderTimes(CMonitor* pMonitor); // avg max min void renderLockscreen(CMonitor* pMonitor, timespec* now, const CBox& geometry); void setOccludedForBackLayers(CRegion& region, PHLWORKSPACE pWorkspace); void setOccludedForMainWorkspace(CRegion& region, PHLWORKSPACE pWorkspace); // TODO: merge occlusion methods bool canSkipBackBufferClear(CMonitor* pMonitor); void recheckSolitaryForMonitor(CMonitor* pMonitor); - void setCursorSurface(CWLSurface* surf, int hotspotX, int hotspotY, bool force = false); + void setCursorSurface(SP surf, int hotspotX, int hotspotY, bool force = false); void setCursorFromName(const std::string& name, bool force = false); void onRenderbufferDestroy(CRenderbuffer* rb); CRenderbuffer* getCurrentRBO(); @@ -74,7 +75,7 @@ class CHyprRenderer { // if RENDER_MODE_NORMAL, provided damage will be written to. // otherwise, it will be the one used. - bool beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, wlr_buffer* buffer = nullptr, CFramebuffer* fb = nullptr, bool simple = false); + bool beginRender(CMonitor* pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, CFramebuffer* fb = nullptr, bool simple = false); void endRender(); bool m_bBlockSurfaceFeedback = false; @@ -98,10 +99,10 @@ class CHyprRenderer { CTimer m_tRenderTimer; struct { - int hotspotX; - int hotspotY; - std::optional surf = nullptr; - std::string name; + int hotspotX; + int hotspotY; + std::optional> surf; + std::string name; } m_sLastCursorData; private: @@ -121,6 +122,7 @@ class CHyprRenderer { bool m_bCursorHasSurface = false; CRenderbuffer* m_pCurrentRenderbuffer = nullptr; wlr_buffer* m_pCurrentWlrBuffer = nullptr; + WP m_pCurrentHLBuffer = {}; eRenderMode m_eRenderMode = RENDER_MODE_NORMAL; bool m_bNvidia = false; @@ -132,6 +134,7 @@ class CHyprRenderer { } m_sCursorHiddenConditions; CRenderbuffer* getOrCreateRenderbuffer(wlr_buffer* buffer, uint32_t fmt); + CRenderbuffer* getOrCreateRenderbuffer(SP buffer, uint32_t fmt); std::vector> m_vRenderbuffers; friend class CHyprOpenGLImpl; diff --git a/src/render/Texture.cpp b/src/render/Texture.cpp index 1820d0fb..aef8e4ac 100644 --- a/src/render/Texture.cpp +++ b/src/render/Texture.cpp @@ -1,16 +1,54 @@ #include "Texture.hpp" +#include "Renderer.hpp" +#include "../Compositor.hpp" +#include "../protocols/types/Buffer.hpp" +#include "../helpers/Format.hpp" CTexture::CTexture() { // naffin' } +CTexture::~CTexture() { + if (m_bNonOwning) + return; + + g_pHyprRenderer->makeEGLCurrent(); + destroyTexture(); +} + +CTexture::CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size_) { + g_pHyprRenderer->makeEGLCurrent(); + + const auto format = FormatUtils::getPixelFormatFromDRM(drmFormat); + ASSERT(format); + + m_iType = format->withAlpha ? TEXTURE_RGBA : TEXTURE_RGBX; + m_vSize = size_; + allocate(); + + GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); +#ifndef GLES2 + if (format->flipRB) { + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED)); + } +#endif + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); + GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, format->glInternalFormat ? format->glInternalFormat : format->glFormat, size_.x, size_.y, 0, format->glFormat, format->glType, pixels)); + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); + GLCALL(glBindTexture(GL_TEXTURE_2D, 0)); +} + CTexture::CTexture(wlr_texture* tex) { RASSERT(wlr_texture_is_gles2(tex), "wlr_texture provided to CTexture that isn't GLES2!"); wlr_gles2_texture_attribs attrs; wlr_gles2_texture_get_attribs(tex, &attrs); - m_iTarget = attrs.target; - m_iTexID = attrs.tex; + m_iTarget = attrs.target; + m_iTexID = attrs.tex; + m_bNonOwning = true; if (m_iTarget == GL_TEXTURE_2D) m_iType = attrs.has_alpha ? TEXTURE_RGBA : TEXTURE_RGBX; @@ -20,14 +58,72 @@ CTexture::CTexture(wlr_texture* tex) { m_vSize = Vector2D(tex->width, tex->height); } +CTexture::CTexture(const SDMABUFAttrs& attrs, void* image) { + if (!g_pHyprOpenGL->m_sProc.glEGLImageTargetTexture2DOES) { + Debug::log(ERR, "Cannot create a dmabuf texture: no glEGLImageTargetTexture2DOES"); + return; + } + + m_iTarget = GL_TEXTURE_2D; + m_iType = TEXTURE_RGBA; + m_vSize = attrs.size; + m_iType = FormatUtils::isFormatOpaque(attrs.format) ? TEXTURE_RGBX : TEXTURE_RGBA; + allocate(); + m_pEglImage = image; + + GLCALL(glBindTexture(GL_TEXTURE_2D, m_iTexID)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GLCALL(g_pHyprOpenGL->m_sProc.glEGLImageTargetTexture2DOES(m_iTarget, image)); + GLCALL(glBindTexture(GL_TEXTURE_2D, 0)); +} + +void CTexture::update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage) { + g_pHyprRenderer->makeEGLCurrent(); + + const auto format = FormatUtils::getPixelFormatFromDRM(drmFormat); + ASSERT(format); + + glBindTexture(GL_TEXTURE_2D, m_iTexID); + + auto rects = damage.copy().intersect(CBox{{}, m_vSize}).getRects(); + +#ifndef GLES2 + if (format->flipRB) { + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE)); + GLCALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED)); + } +#endif + + for (auto& rect : rects) { + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format->bytesPerBlock)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, rect.x1)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, rect.y1)); + + int width = rect.x2 - rect.x1; + int height = rect.y2 - rect.y1; + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x1, rect.y1, width, height, format->glFormat, format->glType, pixels)); + } + + GLCALL(glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0)); + GLCALL(glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0)); + + glBindTexture(GL_TEXTURE_2D, 0); +} + void CTexture::destroyTexture() { if (m_iTexID) { - glDeleteTextures(1, &m_iTexID); + GLCALL(glDeleteTextures(1, &m_iTexID)); m_iTexID = 0; } + + if (m_pEglImage) + g_pHyprOpenGL->m_sProc.eglDestroyImageKHR(wlr_egl_get_display(g_pCompositor->m_sWLREGL), m_pEglImage); + m_pEglImage = nullptr; } void CTexture::allocate() { if (!m_iTexID) - glGenTextures(1, &m_iTexID); -} \ No newline at end of file + GLCALL(glGenTextures(1, &m_iTexID)); +} diff --git a/src/render/Texture.hpp b/src/render/Texture.hpp index 93a6aa5f..fa1ca4fe 100644 --- a/src/render/Texture.hpp +++ b/src/render/Texture.hpp @@ -2,6 +2,10 @@ #include "../defines.hpp" +class IWLBuffer; +struct SDMABUFAttrs; +class CRegion; + enum TEXTURETYPE { TEXTURE_INVALID, // Invalid TEXTURE_RGBA, // 4 channels @@ -12,13 +16,27 @@ enum TEXTURETYPE { class CTexture { public: CTexture(); + + CTexture(CTexture&) = delete; + CTexture(CTexture&&) = delete; + CTexture(const CTexture&&) = delete; + CTexture(const CTexture&) = delete; + + CTexture(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const Vector2D& size); CTexture(wlr_texture*); + // this ctor takes ownership of the eglImage. + CTexture(const SDMABUFAttrs&, void* image); + ~CTexture(); + void destroyTexture(); void allocate(); + void update(uint32_t drmFormat, uint8_t* pixels, uint32_t stride, const CRegion& damage); TEXTURETYPE m_iType = TEXTURE_RGBA; GLenum m_iTarget = GL_TEXTURE_2D; GLuint m_iTexID = 0; Vector2D m_vSize; + void* m_pEglImage = nullptr; + bool m_bNonOwning = false; // wlr }; \ No newline at end of file diff --git a/src/render/decorations/CHyprGroupBarDecoration.cpp b/src/render/decorations/CHyprGroupBarDecoration.cpp index 04f69aaa..731fce04 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.cpp +++ b/src/render/decorations/CHyprGroupBarDecoration.cpp @@ -6,23 +6,23 @@ #include // shared things to conserve VRAM -static CTexture m_tGradientActive; -static CTexture m_tGradientInactive; -static CTexture m_tGradientLockedActive; -static CTexture m_tGradientLockedInactive; +static SP m_tGradientActive = makeShared(); +static SP m_tGradientInactive = makeShared(); +static SP m_tGradientLockedActive = makeShared(); +static SP m_tGradientLockedInactive = makeShared(); -constexpr int BAR_INDICATOR_HEIGHT = 3; -constexpr int BAR_PADDING_OUTER_VERT = 2; -constexpr int BAR_PADDING_OUTER_HORZ = 2; -constexpr int BAR_TEXT_PAD = 2; -constexpr int BAR_HORIZONTAL_PADDING = 2; +constexpr int BAR_INDICATOR_HEIGHT = 3; +constexpr int BAR_PADDING_OUTER_VERT = 2; +constexpr int BAR_PADDING_OUTER_HORZ = 2; +constexpr int BAR_TEXT_PAD = 2; +constexpr int BAR_HORIZONTAL_PADDING = 2; CHyprGroupBarDecoration::CHyprGroupBarDecoration(PHLWINDOW pWindow) : IHyprWindowDecoration(pWindow) { static auto PGRADIENTS = CConfigValue("group:groupbar:enabled"); static auto PENABLED = CConfigValue("group:groupbar:gradients"); m_pWindow = pWindow; - if (m_tGradientActive.m_iTexID == 0 && *PENABLED && *PGRADIENTS) + if (m_tGradientActive->m_iTexID == 0 && *PENABLED && *PGRADIENTS) refreshGroupBarGradients(); } @@ -157,10 +157,9 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) { rect.scale(pMonitor->scale); if (*PGRADIENTS) { - const auto& GRADIENTTEX = - (m_dwGroupMembers[WINDOWINDEX].lock() == g_pCompositor->m_pLastWindow.lock() ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : - (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); - if (GRADIENTTEX.m_iTexID != 0) + const auto GRADIENTTEX = (m_dwGroupMembers[WINDOWINDEX] == g_pCompositor->m_pLastWindow ? (GROUPLOCKED ? m_tGradientLockedActive : m_tGradientActive) : + (GROUPLOCKED ? m_tGradientLockedInactive : m_tGradientInactive)); + if (GRADIENTTEX->m_iTexID != 0) g_pHyprOpenGL->renderTexture(GRADIENTTEX, &rect, 1.0); } @@ -173,9 +172,11 @@ void CHyprGroupBarDecoration::draw(CMonitor* pMonitor, float a) { .emplace_back(std::make_unique(m_dwGroupMembers[WINDOWINDEX].lock(), Vector2D{m_fBarWidth * pMonitor->scale, (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale}, pMonitor->scale)) .get(); - - rect.y += (*PHEIGHT / 2.0 - (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) / 2.0) * pMonitor->scale; - rect.height = (*PTITLEFONTSIZE + 2 * BAR_TEXT_PAD) * pMonitor->scale; + rect.y += ((rect.height - pTitleTex->textHeight) / 2.0) * pMonitor->scale; + rect.height = (pTitleTex->textHeight) * pMonitor->scale; + rect.width = pTitleTex->textWidth * pMonitor->scale; + rect.x += m_fBarWidth / 2.0 - (pTitleTex->textWidth / 2.0) * pMonitor->scale; + rect.round(); g_pHyprOpenGL->renderTexture(pTitleTex->tex, &rect, 1.f); } @@ -204,10 +205,11 @@ void CHyprGroupBarDecoration::invalidateTextures() { } CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale) { - szContent = pWindow->m_szTitle; - pWindowOwner = pWindow; - const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); - const auto CAIRO = cairo_create(CAIROSURFACE); + tex = makeShared(); + szContent = pWindow->m_szTitle; + pWindowOwner = pWindow; + const auto LAYOUTSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); + const auto LAYOUTCAIRO = cairo_create(LAYOUTSURFACE); static auto FALLBACKFONT = CConfigValue("misc:font_family"); static auto PTITLEFONTFAMILY = CConfigValue("group:groupbar:font_family"); @@ -217,14 +219,11 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float const CColor COLOR = CColor(*PTEXTCOLOR); const auto FONTFAMILY = *PTITLEFONTFAMILY != STRVAL_EMPTY ? *PTITLEFONTFAMILY : *FALLBACKFONT; - // clear the pixmap - cairo_save(CAIRO); - cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR); - cairo_paint(CAIRO); - cairo_restore(CAIRO); + cairo_surface_destroy(LAYOUTSURFACE); // draw title using Pango - PangoLayout* layout = pango_cairo_create_layout(CAIRO); + PangoLayout* layout = pango_cairo_create_layout(LAYOUTCAIRO); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); pango_layout_set_text(layout, szContent.c_str(), -1); PangoFontDescription* fontDesc = pango_font_description_new(); @@ -238,14 +237,23 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float pango_layout_set_width(layout, maxWidth * PANGO_SCALE); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + int layoutWidth, layoutHeight; + PangoRectangle inkRect; + PangoRectangle logicalRect; + pango_layout_get_pixel_extents(layout, &inkRect, &logicalRect); + layoutWidth = inkRect.width; + layoutHeight = inkRect.height; + + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layoutWidth, layoutHeight); + const auto CAIRO = cairo_create(CAIROSURFACE); + + // clear the pixmap + cairo_save(CAIRO); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR); + cairo_paint(CAIRO); + cairo_restore(CAIRO); + cairo_move_to(CAIRO, -inkRect.x, -inkRect.y); cairo_set_source_rgba(CAIRO, COLOR.r, COLOR.g, COLOR.b, COLOR.a); - - int layoutWidth, layoutHeight; - pango_layout_get_size(layout, &layoutWidth, &layoutHeight); - const int xOffset = std::round((bufferSize.x / 2.0 - layoutWidth / PANGO_SCALE / 2.0)); - const int yOffset = std::round((bufferSize.y / 2.0 - layoutHeight / PANGO_SCALE / 2.0)); - - cairo_move_to(CAIRO, xOffset, yOffset); pango_cairo_show_layout(CAIRO, layout); g_object_unref(layout); @@ -254,8 +262,8 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - tex.allocate(); - glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); + tex->allocate(); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -264,18 +272,21 @@ CTitleTex::CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layoutWidth, layoutHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); // delete cairo + textWidth = layoutWidth; + textHeight = layoutHeight; + cairo_destroy(LAYOUTCAIRO); cairo_destroy(CAIRO); cairo_surface_destroy(CAIROSURFACE); } CTitleTex::~CTitleTex() { - tex.destroyTexture(); + tex->destroyTexture(); } -void renderGradientTo(CTexture& tex, CGradientValueData* grad) { +void renderGradientTo(SP tex, CGradientValueData* grad) { if (!g_pCompositor->m_pLastMonitor) return; @@ -308,8 +319,8 @@ void renderGradientTo(CTexture& tex, CGradientValueData* grad) { // copy the data to an OpenGL texture we have const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); - tex.allocate(); - glBindTexture(GL_TEXTURE_2D, tex.m_iTexID); + tex->allocate(); + glBindTexture(GL_TEXTURE_2D, tex->m_iTexID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -340,11 +351,11 @@ void refreshGroupBarGradients() { g_pHyprRenderer->makeEGLCurrent(); - if (m_tGradientActive.m_iTexID != 0) { - m_tGradientActive.destroyTexture(); - m_tGradientInactive.destroyTexture(); - m_tGradientLockedActive.destroyTexture(); - m_tGradientLockedInactive.destroyTexture(); + if (m_tGradientActive->m_iTexID != 0) { + m_tGradientActive->destroyTexture(); + m_tGradientInactive->destroyTexture(); + m_tGradientLockedActive->destroyTexture(); + m_tGradientLockedInactive->destroyTexture(); } if (!*PENABLED || !*PGRADIENTS) diff --git a/src/render/decorations/CHyprGroupBarDecoration.hpp b/src/render/decorations/CHyprGroupBarDecoration.hpp index e3f553c5..1af24717 100644 --- a/src/render/decorations/CHyprGroupBarDecoration.hpp +++ b/src/render/decorations/CHyprGroupBarDecoration.hpp @@ -12,8 +12,11 @@ class CTitleTex { CTitleTex(PHLWINDOW pWindow, const Vector2D& bufferSize, const float monitorScale); ~CTitleTex(); - CTexture tex; + SP tex; std::string szContent; + int textWidth; + int textHeight; + PHLWINDOWREF pWindowOwner; }; diff --git a/src/xwayland/Server.cpp b/src/xwayland/Server.cpp new file mode 100644 index 00000000..26010cfb --- /dev/null +++ b/src/xwayland/Server.cpp @@ -0,0 +1,428 @@ +#ifndef NO_XWAYLAND + +#include "Server.hpp" +#include "../defines.hpp" +#include "../Compositor.hpp" +#include "../managers/CursorManager.hpp" +#include "XWayland.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// TODO: cleanup +static bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + wlr_log_errno(WLR_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + wlr_log_errno(WLR_ERROR, "fcntl failed"); + return false; + } + return true; +} + +static int openSocket(struct sockaddr_un* addr, size_t path_size) { + int fd, rc; + socklen_t size = offsetof(struct sockaddr_un, sun_path) + path_size + 1; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to create socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1); + return -1; + } + if (!set_cloexec(fd, true)) { + close(fd); + return -1; + } + + if (addr->sun_path[0]) { + unlink(addr->sun_path); + } + if (bind(fd, (struct sockaddr*)addr, size) < 0) { + rc = errno; + wlr_log_errno(WLR_ERROR, "Failed to bind socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1); + goto cleanup; + } + if (listen(fd, 1) < 0) { + rc = errno; + wlr_log_errno(WLR_ERROR, "Failed to listen to socket %c%s", addr->sun_path[0] ? addr->sun_path[0] : '@', addr->sun_path + 1); + goto cleanup; + } + + return fd; + +cleanup: + close(fd); + if (addr->sun_path[0]) { + unlink(addr->sun_path); + } + errno = rc; + return -1; +} + +static bool checkPermissionsForSocketDir(void) { + struct stat buf; + + if (lstat("/tmp/.X11-unix", &buf)) { + Debug::log(ERR, "Failed statting X11 socket dir"); + return false; + } + + if (!(buf.st_mode & S_IFDIR)) { + Debug::log(ERR, "X11 socket dir is not a dir"); + return false; + } + + if (!((buf.st_uid == 0) || (buf.st_uid == getuid()))) { + Debug::log(ERR, "X11 socket dir is not ours"); + return false; + } + + if (!(buf.st_mode & S_ISVTX)) { + if ((buf.st_mode & (S_IWGRP | S_IWOTH))) { + Debug::log(ERR, "X11 socket dir is sticky by others"); + return false; + } + } + + return true; +} + +static bool openSockets(std::array& sockets, int display) { + auto ret = mkdir("/tmp/.X11-unix", 755); + + if (ret != 0) { + if (errno == EEXIST) { + if (!checkPermissionsForSocketDir()) + return false; + } else { + Debug::log(ERR, "XWayland: couldn't create socket dir"); + return false; + } + } + + std::string path; + sockaddr_un addr = {.sun_family = AF_UNIX}; + +#ifdef __linux__ + // cursed... + addr.sun_path[0] = 0; + path = std::format("/tmp/.X11-unix/X{}", display); + strncpy(addr.sun_path + 1, path.c_str(), path.length() + 1); +#else + path = std::format("/tmp/.X11-unix/X{}_", display); + strncpy(addr.sun_path, path.c_str(), path.length() + 1); +#endif + sockets[0] = openSocket(&addr, path.length()); + if (sockets[0] < 0) + return false; + + path = std::format("/tmp/.X11-unix/X{}", display); + strncpy(addr.sun_path, path.c_str(), path.length() + 1); + sockets[1] = openSocket(&addr, path.length()); + if (sockets[1] < 0) { + close(sockets[0]); + sockets[0] = -1; + return false; + } + + return true; +} + +static void startServer(void* data) { + if (!g_pXWayland->pServer->start()) + Debug::log(ERR, "The XWayland server could not start! XWayland will not work..."); +} + +static int xwaylandReady(int fd, uint32_t mask, void* data) { + return g_pXWayland->pServer->ready(fd, mask); +} + +bool CXWaylandServer::tryOpenSockets() { + for (size_t i = 0; i <= 32; ++i) { + auto LOCK = std::format("/tmp/.X{}-lock", i); + + if (int fd = open(LOCK.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0444); fd >= 0) { + // we managed to open the lock + if (!openSockets(xFDs, i)) { + std::filesystem::remove(LOCK); + close(fd); + continue; + } + + const auto PIDSTR = std::format("{}", getpid()); + + if (write(fd, PIDSTR.c_str(), PIDSTR.length()) != (long)PIDSTR.length()) { + std::filesystem::remove(LOCK); + close(fd); + continue; + } + + close(fd); + + display = i; + displayName = std::format(":{}", display); + break; + } + + int fd = open(LOCK.c_str(), O_RDONLY | O_CLOEXEC); + + if (fd < 0) + continue; + + char pidstr[12] = {0}; + read(fd, pidstr, sizeof(pidstr) - 1); + close(fd); + + uint64_t pid = 0; + try { + pid = std::stoi(std::string{pidstr, 11}); + } catch (...) { continue; } + + if (kill(pid, 0) != 0 && errno == ESRCH) { + if (!std::filesystem::remove(LOCK)) + continue; + + i--; + } + } + + if (display < 0) { + Debug::log(ERR, "Failed to find a suitable socket for xwayland"); + return false; + } + + Debug::log(LOG, "XWayland found a suitable display socket at DISPLAY: {}", displayName); + return true; +} + +CXWaylandServer::CXWaylandServer() { + ; +} + +CXWaylandServer::~CXWaylandServer() { + die(); + if (display < 0) + return; + + if (xFDs[0]) + close(xFDs[0]); + if (xFDs[1]) + close(xFDs[1]); + + auto LOCK = std::format("/tmp/.X{}-lock", display); + std::filesystem::remove(LOCK); + + std::string path; +#ifdef __linux__ + path = std::format("/tmp/.X11-unix/X{}", display); +#else + path = std::format("/tmp/.X11-unix/X{}_", display); +#endif + std::filesystem::remove(path); +} + +void CXWaylandServer::die() { + if (display < 0) + return; + + if (xFDReadEvents[0]) { + wl_event_source_remove(xFDReadEvents[0]); + wl_event_source_remove(xFDReadEvents[1]); + + xFDReadEvents = {nullptr, nullptr}; + } + + if (pipeSource) + wl_event_source_remove(pipeSource); + + if (waylandFDs[0]) + close(waylandFDs[0]); + if (waylandFDs[1]) + close(waylandFDs[1]); + if (xwmFDs[0]) + close(xwmFDs[0]); + if (xwmFDs[1]) + close(xwmFDs[1]); + + // possible crash. Better to leak a bit. + //if (xwaylandClient) + // wl_client_destroy(xwaylandClient); + + xwaylandClient = nullptr; + waylandFDs = {-1, -1}; + xwmFDs = {-1, -1}; +} + +bool CXWaylandServer::create() { + if (!tryOpenSockets()) + return false; + + setenv("DISPLAY", displayName.c_str(), true); + + // TODO: lazy mode + + idleSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, ::startServer, nullptr); + + return true; +} + +void CXWaylandServer::runXWayland(int notifyFD) { + if (!set_cloexec(xFDs[0], false) || !set_cloexec(xFDs[1], false) || !set_cloexec(waylandFDs[1], false) || !set_cloexec(xwmFDs[1], false)) { + Debug::log(ERR, "Failed to unset cloexec on fds"); + _exit(EXIT_FAILURE); + } + + auto cmd = std::format("Xwayland {} -rootless -core -listenfd {} -listenfd {} -displayfd {} -wm {}", displayName, xFDs[0], xFDs[1], notifyFD, xwmFDs[1]); + + auto waylandSocket = std::format("{}", waylandFDs[1]); + setenv("WAYLAND_SOCKET", waylandSocket.c_str(), true); + + Debug::log(LOG, "Starting XWayland with \"{}\", bon voyage!", cmd); + + execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), nullptr); + + Debug::log(ERR, "XWayland failed to open"); + _exit(1); +} + +bool CXWaylandServer::start() { + idleSource = nullptr; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, waylandFDs.data()) != 0) { + Debug::log(ERR, "socketpair failed (1)"); + die(); + return false; + } + + if (!set_cloexec(waylandFDs[0], true) || !set_cloexec(waylandFDs[1], true)) { + Debug::log(ERR, "set_cloexec failed (1)"); + die(); + return false; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, xwmFDs.data()) != 0) { + Debug::log(ERR, "socketpair failed (2)"); + die(); + return false; + } + + if (!set_cloexec(xwmFDs[0], true) || !set_cloexec(xwmFDs[1], true)) { + Debug::log(ERR, "set_cloexec failed (2)"); + die(); + return false; + } + + xwaylandClient = wl_client_create(g_pCompositor->m_sWLDisplay, waylandFDs[0]); + if (!xwaylandClient) { + Debug::log(ERR, "wl_client_create failed"); + die(); + return false; + } + + waylandFDs[0] = -1; + + int notify[2] = {-1, -1}; + if (pipe(notify) < 0) { + Debug::log(ERR, "pipe failed"); + die(); + return false; + } + + if (!set_cloexec(notify[0], true)) { + Debug::log(ERR, "set_cloexec failed (3)"); + close(notify[0]); + close(notify[1]); + die(); + return false; + } + + pipeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, notify[0], WL_EVENT_READABLE, ::xwaylandReady, nullptr); + + serverPID = fork(); + if (serverPID < 0) { + Debug::log(ERR, "fork failed"); + close(notify[0]); + close(notify[1]); + die(); + return false; + } else if (serverPID == 0) { + pid_t pid = fork(); + if (pid < 0) { + Debug::log(ERR, "second fork failed"); + _exit(1); + } else if (pid == 0) { + runXWayland(notify[1]); + } + + _exit(0); + } + + close(notify[1]); + close(waylandFDs[1]); + close(xwmFDs[1]); + waylandFDs[1] = -1; + xwmFDs[1] = -1; + + return true; +} + +int CXWaylandServer::ready(int fd, uint32_t mask) { + if (mask & WL_EVENT_READABLE) { + // xwayland writes twice + char buf[64]; + ssize_t n = read(fd, buf, sizeof(buf)); + if (n < 0 && errno != EINTR) { + Debug::log(ERR, "Xwayland: read from displayFd failed"); + mask = 0; + } else if (n <= 0 || buf[n - 1] != '\n') + return 1; + } + + while (waitpid(serverPID, nullptr, 0) < 0) { + if (errno == EINTR) + continue; + Debug::log(ERR, "Xwayland: waitpid for fork failed"); + g_pXWayland->pServer.reset(); + return 1; + } + + // if we don't have readable here, it failed + if (!(mask & WL_EVENT_READABLE)) { + Debug::log(ERR, "Xwayland: startup failed, not setting up xwm"); + g_pXWayland->pServer.reset(); + return 1; + } + + Debug::log(LOG, "XWayland is ready"); + + close(fd); + wl_event_source_remove(pipeSource); + pipeSource = nullptr; + + // start the wm + g_pXWayland->pWM = std::make_unique(); + + g_pCursorManager->setXWaylandCursor(); + + return 0; +} + +#endif diff --git a/src/xwayland/Server.hpp b/src/xwayland/Server.hpp new file mode 100644 index 00000000..0c06a56c --- /dev/null +++ b/src/xwayland/Server.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include "../helpers/signal/Signal.hpp" + +struct wl_event_source; +struct wl_client; + +// TODO: add lazy mode +class CXWaylandServer { + public: + CXWaylandServer(); + ~CXWaylandServer(); + + // create the server. + bool create(); + + // starts the server, meant to be called by CXWaylandServer. + bool start(); + + // called on ready + int ready(int fd, uint32_t mask); + + void die(); + + struct { + CSignal ready; + } events; + + wl_client* xwaylandClient = nullptr; + + private: + bool tryOpenSockets(); + void runXWayland(int notifyFD); + + pid_t serverPID = 0; + + std::string displayName; + int display = -1; + std::array xFDs = {-1, -1}; + std::array xFDReadEvents = {nullptr, nullptr}; + wl_event_source* idleSource = nullptr; + wl_event_source* pipeSource = nullptr; + std::array xwmFDs = {-1, -1}; + std::array waylandFDs = {-1, -1}; + + friend class CXWM; +}; diff --git a/src/xwayland/XDataSource.cpp b/src/xwayland/XDataSource.cpp new file mode 100644 index 00000000..98e0701b --- /dev/null +++ b/src/xwayland/XDataSource.cpp @@ -0,0 +1,97 @@ +#ifndef NO_XWAYLAND + +#include "XDataSource.hpp" +#include "XWayland.hpp" +#include "../defines.hpp" + +#include + +CXDataSource::CXDataSource(SXSelection& sel_) : selection(sel_) { + xcb_get_property_cookie_t cookie = xcb_get_property(g_pXWayland->pWM->connection, + 1, // delete + selection.window, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 4096); + + xcb_get_property_reply_t* reply = xcb_get_property_reply(g_pXWayland->pWM->connection, cookie, NULL); + if (!reply) + return; + + if (reply->type != XCB_ATOM_ATOM) { + free(reply); + return; + } + + auto value = (xcb_atom_t*)xcb_get_property_value(reply); + for (uint32_t i = 0; i < reply->value_len; i++) { + if (value[i] == HYPRATOMS["UTF8_STRING"]) + mimeTypes.push_back("text/plain;charset=utf-8"); + else if (value[i] == HYPRATOMS["TEXT"]) + mimeTypes.push_back("text/plain"); + else if (value[i] != HYPRATOMS["TARGETS"] && value[i] != HYPRATOMS["TIMESTAMP"]) { + + auto type = g_pXWayland->pWM->mimeFromAtom(value[i]); + + if (type == "INVALID") + continue; + + mimeTypes.push_back(type); + } + + mimeAtoms.push_back(value[i]); + } + + free(reply); +} + +std::vector CXDataSource::mimes() { + return mimeTypes; +} + +void CXDataSource::send(const std::string& mime, uint32_t fd) { + xcb_atom_t mimeAtom = 0; + + for (size_t i = 0; i < mimeTypes.size(); ++i) { + if (mimeTypes.at(i) == mime) { + mimeAtom = mimeAtoms.at(i); + break; + } + } + + if (!mimeAtom) { + Debug::log(ERR, "[XDataSource] mime atom not found"); + close(fd); + return; + } + + Debug::log(LOG, "[XDataSource] send with mime {} to fd {}", mime, fd); + + selection.transfer = std::make_unique(selection); + selection.transfer->incomingWindow = xcb_generate_id(g_pXWayland->pWM->connection); + const uint32_t MASK = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_create_window(g_pXWayland->pWM->connection, XCB_COPY_FROM_PARENT, selection.transfer->incomingWindow, g_pXWayland->pWM->screen->root, 0, 0, 10, 10, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, g_pXWayland->pWM->screen->root_visual, XCB_CW_EVENT_MASK, &MASK); + + xcb_convert_selection(g_pXWayland->pWM->connection, selection.transfer->incomingWindow, HYPRATOMS["CLIPBOARD"], mimeAtom, HYPRATOMS["_WL_SELECTION"], XCB_TIME_CURRENT_TIME); + + xcb_flush(g_pXWayland->pWM->connection); + + fcntl(fd, F_SETFL, O_WRONLY | O_NONBLOCK); + selection.transfer->wlFD = fd; +} + +void CXDataSource::accepted(const std::string& mime) { + Debug::log(LOG, "[XDataSource] accepted is a stub"); +} + +void CXDataSource::cancelled() { + Debug::log(LOG, "[XDataSource] cancelled is a stub"); +} + +void CXDataSource::error(uint32_t code, const std::string& msg) { + Debug::log(LOG, "[XDataSource] error is a stub: err {}: {}", code, msg); +} + +eDataSourceType CXDataSource::type() { + return DATA_SOURCE_TYPE_X11; +} + +#endif \ No newline at end of file diff --git a/src/xwayland/XDataSource.hpp b/src/xwayland/XDataSource.hpp new file mode 100644 index 00000000..c629aa2a --- /dev/null +++ b/src/xwayland/XDataSource.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "../protocols/types/DataDevice.hpp" + +struct SXSelection; + +class CXDataSource : public IDataSource { + public: + CXDataSource(SXSelection&); + + virtual std::vector mimes(); + virtual void send(const std::string& mime, uint32_t fd); + virtual void accepted(const std::string& mime); + virtual void cancelled(); + virtual void error(uint32_t code, const std::string& msg); + virtual eDataSourceType type(); + + private: + SXSelection& selection; + std::vector mimeTypes; // these two have shared idx + std::vector mimeAtoms; // +}; \ No newline at end of file diff --git a/src/xwayland/XSurface.cpp b/src/xwayland/XSurface.cpp new file mode 100644 index 00000000..07cac832 --- /dev/null +++ b/src/xwayland/XSurface.cpp @@ -0,0 +1,287 @@ +#include "XSurface.hpp" +#include "XWayland.hpp" +#include "../protocols/XWaylandShell.hpp" +#include "../protocols/core/Compositor.hpp" + +#ifndef NO_XWAYLAND + +#include "../Compositor.hpp" +#include + +CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : xID(xID_), geometry(geometry_), overrideRedirect(OR) { + xcb_res_query_client_ids_cookie_t client_id_cookie = {0}; + if (g_pXWayland->pWM->xres) { + xcb_res_client_id_spec_t spec = {.client = xID, .mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID}; + client_id_cookie = xcb_res_query_client_ids(g_pXWayland->pWM->connection, 1, &spec); + } + + uint32_t values[1]; + values[0] = XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_PROPERTY_CHANGE; + xcb_change_window_attributes(g_pXWayland->pWM->connection, xID, XCB_CW_EVENT_MASK, values); + + if (g_pXWayland->pWM->xres) { + xcb_res_query_client_ids_reply_t* reply = xcb_res_query_client_ids_reply(g_pXWayland->pWM->connection, client_id_cookie, nullptr); + if (!reply) + return; + + uint32_t* ppid = nullptr; + xcb_res_client_id_value_iterator_t iter = xcb_res_query_client_ids_ids_iterator(reply); + while (iter.rem > 0) { + if (iter.data->spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID && xcb_res_client_id_value_value_length(iter.data) > 0) { + ppid = xcb_res_client_id_value_value(iter.data); + break; + } + xcb_res_client_id_value_next(&iter); + } + if (ppid == NULL) { + free(reply); + return; + } + pid = *ppid; + free(reply); + } + + events.resourceChange.registerStaticListener([this](void* data, std::any d) { ensureListeners(); }, nullptr); +} + +void CXWaylandSurface::ensureListeners() { + bool connected = listeners.destroySurface; + + if (connected && !surface) { + listeners.destroySurface.reset(); + listeners.commitSurface.reset(); + } else if (!connected && surface) { + listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { + if (mapped) + unmap(); + + surface.reset(); + listeners.destroySurface.reset(); + listeners.commitSurface.reset(); + events.resourceChange.emit(); + }); + + listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) { + if (surface->pending.buffer && !mapped) { + map(); + return; + } + + if (!surface->pending.buffer && mapped) { + unmap(); + return; + } + + events.commit.emit(); + }); + } + + if (resource) { + listeners.destroyResource = resource->events.destroy.registerListener([this](std::any d) { + unmap(); + surface.reset(); + events.resourceChange.emit(); + }); + } +} + +void CXWaylandSurface::map() { + if (mapped) + return; + + ASSERT(surface); + + g_pXWayland->pWM->mappedSurfaces.emplace_back(self); + g_pXWayland->pWM->mappedSurfacesStacking.emplace_back(self); + + mapped = true; + surface->map(); + + Debug::log(LOG, "XWayland surface {:x} mapping", (uintptr_t)this); + + events.map.emit(); + + g_pXWayland->pWM->updateClientList(); +} + +void CXWaylandSurface::unmap() { + if (!mapped) + return; + + ASSERT(surface); + + std::erase(g_pXWayland->pWM->mappedSurfaces, self); + std::erase(g_pXWayland->pWM->mappedSurfacesStacking, self); + + mapped = false; + surface->unmap(); + + Debug::log(LOG, "XWayland surface {:x} unmapping", (uintptr_t)this); + + events.unmap.emit(); + + g_pXWayland->pWM->updateClientList(); +} + +void CXWaylandSurface::considerMap() { + if (mapped) + return; + + if (!surface) { + Debug::log(LOG, "XWayland surface: considerMap, nope, no surface"); + return; + } + + if (surface->pending.buffer) { + Debug::log(LOG, "XWayland surface: considerMap, sure, we have a buffer"); + map(); + return; + } + + Debug::log(LOG, "XWayland surface: considerMap, nope, we don't have a buffer"); +} + +bool CXWaylandSurface::wantsFocus() { + if (atoms.empty()) + return true; + + const std::array search = { + HYPRATOMS["_NET_WM_WINDOW_TYPE_COMBO"], HYPRATOMS["_NET_WM_WINDOW_TYPE_DND"], HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"], + HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"], HYPRATOMS["_NET_WM_WINDOW_TYPE_NOTIFICATION"], HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"], + HYPRATOMS["_NET_WM_WINDOW_TYPE_SPLASH"], HYPRATOMS["_NET_WM_WINDOW_TYPE_DESKTOP"], HYPRATOMS["_NET_WM_WINDOW_TYPE_TOOLTIP"], + HYPRATOMS["_NET_WM_WINDOW_TYPE_UTILITY"], + }; + + for (auto& searched : search) { + for (auto& a : atoms) { + if (a == searched) + return false; + } + } + + return true; +} + +void CXWaylandSurface::configure(const CBox& box) { + Vector2D oldSize = geometry.size(); + + geometry = box; + + uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH; + uint32_t values[] = {box.x, box.y, box.width, box.height, 0}; + xcb_configure_window(g_pXWayland->pWM->connection, xID, mask, values); + + g_pXWayland->pWM->updateClientList(); + + xcb_flush(g_pXWayland->pWM->connection); +} + +void CXWaylandSurface::activate(bool activate) { + if (overrideRedirect && !activate) + return; + g_pXWayland->pWM->activateSurface(self.lock()); +} + +void CXWaylandSurface::setFullscreen(bool fs) { + fullscreen = fs; + g_pXWayland->pWM->sendState(self.lock()); +} + +void CXWaylandSurface::setMinimized(bool mz) { + minimized = mz; + g_pXWayland->pWM->sendState(self.lock()); +} + +void CXWaylandSurface::restackToTop() { + uint32_t values[1] = {XCB_STACK_MODE_ABOVE}; + + xcb_configure_window(g_pXWayland->pWM->connection, xID, XCB_CONFIG_WINDOW_STACK_MODE, values); + + for (auto it = g_pXWayland->pWM->mappedSurfacesStacking.begin(); it != g_pXWayland->pWM->mappedSurfacesStacking.end(); ++it) { + if (*it == self) { + std::rotate(it, it + 1, g_pXWayland->pWM->mappedSurfacesStacking.end()); + break; + } + } + + g_pXWayland->pWM->updateClientList(); + + xcb_flush(g_pXWayland->pWM->connection); +} + +void CXWaylandSurface::close() { + xcb_client_message_data_t msg = {0}; + msg.data32[0] = HYPRATOMS["WM_DELETE_WINDOW"]; + msg.data32[1] = XCB_CURRENT_TIME; + g_pXWayland->pWM->sendWMMessage(self.lock(), &msg, XCB_EVENT_MASK_NO_EVENT); +} + +void CXWaylandSurface::setWithdrawn(bool withdrawn_) { + withdrawn = withdrawn_; + std::vector props = {XCB_ICCCM_WM_STATE_NORMAL, XCB_WINDOW_NONE}; + + if (withdrawn) + props[0] = XCB_ICCCM_WM_STATE_WITHDRAWN; + else if (minimized) + props[0] = XCB_ICCCM_WM_STATE_ICONIC; + else + props[0] = XCB_ICCCM_WM_STATE_NORMAL; + + xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, xID, HYPRATOMS["WM_STATE"], HYPRATOMS["WM_STATE"], 32, props.size(), props.data()); +} + +#else + +CXWaylandSurface::CXWaylandSurface(uint32_t xID_, CBox geometry_, bool OR) : xID(xID_), geometry(geometry_), overrideRedirect(OR) { + ; +} + +void CXWaylandSurface::ensureListeners() { + ; +} + +void CXWaylandSurface::map() { + ; +} + +void CXWaylandSurface::unmap() { + ; +} + +bool CXWaylandSurface::wantsFocus() { + return false; +} + +void CXWaylandSurface::configure(const CBox& box) { + ; +} + +void CXWaylandSurface::activate(bool activate) { + ; +} + +void CXWaylandSurface::setFullscreen(bool fs) { + ; +} + +void CXWaylandSurface::setMinimized(bool mz) { + ; +} + +void CXWaylandSurface::restackToTop() { + ; +} + +void CXWaylandSurface::close() { + ; +} + +void CXWaylandSurface::considerMap() { + ; +} + +void CXWaylandSurface::setWithdrawn(bool withdrawn) { + ; +} + +#endif diff --git a/src/xwayland/XSurface.hpp b/src/xwayland/XSurface.hpp new file mode 100644 index 00000000..0cdac1d5 --- /dev/null +++ b/src/xwayland/XSurface.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include "../helpers/WLListener.hpp" +#include "../helpers/signal/Signal.hpp" +#include "../helpers/Box.hpp" +#include + +class CWLSurfaceResource; +class CXWaylandSurfaceResource; + +#ifdef NO_XWAYLAND +typedef uint32_t xcb_pixmap_t; +typedef uint32_t xcb_window_t; +typedef struct { + int32_t flags; + uint32_t input; + int32_t initial_state; + xcb_pixmap_t icon_pixmap; + xcb_window_t icon_window; + int32_t icon_x, icon_y; + xcb_pixmap_t icon_mask; + xcb_window_t window_group; +} xcb_icccm_wm_hints_t; +typedef struct { + uint32_t flags; + int32_t x, y; + int32_t width, height; + int32_t min_width, min_height; + int32_t max_width, max_height; + int32_t width_inc, height_inc; + int32_t min_aspect_num, min_aspect_den; + int32_t max_aspect_num, max_aspect_den; + int32_t base_width, base_height; + uint32_t win_gravity; +} xcb_size_hints_t; +#else +#include +#endif + +class CXWaylandSurface { + public: + WP surface; + WP resource; + + struct { + CSignal stateChanged; // maximized, fs, minimized, etc. + CSignal metadataChanged; // title, appid + CSignal destroy; + + CSignal resourceChange; // associated / dissociated + + CSignal setGeometry; + CSignal configure; // CBox + + CSignal map; + CSignal unmap; + CSignal commit; + + CSignal activate; + } events; + + struct { + std::string title; + std::string appid; + + // volatile state: is reset after the stateChanged signal fires + std::optional requestsMaximize; + std::optional requestsFullscreen; + std::optional requestsMinimize; + } state; + + uint32_t xID = 0; + uint64_t wlID = 0; + uint64_t wlSerial = 0; + pid_t pid = 0; + CBox geometry; + bool overrideRedirect = false; + bool withdrawn = false; + bool fullscreen = false; + bool maximized = false; + bool minimized = false; + bool mapped = false; + bool modal = false; + + WP parent; + WP self; + std::vector> children; + + UP hints; + UP sizeHints; + std::vector atoms; + std::string role = ""; + bool transient = false; + + bool wantsFocus(); + void configure(const CBox& box); + void activate(bool activate); + void setFullscreen(bool fs); + void setMinimized(bool mz); + void restackToTop(); + void close(); + + private: + CXWaylandSurface(uint32_t xID, CBox geometry, bool OR); + + void ensureListeners(); + void map(); + void unmap(); + void considerMap(); + void setWithdrawn(bool withdrawn); + + struct { + CHyprSignalListener destroyResource; + CHyprSignalListener destroySurface; + CHyprSignalListener commitSurface; + } listeners; + + friend class CXWM; +}; \ No newline at end of file diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp new file mode 100644 index 00000000..7af1d506 --- /dev/null +++ b/src/xwayland/XWM.cpp @@ -0,0 +1,1220 @@ +#include "helpers/Vector2D.hpp" +#ifndef NO_XWAYLAND + +#include "XWayland.hpp" +#include "../defines.hpp" +#include +#include "../Compositor.hpp" +#include "../protocols/XWaylandShell.hpp" +#include "../protocols/core/Compositor.hpp" +#include "../managers/SeatManager.hpp" +#include "../protocols/core/Seat.hpp" +#include +#include +#include +#include + +#include + +#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f +#define INCR_CHUNK_SIZE (64 * 1024) + +static int onX11Event(int fd, uint32_t mask, void* data) { + return g_pXWayland->pWM->onEvent(fd, mask); +} + +SP CXWM::windowForXID(xcb_window_t wid) { + for (auto& s : surfaces) { + if (s->xID == wid) + return s; + } + + return nullptr; +} + +void CXWM::handleCreate(xcb_create_notify_event_t* e) { + if (isWMWindow(e->window)) + return; + + const auto XSURF = surfaces.emplace_back(SP(new CXWaylandSurface(e->window, CBox{e->x, e->y, e->width, e->height}, e->override_redirect))); + XSURF->self = XSURF; + Debug::log(LOG, "[xwm] New XSurface at {:x} with xid of {}", (uintptr_t)XSURF.get(), e->window); + + const auto WINDOW = CWindow::create(XSURF); + g_pCompositor->m_vWindows.emplace_back(WINDOW); + WINDOW->m_pSelf = WINDOW; + Debug::log(LOG, "[xwm] New XWayland window at {:x} for surf {:x}", (uintptr_t)WINDOW.get(), (uintptr_t)XSURF.get()); +} + +void CXWM::handleDestroy(xcb_destroy_notify_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + XSURF->events.destroy.emit(); + std::erase_if(surfaces, [XSURF](const auto& other) { return XSURF == other; }); +} + +void CXWM::handleConfigure(xcb_configure_request_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + const uint16_t MASK = e->value_mask; + constexpr uint16_t GEOMETRY = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; + if (!(MASK & GEOMETRY)) + return; + + XSURF->events.configure.emit(CBox{MASK & XCB_CONFIG_WINDOW_X ? e->x : XSURF->geometry.x, MASK & XCB_CONFIG_WINDOW_Y ? e->y : XSURF->geometry.y, + MASK & XCB_CONFIG_WINDOW_WIDTH ? e->width : XSURF->geometry.width, MASK & XCB_CONFIG_WINDOW_HEIGHT ? e->height : XSURF->geometry.height}); +} + +void CXWM::handleConfigureNotify(xcb_configure_notify_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + if (XSURF->geometry == CBox{e->x, e->y, e->width, e->height}) + return; + + XSURF->geometry = {e->x, e->y, e->width, e->height}; + XSURF->events.setGeometry.emit(); +} + +void CXWM::handleMapRequest(xcb_map_request_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + xcb_map_window(connection, e->window); + + XSURF->restackToTop(); + + const bool SMALL = + XSURF->geometry.size() < Vector2D{2, 2} || (XSURF->sizeHints && XSURF->geometry.size() < Vector2D{XSURF->sizeHints->min_width, XSURF->sizeHints->min_height}); + const bool HAS_HINTS = XSURF->sizeHints && Vector2D{XSURF->sizeHints->base_width, XSURF->sizeHints->base_height} > Vector2D{5, 5}; + const auto DESIREDSIZE = HAS_HINTS ? Vector2D{XSURF->sizeHints->base_width, XSURF->sizeHints->base_height} : Vector2D{800, 800}; + + // if it's too small, or its base size is set, configure it. + if ((SMALL || HAS_HINTS) && !XSURF->overrideRedirect) // default to 800 x 800 + XSURF->configure({XSURF->geometry.pos(), DESIREDSIZE}); + + Debug::log(LOG, "[xwm] Mapping window {} in X (geometry {}x{} at {}x{}))", e->window, XSURF->geometry.width, XSURF->geometry.height, XSURF->geometry.x, XSURF->geometry.y); + + // read data again. Some apps for some reason fail to send WINDOW_TYPE + // this shouldn't happen but does, I prolly fucked up somewhere, this is a band-aid + readWindowData(XSURF); + + XSURF->considerMap(); +} + +void CXWM::handleMapNotify(xcb_map_notify_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF || XSURF->overrideRedirect) + return; + + XSURF->setWithdrawn(false); + sendState(XSURF); + xcb_flush(connection); + + XSURF->considerMap(); +} + +void CXWM::handleUnmapNotify(xcb_unmap_notify_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + XSURF->unmap(); + dissociate(XSURF); + + if (XSURF->overrideRedirect) + return; + + XSURF->setWithdrawn(true); + sendState(XSURF); + xcb_flush(connection); +} + +static bool lookupParentExists(SP XSURF, SP prospectiveChild) { + while (XSURF->parent) { + if (XSURF->parent == prospectiveChild) + return true; + XSURF = XSURF->parent.lock(); + } + + return false; +} + +void CXWM::readProp(SP XSURF, uint32_t atom, xcb_get_property_reply_t* reply) { + std::string propName = "?"; + for (auto& ha : HYPRATOMS) { + if (ha.second != atom) + continue; + + propName = ha.first; + break; + } + + if (atom == XCB_ATOM_WM_CLASS) { + size_t len = xcb_get_property_value_length(reply); + char* string = (char*)xcb_get_property_value(reply); + XSURF->state.appid = std::string{string, len}; + if (std::count(XSURF->state.appid.begin(), XSURF->state.appid.end(), '\000') == 2) + XSURF->state.appid = XSURF->state.appid.substr(XSURF->state.appid.find_first_of('\000') + 1); // fuck you X + if (!XSURF->state.appid.empty()) + XSURF->state.appid.pop_back(); + XSURF->events.metadataChanged.emit(); + } else if (atom == XCB_ATOM_WM_NAME || atom == HYPRATOMS["_NET_WM_NAME"]) { + size_t len = xcb_get_property_value_length(reply); + char* string = (char*)xcb_get_property_value(reply); + XSURF->state.title = std::string{string, len}; + XSURF->events.metadataChanged.emit(); + } else if (atom == HYPRATOMS["_NET_WM_WINDOW_TYPE"]) { + xcb_atom_t* atomsArr = (xcb_atom_t*)xcb_get_property_value(reply); + size_t atomsNo = reply->value_len; + XSURF->atoms.clear(); + for (size_t i = 0; i < atomsNo; ++i) { + XSURF->atoms.push_back(atomsArr[i]); + } + } else if (atom == HYPRATOMS["_NET_WM_STATE"]) { + xcb_atom_t* atoms = (xcb_atom_t*)xcb_get_property_value(reply); + for (uint32_t i = 0; i < reply->value_len; i++) { + if (atoms[i] == HYPRATOMS["_NET_WM_STATE_MODAL"]) + XSURF->modal = true; + } + } else if (atom == HYPRATOMS["WM_HINTS"]) { + if (reply->value_len != 0) { + XSURF->hints = std::make_unique(); + xcb_icccm_get_wm_hints_from_reply(XSURF->hints.get(), reply); + + if (!(XSURF->hints->flags & XCB_ICCCM_WM_HINT_INPUT)) + XSURF->hints->input = true; + } + } else if (atom == HYPRATOMS["WM_WINDOW_ROLE"]) { + size_t len = xcb_get_property_value_length(reply); + + if (len <= 0) + XSURF->role = ""; + else { + XSURF->role = std::string{(char*)xcb_get_property_value(reply), len}; + XSURF->role = XSURF->role.substr(0, XSURF->role.find_first_of('\000')); + } + } else if (atom == XCB_ATOM_WM_TRANSIENT_FOR) { + if (reply->type == XCB_ATOM_WINDOW) { + const auto XID = (xcb_window_t*)xcb_get_property_value(reply); + XSURF->transient = XID; + if (XID) { + if (const auto NEWXSURF = windowForXID(*XID); NEWXSURF && !lookupParentExists(XSURF, NEWXSURF)) { + XSURF->parent = NEWXSURF; + NEWXSURF->children.push_back(XSURF); + } else + Debug::log(LOG, "[xwm] Denying transient because it would create a loop"); + } + } + } else if (atom == HYPRATOMS["WM_NORMAL_HINTS"]) { + if (reply->type == HYPRATOMS["WM_SIZE_HINTS"] && reply->value_len > 0) { + XSURF->sizeHints = std::make_unique(); + std::memset(XSURF->sizeHints.get(), 0, sizeof(xcb_size_hints_t)); + + xcb_icccm_get_wm_size_hints_from_reply(XSURF->sizeHints.get(), reply); + + const int32_t FLAGS = XSURF->sizeHints->flags; + const bool HASMIN = (FLAGS & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE); + const bool HASBASE = (FLAGS & XCB_ICCCM_SIZE_HINT_BASE_SIZE); + + if (!HASMIN && !HASBASE) { + XSURF->sizeHints->min_width = -1; + XSURF->sizeHints->min_height = -1; + XSURF->sizeHints->base_width = -1; + XSURF->sizeHints->base_height = -1; + } else if (!HASBASE) { + XSURF->sizeHints->base_width = XSURF->sizeHints->min_width; + XSURF->sizeHints->base_height = XSURF->sizeHints->min_height; + } else if (!HASMIN) { + XSURF->sizeHints->min_width = XSURF->sizeHints->base_width; + XSURF->sizeHints->min_height = XSURF->sizeHints->base_height; + } + + if (!(FLAGS & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)) { + XSURF->sizeHints->max_width = -1; + XSURF->sizeHints->max_height = -1; + } + } + } else { + Debug::log(LOG, "[xwm] Unhandled prop {} -> {}", atom, propName); + return; + } + + Debug::log(LOG, "[xwm] Handled prop {} -> {}", atom, propName); +} + +void CXWM::handlePropertyNotify(xcb_property_notify_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + xcb_get_property_cookie_t cookie = xcb_get_property(connection, 0, XSURF->xID, e->atom, XCB_ATOM_ANY, 0, 2048); + xcb_get_property_reply_t* reply = xcb_get_property_reply(connection, cookie, nullptr); + if (!reply) { + Debug::log(ERR, "[xwm] Failed to read property notify cookie"); + return; + } + + readProp(XSURF, e->atom, reply); + + free(reply); +} + +void CXWM::handleClientMessage(xcb_client_message_event_t* e) { + const auto XSURF = windowForXID(e->window); + + if (!XSURF) + return; + + std::string propName = "?"; + for (auto& ha : HYPRATOMS) { + if (ha.second != e->type) + continue; + + propName = ha.first; + break; + } + + if (e->type == HYPRATOMS["WL_SURFACE_ID"]) { + if (XSURF->surface) { + Debug::log(WARN, "[xwm] Re-assignment of WL_SURFACE_ID"); + dissociate(XSURF); + } + + auto id = e->data.data32[0]; + auto resource = wl_client_get_object(g_pXWayland->pServer->xwaylandClient, id); + if (resource) { + auto wlrSurface = CWLSurfaceResource::fromResource(resource); + associate(XSURF, wlrSurface); + } + } else if (e->type == HYPRATOMS["WL_SURFACE_SERIAL"]) { + if (XSURF->wlSerial) { + Debug::log(WARN, "[xwm] Re-assignment of WL_SURFACE_SERIAL"); + dissociate(XSURF); + } + + uint32_t serialLow = e->data.data32[0]; + uint32_t serialHigh = e->data.data32[1]; + XSURF->wlSerial = ((uint64_t)serialHigh << 32) | serialLow; + + Debug::log(LOG, "[xwm] surface {:x} requests serial {:x}", (uintptr_t)XSURF.get(), XSURF->wlSerial); + + for (auto& res : shellResources) { + if (!res) + continue; + + if (res->serial != XSURF->wlSerial || !XSURF->wlSerial) + continue; + + associate(XSURF, res->surface.lock()); + break; + } + + } else if (e->type == HYPRATOMS["_NET_WM_STATE"]) { + if (e->format == 32) { + uint32_t action = e->data.data32[0]; + for (size_t i = 0; i < 2; ++i) { + xcb_atom_t prop = e->data.data32[1 + i]; + + auto updateState = [XSURF](int action, bool current) -> bool { + switch (action) { + case 0: + /* remove */ + return false; + case 1: + /* add */ + return true; + case 2: + /* toggle */ + return !current; + default: return false; + } + return false; + }; + + if (prop == HYPRATOMS["_NET_WM_STATE_FULLSCREEN"]) + XSURF->state.requestsFullscreen = updateState(action, XSURF->fullscreen); + } + + XSURF->events.stateChanged.emit(); + } + } else if (e->type == HYPRATOMS["_NET_ACTIVE_WINDOW"]) { + XSURF->events.activate.emit(); + } else { + Debug::log(LOG, "[xwm] Unhandled message prop {} -> {}", e->type, propName); + return; + } + + Debug::log(LOG, "[xwm] Handled message prop {} -> {}", e->type, propName); +} + +void CXWM::handleFocusIn(xcb_focus_in_event_t* e) { + if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB || e->detail == XCB_NOTIFY_DETAIL_POINTER) + return; + + const auto XSURF = windowForXID(e->event); + + if (!XSURF) + return; + + if (focusedSurface && focusedSurface->pid == XSURF->pid && e->sequence - lastFocusSeq <= 255) + focusWindow(XSURF); + else + focusWindow(focusedSurface.lock()); +} + +void CXWM::sendWMMessage(SP surf, xcb_client_message_data_t* data, uint32_t mask) { + xcb_client_message_event_t event = { + .response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .sequence = 0, + .window = surf->xID, + .type = HYPRATOMS["WM_PROTOCOLS"], + .data = *data, + }; + + xcb_send_event(connection, 0, surf->xID, mask, (const char*)&event); + xcb_flush(connection); +} + +void CXWM::focusWindow(SP surf) { + if (surf == focusedSurface) + return; + + auto oldSurf = focusedSurface.lock(); + focusedSurface = surf; + + if (oldSurf) + sendState(oldSurf); + + if (!surf) { + xcb_set_input_focus_checked(connection, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME); + return; + } + + if (surf->overrideRedirect) + return; + + xcb_client_message_data_t msg = {0}; + msg.data32[0] = HYPRATOMS["WM_TAKE_FOCUS"]; + msg.data32[1] = XCB_TIME_CURRENT_TIME; + + if (surf->hints && !surf->hints->input) + sendWMMessage(surf, &msg, XCB_EVENT_MASK_NO_EVENT); + else { + sendWMMessage(surf, &msg, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT); + + xcb_void_cookie_t cookie = xcb_set_input_focus(connection, XCB_INPUT_FOCUS_POINTER_ROOT, surf->xID, XCB_CURRENT_TIME); + lastFocusSeq = cookie.sequence; + } +} + +void CXWM::handleError(xcb_value_error_t* e) { + const char* major_name = xcb_errors_get_name_for_major_code(errors, e->major_opcode); + if (!major_name) { + Debug::log(ERR, "xcb error happened, but could not get major name"); + return; + } + + const char* minor_name = xcb_errors_get_name_for_minor_code(errors, e->major_opcode, e->minor_opcode); + + const char* extension; + const char* error_name = xcb_errors_get_name_for_error(errors, e->error_code, &extension); + if (!error_name) { + Debug::log(ERR, "xcb error happened, but could not get error name"); + return; + } + + Debug::log(ERR, "[xwm] xcb error: {} ({}), code {} ({}), seq {}, val {}", major_name, minor_name ? minor_name : "no minor", error_name, extension ? extension : "no extension", + e->sequence, e->bad_value); +} + +void CXWM::selectionSendNotify(xcb_selection_request_event_t* e, bool success) { + xcb_selection_notify_event_t selection_notify = { + .response_type = XCB_SELECTION_NOTIFY, + .sequence = 0, + .time = e->time, + .requestor = e->requestor, + .selection = e->selection, + .target = e->target, + .property = success ? e->property : (uint32_t)XCB_ATOM_NONE, + }; + + xcb_send_event(connection, 0, e->requestor, XCB_EVENT_MASK_NO_EVENT, (const char*)&selection_notify); + xcb_flush(connection); +} + +xcb_atom_t CXWM::mimeToAtom(const std::string& mime) { + if (mime == "text/plain;charset=utf-8") + return HYPRATOMS["UTF8_STRING"]; + if (mime == "text/plain") + return HYPRATOMS["TEXT"]; + + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 0, mime.length(), mime.c_str()); + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(connection, cookie, nullptr); + if (!reply) + return XCB_ATOM_NONE; + xcb_atom_t atom = reply->atom; + free(reply); + return atom; +} + +std::string CXWM::mimeFromAtom(xcb_atom_t atom) { + if (atom == HYPRATOMS["UTF8_STRING"]) + return "text/plain;charset=utf-8"; + if (atom == HYPRATOMS["TEXT"]) + return "text/plain"; + + xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(connection, atom); + xcb_get_atom_name_reply_t* reply = xcb_get_atom_name_reply(connection, cookie, nullptr); + if (!reply) + return "INVALID"; + size_t len = xcb_get_atom_name_name_length(reply); + char* buf = xcb_get_atom_name_name(reply); // not a C string + std::string SZNAME{buf, len}; + free(reply); + return SZNAME; +} + +void CXWM::handleSelectionNotify(xcb_selection_notify_event_t* e) { + Debug::log(LOG, "[xwm] Selection notify for {} prop {} target {}", e->selection, e->property, e->target); + + SXSelection& sel = clipboard; + + if (e->property == XCB_ATOM_NONE) { + if (sel.transfer) { + Debug::log(ERR, "[xwm] converting selection failed"); + sel.transfer.reset(); + } + } else if (e->target == HYPRATOMS["TARGETS"]) { + if (!focusedSurface) { + Debug::log(LOG, "[xwm] denying access to write to clipboard because no X client is in focus"); + return; + } + + setClipboardToWayland(sel); + } else if (sel.transfer) + getTransferData(sel); +} + +bool CXWM::handleSelectionPropertyNotify(xcb_property_notify_event_t* e) { + // Debug::log(LOG, "[xwm] Selection property notify for {} target {}", e->atom, e->window); + + // Debug::log(ERR, "[xwm] FIXME: CXWM::handleSelectionPropertyNotify stub"); + + return true; +} + +void CXWM::handleSelectionRequest(xcb_selection_request_event_t* e) { + Debug::log(LOG, "[xwm] Selection request for {} prop {} target {} time {} requestor {} selection {}", e->selection, e->property, e->target, e->time, e->requestor, + e->selection); + + SXSelection& sel = clipboard; + + if (!g_pSeatManager->selection.currentSelection) { + Debug::log(ERR, "[xwm] No selection"); + selectionSendNotify(e, false); + return; + } + + if (e->selection == HYPRATOMS["CLIPBOARD_MANAGER"]) { + selectionSendNotify(e, true); + return; + } + + if (sel.window != e->owner && e->time != XCB_CURRENT_TIME && e->time < sel.timestamp) { + Debug::log(ERR, "[xwm] outdated selection request. Time {} < {}", e->time, sel.timestamp); + selectionSendNotify(e, false); + return; + } + + if (!g_pSeatManager->state.keyboardFocusResource || g_pSeatManager->state.keyboardFocusResource->client() != g_pXWayland->pServer->xwaylandClient) { + Debug::log(LOG, "[xwm] Ignoring clipboard access: xwayland not in focus"); + selectionSendNotify(e, false); + return; + } + + if (e->target == HYPRATOMS["TARGETS"]) { + // send mime types + auto mimes = g_pSeatManager->selection.currentSelection->mimes(); + + std::vector atoms; + atoms.push_back(HYPRATOMS["TIMESTAMP"]); + atoms.push_back(HYPRATOMS["TARGETS"]); + + for (auto& m : mimes) { + atoms.push_back(mimeToAtom(m)); + } + + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, e->requestor, e->property, XCB_ATOM_ATOM, 32, atoms.size(), atoms.data()); + selectionSendNotify(e, true); + } else if (e->target == HYPRATOMS["TIMESTAMP"]) { + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, e->requestor, e->property, XCB_ATOM_INTEGER, 32, 1, &sel.timestamp); + selectionSendNotify(e, true); + } else if (e->target == HYPRATOMS["DELETE"]) { + selectionSendNotify(e, true); + } else { + + std::string mime = mimeFromAtom(e->target); + + if (mime == "INVALID") { + Debug::log(LOG, "[xwm] Ignoring clipboard access: invalid mime atom {}", e->target); + selectionSendNotify(e, false); + return; + } + + if (!sel.sendData(e, mime)) { + Debug::log(LOG, "[xwm] Failed to send selection :("); + selectionSendNotify(e, false); + return; + } + } +} + +bool CXWM::handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e) { + Debug::log(LOG, "[xwm] Selection xfixes notify for {}", e->selection); + + // IMPORTANT: mind the g_pSeatManager below + SXSelection& sel = clipboard; + + if (e->owner == XCB_WINDOW_NONE) { + if (sel.owner != sel.window) + g_pSeatManager->setCurrentSelection(nullptr); + + sel.owner = 0; + return true; + } + + sel.owner = e->owner; + + if (sel.owner == sel.window) { + sel.timestamp = e->timestamp; + return true; + } + + xcb_convert_selection(connection, sel.window, HYPRATOMS["CLIPBOARD"], HYPRATOMS["TARGETS"], HYPRATOMS["_WL_SELECTION"], e->timestamp); + xcb_flush(connection); + + return true; +} + +bool CXWM::handleSelectionEvent(xcb_generic_event_t* e) { + switch (e->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { + case XCB_SELECTION_NOTIFY: { + handleSelectionNotify((xcb_selection_notify_event_t*)e); + return true; + } + case XCB_PROPERTY_NOTIFY: { + return handleSelectionPropertyNotify((xcb_property_notify_event_t*)e); + } + case XCB_SELECTION_REQUEST: { + handleSelectionRequest((xcb_selection_request_event_t*)e); + return true; + } + } + + if (e->response_type - xfixes->first_event == XCB_XFIXES_SELECTION_NOTIFY) + return handleSelectionXFixesNotify((xcb_xfixes_selection_notify_event_t*)e); + + return 0; +} + +int CXWM::onEvent(int fd, uint32_t mask) { + + if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { + Debug::log(ERR, "XWayland has yeeten the xwm off?!"); + Debug::log(CRIT, "XWayland has yeeten the xwm off?!"); + g_pXWayland->pWM.reset(); + g_pXWayland->pServer.reset(); + return 0; + } + + int count = 0; + + while (42069) { + xcb_generic_event_t* event = xcb_poll_for_event(connection); + if (!event) + break; + + count++; + + if (handleSelectionEvent(event)) { + free(event); + continue; + } + + switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { + case XCB_CREATE_NOTIFY: handleCreate((xcb_create_notify_event_t*)event); break; + case XCB_DESTROY_NOTIFY: handleDestroy((xcb_destroy_notify_event_t*)event); break; + case XCB_CONFIGURE_REQUEST: handleConfigure((xcb_configure_request_event_t*)event); break; + case XCB_CONFIGURE_NOTIFY: handleConfigureNotify((xcb_configure_notify_event_t*)event); break; + case XCB_MAP_REQUEST: handleMapRequest((xcb_map_request_event_t*)event); break; + case XCB_MAP_NOTIFY: handleMapNotify((xcb_map_notify_event_t*)event); break; + case XCB_UNMAP_NOTIFY: handleUnmapNotify((xcb_unmap_notify_event_t*)event); break; + case XCB_PROPERTY_NOTIFY: handlePropertyNotify((xcb_property_notify_event_t*)event); break; + case XCB_CLIENT_MESSAGE: handleClientMessage((xcb_client_message_event_t*)event); break; + case XCB_FOCUS_IN: handleFocusIn((xcb_focus_in_event_t*)event); break; + case 0: handleError((xcb_value_error_t*)event); break; + default: { + ; + } + } + free(event); + } + + if (count) + xcb_flush(connection); + + return count; +} + +void CXWM::gatherResources() { + xcb_prefetch_extension_data(connection, &xcb_xfixes_id); + xcb_prefetch_extension_data(connection, &xcb_composite_id); + xcb_prefetch_extension_data(connection, &xcb_res_id); + + for (auto& ATOM : HYPRATOMS) { + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(connection, 0, ATOM.first.length(), ATOM.first.c_str()); + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(connection, cookie, nullptr); + + if (!reply) { + Debug::log(ERR, "[xwm] Atom failed: {}", ATOM.first); + continue; + } + + ATOM.second = reply->atom; + free(reply); + } + + xfixes = xcb_get_extension_data(connection, &xcb_xfixes_id); + + if (!xfixes || !xfixes->present) + Debug::log(WARN, "XFixes not available"); + + xcb_xfixes_query_version_cookie_t xfixes_cookie; + xcb_xfixes_query_version_reply_t* xfixes_reply; + xfixes_cookie = xcb_xfixes_query_version(connection, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); + xfixes_reply = xcb_xfixes_query_version_reply(connection, xfixes_cookie, NULL); + + Debug::log(LOG, "xfixes version: {}.{}", xfixes_reply->major_version, xfixes_reply->minor_version); + xfixesMajor = xfixes_reply->major_version; + + free(xfixes_reply); + + const xcb_query_extension_reply_t* xresReply1 = xcb_get_extension_data(connection, &xcb_res_id); + if (!xresReply1 || !xresReply1->present) + return; + + xcb_res_query_version_cookie_t xres_cookie = xcb_res_query_version(connection, XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION); + xcb_res_query_version_reply_t* xres_reply = xcb_res_query_version_reply(connection, xres_cookie, NULL); + if (xres_reply == NULL) + return; + + Debug::log(LOG, "xres version: {}.{}", xres_reply->server_major, xres_reply->server_minor); + if (xres_reply->server_major > 1 || (xres_reply->server_major == 1 && xres_reply->server_minor >= 2)) { + xres = xresReply1; + } + free(xres_reply); +} + +void CXWM::getVisual() { + xcb_depth_iterator_t d_iter; + xcb_visualtype_iterator_t vt_iter; + xcb_visualtype_t* visualtype; + + d_iter = xcb_screen_allowed_depths_iterator(screen); + visualtype = NULL; + while (d_iter.rem > 0) { + if (d_iter.data->depth == 32) { + vt_iter = xcb_depth_visuals_iterator(d_iter.data); + visualtype = vt_iter.data; + break; + } + + xcb_depth_next(&d_iter); + } + + if (visualtype == NULL) { + wlr_log(WLR_DEBUG, "No 32 bit visualtype\n"); + return; + } + + visual_id = visualtype->visual_id; + colormap = xcb_generate_id(connection); + xcb_create_colormap(connection, XCB_COLORMAP_ALLOC_NONE, colormap, screen->root, visual_id); +} + +void CXWM::getRenderFormat() { + xcb_render_query_pict_formats_cookie_t cookie = xcb_render_query_pict_formats(connection); + xcb_render_query_pict_formats_reply_t* reply = xcb_render_query_pict_formats_reply(connection, cookie, NULL); + if (!reply) { + wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats"); + return; + } + xcb_render_pictforminfo_iterator_t iter = xcb_render_query_pict_formats_formats_iterator(reply); + xcb_render_pictforminfo_t* format = NULL; + while (iter.rem > 0) { + if (iter.data->depth == 32) { + format = iter.data; + break; + } + + xcb_render_pictforminfo_next(&iter); + } + + if (format == NULL) { + wlr_log(WLR_DEBUG, "No 32 bit render format"); + free(reply); + return; + } + + render_format_id = format->id; + free(reply); +} + +CXWM::CXWM() { + connection = xcb_connect_to_fd(g_pXWayland->pServer->xwmFDs[0], nullptr); + + if (int ret = xcb_connection_has_error(connection); ret) { + Debug::log(ERR, "[xwm] Couldn't start, error {}", ret); + return; + } + + if (xcb_errors_context_new(connection, &errors)) { + Debug::log(ERR, "[xwm] Couldn't allocate errors context"); + return; + } + + xcb_screen_iterator_t screen_iterator = xcb_setup_roots_iterator(xcb_get_setup(connection)); + screen = screen_iterator.data; + + eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, g_pXWayland->pServer->xwmFDs[0], WL_EVENT_READABLE, ::onX11Event, nullptr); + wl_event_source_check(eventSource); + + gatherResources(); + getVisual(); + getRenderFormat(); + + uint32_t values[] = { + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE, + }; + xcb_change_window_attributes(connection, screen->root, XCB_CW_EVENT_MASK, values); + + xcb_composite_redirect_subwindows(connection, screen->root, XCB_COMPOSITE_REDIRECT_MANUAL); + + xcb_atom_t supported[] = { + HYPRATOMS["_NET_WM_STATE"], HYPRATOMS["_NET_ACTIVE_WINDOW"], HYPRATOMS["_NET_WM_MOVERESIZE"], HYPRATOMS["_NET_WM_STATE_FOCUSED"], + HYPRATOMS["_NET_WM_STATE_MODAL"], HYPRATOMS["_NET_WM_STATE_FULLSCREEN"], HYPRATOMS["_NET_WM_STATE_MAXIMIZED_VERT"], HYPRATOMS["_NET_WM_STATE_MAXIMIZED_HORZ"], + HYPRATOMS["_NET_WM_STATE_HIDDEN"], HYPRATOMS["_NET_CLIENT_LIST"], HYPRATOMS["_NET_CLIENT_LIST_STACKING"], + }; + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_SUPPORTED"], XCB_ATOM_ATOM, 32, sizeof(supported) / sizeof(*supported), supported); + + xcb_flush(connection); + + setActiveWindow(XCB_WINDOW_NONE); + + initSelection(); + + listeners.newWLSurface = PROTO::compositor->events.newSurface.registerListener([this](std::any d) { onNewSurface(std::any_cast>(d)); }); + listeners.newXShellSurface = PROTO::xwaylandShell->events.newSurface.registerListener([this](std::any d) { onNewResource(std::any_cast>(d)); }); + + createWMWindow(); + + xcb_flush(connection); +} + +CXWM::~CXWM() { + if (errors) + xcb_errors_context_free(errors); + + if (connection) + xcb_disconnect(connection); + + if (eventSource) + wl_event_source_remove(eventSource); +} + +void CXWM::setActiveWindow(xcb_window_t window) { + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_ACTIVE_WINDOW"], HYPRATOMS["WINDOW"], 32, 1, &window); +} + +void CXWM::createWMWindow() { + constexpr const char* wmName = "Hyprland :D"; + wmWindow = xcb_generate_id(connection); + xcb_create_window(connection, XCB_COPY_FROM_PARENT, wmWindow, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, 0, NULL); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, wmWindow, HYPRATOMS["_NET_WM_NAME"], HYPRATOMS["UTF8_STRING"], + 8, // format + strlen(wmName), wmName); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_WINDOW, + 32, // format + 1, &wmWindow); + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, wmWindow, HYPRATOMS["_NET_SUPPORTING_WM_CHECK"], XCB_ATOM_WINDOW, + 32, // format + 1, &wmWindow); + xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["WM_S0"], XCB_CURRENT_TIME); + xcb_set_selection_owner(connection, wmWindow, HYPRATOMS["_NET_WM_CM_S0"], XCB_CURRENT_TIME); +} + +void CXWM::activateSurface(SP surf) { + if (surf == focusedSurface || (surf && surf->overrideRedirect)) + return; + + setActiveWindow(surf ? surf->xID : (uint32_t)XCB_WINDOW_NONE); + + focusWindow(surf); + + xcb_flush(connection); +} + +void CXWM::sendState(SP surf) { + if (surf->withdrawn) { + xcb_delete_property(connection, surf->xID, HYPRATOMS["_NET_WM_STATE"]); + return; + } + + std::vector props; + if (surf->modal) + props.push_back(HYPRATOMS["_NET_WM_STATE_MODAL"]); + if (surf->fullscreen) + props.push_back(HYPRATOMS["_NET_WM_STATE_FULLSCREEN"]); + if (surf->maximized) { + props.push_back(HYPRATOMS["NET_WM_STATE_MAXIMIZED_VERT"]); + props.push_back(HYPRATOMS["NET_WM_STATE_MAXIMIZED_HORZ"]); + } + if (surf->minimized) + props.push_back(HYPRATOMS["_NET_WM_STATE_HIDDEN"]); + if (surf == focusedSurface) + props.push_back(HYPRATOMS["_NET_WM_STATE_FOCUSED"]); + + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, surf->xID, HYPRATOMS["_NET_WM_STATE"], XCB_ATOM_ATOM, 32, props.size(), props.data()); +} + +void CXWM::onNewSurface(SP surf) { + if (surf->client() != g_pXWayland->pServer->xwaylandClient) + return; + + Debug::log(LOG, "[xwm] New XWayland surface at {:x}", (uintptr_t)surf); + + const auto WLID = surf->id(); + + for (auto& sr : surfaces) { + if (sr->surface || sr->wlID != WLID) + continue; + + associate(sr, surf); + return; + } + + Debug::log(WARN, "[xwm] CXWM::onNewSurface: no matching xwaylandSurface"); +} + +void CXWM::onNewResource(SP resource) { + Debug::log(LOG, "[xwm] New XWayland resource at {:x}", (uintptr_t)resource.get()); + + std::erase_if(shellResources, [](const auto& e) { return e.expired(); }); + shellResources.push_back(resource); + + for (auto& surf : surfaces) { + if (surf->resource || surf->wlSerial != resource->serial) + continue; + + associate(surf, resource->surface.lock()); + break; + } +} + +void CXWM::readWindowData(SP surf) { + const std::array interestingProps = { + XCB_ATOM_WM_CLASS, XCB_ATOM_WM_NAME, XCB_ATOM_WM_TRANSIENT_FOR, HYPRATOMS["WM_HINTS"], + HYPRATOMS["_NET_WM_STATE"], HYPRATOMS["_NET_WM_NAME"], HYPRATOMS["_NET_WM_WINDOW_TYPE"], HYPRATOMS["WM_NORMAL_HINTS"], + }; + + for (size_t i = 0; i < interestingProps.size(); i++) { + xcb_get_property_cookie_t cookie = xcb_get_property(connection, 0, surf->xID, interestingProps.at(i), XCB_ATOM_ANY, 0, 2048); + xcb_get_property_reply_t* reply = xcb_get_property_reply(connection, cookie, nullptr); + if (!reply) { + Debug::log(ERR, "[xwm] Failed to get window property"); + continue; + } + readProp(surf, interestingProps.at(i), reply); + free(reply); + } +} + +void CXWM::associate(SP surf, SP wlSurf) { + if (surf->surface) + return; + + auto existing = std::find_if(surfaces.begin(), surfaces.end(), [wlSurf](const auto& e) { return e->surface == wlSurf; }); + + if (existing != surfaces.end()) { + Debug::log(WARN, "[xwm] associate() called but surface is already associated to {:x}, ignoring...", (uintptr_t)surf.get()); + return; + } + + surf->surface = wlSurf; + surf->ensureListeners(); + + readWindowData(surf); + + surf->events.resourceChange.emit(); +} + +void CXWM::dissociate(SP surf) { + if (!surf->surface) + return; + + if (surf->mapped) + surf->unmap(); + + surf->surface.reset(); + surf->events.resourceChange.emit(); + + Debug::log(LOG, "Dissociate for {:x}", (uintptr_t)surf.get()); +} + +void CXWM::updateClientList() { + std::erase_if(mappedSurfaces, [](const auto& e) { return e.expired() || !e->mapped; }); + std::erase_if(mappedSurfacesStacking, [](const auto& e) { return e.expired() || !e->mapped; }); + + std::vector windows; + for (auto& m : mappedSurfaces) { + windows.push_back(m->xID); + } + + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); + + windows.clear(); + + for (auto& m : mappedSurfacesStacking) { + windows.push_back(m->xID); + } + + xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST_STACKING"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data()); +} + +bool CXWM::isWMWindow(xcb_window_t w) { + return w == wmWindow || w == clipboard.window; +} + +void CXWM::initSelection() { + clipboard.window = xcb_generate_id(connection); + uint32_t mask[1] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; + xcb_create_window(connection, XCB_COPY_FROM_PARENT, clipboard.window, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, XCB_CW_EVENT_MASK, + mask); + xcb_set_selection_owner(connection, clipboard.window, HYPRATOMS["CLIPBOARD_MANAGER"], XCB_TIME_CURRENT_TIME); + + uint32_t mask2 = + XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE; + xcb_xfixes_select_selection_input(connection, clipboard.window, HYPRATOMS["CLIPBOARD"], mask2); + + clipboard.listeners.setSelection = g_pSeatManager->events.setSelection.registerListener([this](std::any d) { clipboard.onSelection(); }); +} + +void CXWM::setClipboardToWayland(SXSelection& sel) { + sel.dataSource = makeShared(sel); + if (sel.dataSource->mimes().empty()) { + Debug::log(ERR, "[xwm] can't set clipboard: no MIMEs"); + sel.dataSource.reset(); + } + + if (sel.dataSource) { + Debug::log(LOG, "[xwm] X clipboard at {:x} takes clipboard", (uintptr_t)sel.dataSource.get()); + g_pSeatManager->setCurrentSelection(sel.dataSource); + } +} + +void CXWM::getTransferData(SXSelection& sel) { + Debug::log(LOG, "[xwm] getTransferData"); + + sel.transfer->getIncomingSelectionProp(true); + + if (sel.transfer->propertyReply->type == HYPRATOMS["INCR"]) { + Debug::log(ERR, "[xwm] Transfer is INCR, which we don't support :("); + close(sel.transfer->wlFD); + sel.transfer.reset(); + return; + } else { + char* property = (char*)xcb_get_property_value(sel.transfer->propertyReply); + int remainder = xcb_get_property_value_length(sel.transfer->propertyReply) - sel.transfer->propertyStart; + + ssize_t len = write(sel.transfer->wlFD, property + sel.transfer->propertyStart, remainder); + if (len == -1) { + Debug::log(ERR, "[xwm] write died in transfer get"); + close(sel.transfer->wlFD); + sel.transfer.reset(); + return; + } + + if (len < remainder) { + sel.transfer->propertyStart += len; + Debug::log(ERR, "[xwm] wl client read partially: len {}", len); + return; + } else { + Debug::log(LOG, "[xwm] cb transfer to wl client complete, read {} bytes", len); + close(sel.transfer->wlFD); + sel.transfer.reset(); + } + } +} + +void CXWM::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot) { + if (!render_format_id) { + Debug::log(ERR, "[xwm] can't set cursor: no render format"); + return; + } + + if (cursorXID) + xcb_free_cursor(connection, cursorXID); + + constexpr int CURSOR_DEPTH = 32; + + xcb_pixmap_t pix = xcb_generate_id(connection); + xcb_create_pixmap(connection, CURSOR_DEPTH, pix, screen->root, size.x, size.y); + + xcb_render_picture_t pic = xcb_generate_id(connection); + xcb_render_create_picture(connection, pic, pix, render_format_id, 0, 0); + + xcb_gcontext_t gc = xcb_generate_id(connection); + xcb_create_gc(connection, gc, pix, 0, NULL); + + xcb_put_image(connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, size.x, size.y, 0, 0, 0, CURSOR_DEPTH, stride * size.y * sizeof(uint8_t), pixData); + xcb_free_gc(connection, gc); + + cursorXID = xcb_generate_id(connection); + xcb_render_create_cursor(connection, cursorXID, pic, hotspot.x, hotspot.y); + xcb_free_pixmap(connection, pix); + xcb_render_free_picture(connection, pic); + + uint32_t values[] = {cursorXID}; + xcb_change_window_attributes(connection, screen->root, XCB_CW_CURSOR, values); + xcb_flush(connection); +} + +void SXSelection::onSelection() { + if (g_pSeatManager->selection.currentSelection && g_pSeatManager->selection.currentSelection->type() == DATA_SOURCE_TYPE_X11) + return; + + if (g_pSeatManager->selection.currentSelection) { + xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->clipboard.window, HYPRATOMS["CLIPBOARD"], XCB_TIME_CURRENT_TIME); + xcb_flush(g_pXWayland->pWM->connection); + } +} + +int SXSelection::onRead(int fd, uint32_t mask) { + // TODO: support INCR + + size_t pre = transfer->data.size(); + transfer->data.resize(INCR_CHUNK_SIZE + pre); + + auto len = read(fd, transfer->data.data() + pre, INCR_CHUNK_SIZE - 1); + if (len < 0) { + Debug::log(ERR, "[xwm] readDataSource died"); + g_pXWayland->pWM->selectionSendNotify(&transfer->request, false); + transfer.reset(); + return 0; + } + + transfer->data.resize(pre + len); + + if (len == 0) { + Debug::log(LOG, "[xwm] Received all the bytes, final length {}", transfer->data.size()); + xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, transfer->request.requestor, transfer->request.property, transfer->request.target, 8, + transfer->data.size(), transfer->data.data()); + xcb_flush(g_pXWayland->pWM->connection); + g_pXWayland->pWM->selectionSendNotify(&transfer->request, true); + transfer.reset(); + } else + Debug::log(LOG, "[xwm] Received {} bytes, waiting...", len); + + return 1; +} + +static int readDataSource(int fd, uint32_t mask, void* data) { + Debug::log(LOG, "[xwm] readDataSource on fd {}", fd); + + auto selection = (SXSelection*)data; + + return selection->onRead(fd, mask); +} + +bool SXSelection::sendData(xcb_selection_request_event_t* e, std::string mime) { + WP selection = g_pSeatManager->selection.currentSelection; + + if (!selection) + return false; + + const auto MIMES = selection->mimes(); + + if (MIMES.empty()) + return false; + + if (std::find(MIMES.begin(), MIMES.end(), mime) == MIMES.end()) { + Debug::log(ERR, "[xwm] X Client asked for an invalid MIME, sending the first advertised. THIS SHIT MAY BREAK!"); + mime = *MIMES.begin(); + } + + transfer = std::make_unique(*this); + transfer->request = *e; + + int p[2]; + if (pipe(p) == -1) { + Debug::log(ERR, "[xwm] selection: pipe() failed"); + return false; + } + + fcntl(p[0], F_SETFD, FD_CLOEXEC); + fcntl(p[0], F_SETFL, O_NONBLOCK); + fcntl(p[1], F_SETFD, FD_CLOEXEC); + fcntl(p[1], F_SETFL, O_NONBLOCK); + + transfer->wlFD = p[0]; + + Debug::log(LOG, "[xwm] sending wayland selection to xwayland with mime {}, target {}, fds {} {}", mime, e->target, p[0], p[1]); + + selection->send(mime, p[1]); + + transfer->eventSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, transfer->wlFD, WL_EVENT_READABLE, ::readDataSource, this); + + return true; +} + +SXTransfer::~SXTransfer() { + if (wlFD) + close(wlFD); + if (eventSource) + wl_event_source_remove(eventSource); + if (incomingWindow) + xcb_destroy_window(g_pXWayland->pWM->connection, incomingWindow); + if (propertyReply) + free(propertyReply); +} + +bool SXTransfer::getIncomingSelectionProp(bool erase) { + xcb_get_property_cookie_t cookie = xcb_get_property(g_pXWayland->pWM->connection, erase, incomingWindow, HYPRATOMS["_WL_SELECTION"], XCB_GET_PROPERTY_TYPE_ANY, 0, 0x1fffffff); + + propertyStart = 0; + propertyReply = xcb_get_property_reply(g_pXWayland->pWM->connection, cookie, nullptr); + + if (!propertyReply) { + Debug::log(ERR, "[SXTransfer] couldn't get a prop reply"); + return false; + } + + return true; +} + +#endif diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp new file mode 100644 index 00000000..bdf4fac2 --- /dev/null +++ b/src/xwayland/XWM.hpp @@ -0,0 +1,160 @@ +#pragma once + +#include "../helpers/signal/Listener.hpp" +#include "../helpers/WLListener.hpp" +#include "../macros.hpp" + +#include "XDataSource.hpp" + +#include +#include +#include +#include +#include + +struct wl_event_source; +class CXWaylandSurfaceResource; +struct SXSelection; + +struct SXTransfer { + ~SXTransfer(); + + SXSelection& selection; + bool out = true; + + bool incremental = false; + bool flushOnDelete = false; + bool propertySet = false; + + int wlFD = -1; + wl_event_source* eventSource = nullptr; + + std::vector data; + + xcb_selection_request_event_t request; + + int propertyStart; + xcb_get_property_reply_t* propertyReply; + xcb_window_t incomingWindow; + + bool getIncomingSelectionProp(bool erase); +}; + +struct SXSelection { + xcb_window_t window = 0; + xcb_window_t owner = 0; + xcb_timestamp_t timestamp = 0; + SP dataSource; + + void onSelection(); + bool sendData(xcb_selection_request_event_t* e, std::string mime); + int onRead(int fd, uint32_t mask); + + struct { + CHyprSignalListener setSelection; + } listeners; + + std::unique_ptr transfer; +}; + +class CXWM { + public: + CXWM(); + ~CXWM(); + + int onEvent(int fd, uint32_t mask); + + private: + void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot); + + void gatherResources(); + void getVisual(); + void getRenderFormat(); + void createWMWindow(); + void initSelection(); + + void onNewSurface(SP surf); + void onNewResource(SP resource); + + void setActiveWindow(xcb_window_t window); + void sendState(SP surf); + void focusWindow(SP surf); + void activateSurface(SP surf); + bool isWMWindow(xcb_window_t w); + + void sendWMMessage(SP surf, xcb_client_message_data_t* data, uint32_t mask); + + SP windowForXID(xcb_window_t wid); + + void readWindowData(SP surf); + void associate(SP surf, SP wlSurf); + void dissociate(SP surf); + + void updateClientList(); + + // event handlers + void handleCreate(xcb_create_notify_event_t* e); + void handleDestroy(xcb_destroy_notify_event_t* e); + void handleConfigure(xcb_configure_request_event_t* e); + void handleConfigureNotify(xcb_configure_notify_event_t* e); + void handleMapRequest(xcb_map_request_event_t* e); + void handleMapNotify(xcb_map_notify_event_t* e); + void handleUnmapNotify(xcb_unmap_notify_event_t* e); + void handlePropertyNotify(xcb_property_notify_event_t* e); + void handleClientMessage(xcb_client_message_event_t* e); + void handleFocusIn(xcb_focus_in_event_t* e); + void handleError(xcb_value_error_t* e); + + bool handleSelectionEvent(xcb_generic_event_t* e); + void handleSelectionNotify(xcb_selection_notify_event_t* e); + bool handleSelectionPropertyNotify(xcb_property_notify_event_t* e); + void handleSelectionRequest(xcb_selection_request_event_t* e); + bool handleSelectionXFixesNotify(xcb_xfixes_selection_notify_event_t* e); + + void selectionSendNotify(xcb_selection_request_event_t* e, bool success); + xcb_atom_t mimeToAtom(const std::string& mime); + std::string mimeFromAtom(xcb_atom_t atom); + void setClipboardToWayland(SXSelection& sel); + void getTransferData(SXSelection& sel); + void readProp(SP XSURF, uint32_t atom, xcb_get_property_reply_t* reply); + + // + xcb_connection_t* connection = nullptr; + xcb_errors_context_t* errors = nullptr; + xcb_screen_t* screen = nullptr; + + xcb_window_t wmWindow; + + wl_event_source* eventSource = nullptr; + + const xcb_query_extension_reply_t* xfixes = nullptr; + const xcb_query_extension_reply_t* xres = nullptr; + int xfixesMajor = 0; + + xcb_visualid_t visual_id; + xcb_colormap_t colormap; + uint32_t cursorXID = 0; + + xcb_render_pictformat_t render_format_id; + + std::vector> shellResources; + std::vector> surfaces; + std::vector> mappedSurfaces; // ordered by map time + std::vector> mappedSurfacesStacking; // ordered by stacking + + WP focusedSurface; + uint64_t lastFocusSeq = 0; + + SXSelection clipboard; + + struct { + CHyprSignalListener newWLSurface; + CHyprSignalListener newXShellSurface; + } listeners; + + friend class CXWaylandSurface; + friend class CXWayland; + friend class CXDataSource; + friend struct SXSelection; + friend struct SXTransfer; +}; diff --git a/src/xwayland/XWayland.cpp b/src/xwayland/XWayland.cpp new file mode 100644 index 00000000..8d45fa63 --- /dev/null +++ b/src/xwayland/XWayland.cpp @@ -0,0 +1,28 @@ +#include "XWayland.hpp" +#include "../debug/Log.hpp" + +CXWayland::CXWayland() { +#ifndef NO_XWAYLAND + Debug::log(LOG, "Starting up the XWayland server"); + + pServer = std::make_unique(); + + if (!pServer->create()) { + Debug::log(ERR, "XWayland failed to start: it will not work."); + return; + } +#else + Debug::log(LOG, "Not starting XWayland: disabled at compile time"); +#endif +} + +void CXWayland::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot) { +#ifndef NO_XWAYLAND + if (!pWM) { + Debug::log(ERR, "Couldn't set XCursor: no XWM yet"); + return; + } + + pWM->setCursor(pixData, stride, size, hotspot); +#endif +} \ No newline at end of file diff --git a/src/xwayland/XWayland.hpp b/src/xwayland/XWayland.hpp new file mode 100644 index 00000000..c7981251 --- /dev/null +++ b/src/xwayland/XWayland.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include +#include "../helpers/signal/Signal.hpp" + +#include "XSurface.hpp" + +#ifndef NO_XWAYLAND +#include "Server.hpp" +#include "XWM.hpp" +#else +class CXWaylandServer; +class CXWM; +#endif + +class CXWayland { + public: + CXWayland(); + +#ifndef NO_XWAYLAND + std::unique_ptr pServer; + std::unique_ptr pWM; +#endif + + void setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& size, const Vector2D& hotspot); + + struct { + CSignal newSurface; + } events; +}; + +inline std::unique_ptr g_pXWayland; + +#define HYPRATOM(name) \ + { name, 0 } +inline std::unordered_map HYPRATOMS = { + HYPRATOM("_NET_SUPPORTED"), + HYPRATOM("_NET_SUPPORTING_WM_CHECK"), + HYPRATOM("_NET_WM_NAME"), + HYPRATOM("_NET_WM_VISIBLE_NAME"), + HYPRATOM("_NET_WM_MOVERESIZE"), + HYPRATOM("_NET_WM_STATE_STICKY"), + HYPRATOM("_NET_WM_STATE_FULLSCREEN"), + HYPRATOM("_NET_WM_STATE_DEMANDS_ATTENTION"), + HYPRATOM("_NET_WM_STATE_MODAL"), + HYPRATOM("_NET_WM_STATE_HIDDEN"), + HYPRATOM("_NET_WM_STATE_FOCUSED"), + HYPRATOM("_NET_WM_STATE"), + HYPRATOM("_NET_WM_WINDOW_TYPE"), + HYPRATOM("_NET_WM_WINDOW_TYPE_NORMAL"), + HYPRATOM("_NET_WM_WINDOW_TYPE_DOCK"), + HYPRATOM("_NET_WM_WINDOW_TYPE_DIALOG"), + HYPRATOM("_NET_WM_WINDOW_TYPE_UTILITY"), + HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLBAR"), + HYPRATOM("_NET_WM_WINDOW_TYPE_SPLASH"), + HYPRATOM("_NET_WM_WINDOW_TYPE_MENU"), + HYPRATOM("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"), + HYPRATOM("_NET_WM_WINDOW_TYPE_POPUP_MENU"), + HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLTIP"), + HYPRATOM("_NET_WM_WINDOW_TYPE_NOTIFICATION"), + HYPRATOM("_NET_WM_WINDOW_TYPE_COMBO"), + HYPRATOM("_NET_WM_WINDOW_TYPE_DND"), + HYPRATOM("_NET_WM_WINDOW_TYPE_DESKTOP"), + HYPRATOM("_NET_WM_STATE_MAXIMIZED_HORZ"), + HYPRATOM("_NET_WM_STATE_MAXIMIZED_VERT"), + HYPRATOM("_NET_WM_DESKTOP"), + HYPRATOM("_NET_WM_STRUT_PARTIAL"), + HYPRATOM("_NET_CLIENT_LIST"), + HYPRATOM("_NET_CLIENT_LIST_STACKING"), + HYPRATOM("_NET_CURRENT_DESKTOP"), + HYPRATOM("_NET_NUMBER_OF_DESKTOPS"), + HYPRATOM("_NET_DESKTOP_NAMES"), + HYPRATOM("_NET_DESKTOP_VIEWPORT"), + HYPRATOM("_NET_ACTIVE_WINDOW"), + HYPRATOM("_NET_CLOSE_WINDOW"), + HYPRATOM("_NET_MOVERESIZE_WINDOW"), + HYPRATOM("_NET_WM_USER_TIME"), + HYPRATOM("_NET_STARTUP_ID"), + HYPRATOM("_NET_WORKAREA"), + HYPRATOM("_NET_WM_ICON"), + HYPRATOM("_NET_WM_CM_S0"), + HYPRATOM("WM_PROTOCOLS"), + HYPRATOM("WM_HINTS"), + HYPRATOM("WM_DELETE_WINDOW"), + HYPRATOM("UTF8_STRING"), + HYPRATOM("WM_STATE"), + HYPRATOM("WM_CLIENT_LEADER"), + HYPRATOM("WM_TAKE_FOCUS"), + HYPRATOM("WM_NORMAL_HINTS"), + HYPRATOM("WM_SIZE_HINTS"), + HYPRATOM("WM_WINDOW_ROLE"), + HYPRATOM("_NET_REQUEST_FRAME_EXTENTS"), + HYPRATOM("_NET_FRAME_EXTENTS"), + HYPRATOM("_MOTIF_WM_HINTS"), + HYPRATOM("WM_CHANGE_STATE"), + HYPRATOM("_NET_SYSTEM_TRAY_OPCODE"), + HYPRATOM("_NET_SYSTEM_TRAY_COLORS"), + HYPRATOM("_NET_SYSTEM_TRAY_VISUAL"), + HYPRATOM("_NET_SYSTEM_TRAY_ORIENTATION"), + HYPRATOM("_XEMBED_INFO"), + HYPRATOM("MANAGER"), + HYPRATOM("XdndSelection"), + HYPRATOM("XdndAware"), + HYPRATOM("XdndStatus"), + HYPRATOM("XdndPosition"), + HYPRATOM("XdndEnter"), + HYPRATOM("XdndLeave"), + HYPRATOM("XdndDrop"), + HYPRATOM("XdndFinished"), + HYPRATOM("XdndProxy"), + HYPRATOM("XdndTypeList"), + HYPRATOM("XdndActionMove"), + HYPRATOM("XdndActionCopy"), + HYPRATOM("XdndActionAsk"), + HYPRATOM("XdndActionPrivate"), + HYPRATOM("CLIPBOARD"), + HYPRATOM("PRIMARY"), + HYPRATOM("_WL_SELECTION"), + HYPRATOM("CLIPBOARD_MANAGER"), + HYPRATOM("WINDOW"), + HYPRATOM("WM_S0"), + HYPRATOM("WL_SURFACE_ID"), + HYPRATOM("WL_SURFACE_SERIAL"), + HYPRATOM("TARGETS"), + HYPRATOM("TIMESTAMP"), + HYPRATOM("DELETE"), + HYPRATOM("TEXT"), + HYPRATOM("INCR"), +}; \ No newline at end of file diff --git a/subprojects/wlroots-hyprland b/subprojects/wlroots-hyprland index a336b9b1..91de8da4 160000 --- a/subprojects/wlroots-hyprland +++ b/subprojects/wlroots-hyprland @@ -1 +1 @@ -Subproject commit a336b9b1fb415433e849de002df68c45034d0419 +Subproject commit 91de8da4b6b9b3c5630123d2446cd6de4e80071a