Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
add_subdirectory(tools)
|
||||
|
||||
# CMake has a known bug where target_source() doesn't work as expected with files generated
|
||||
# in a directory other than the one where the target is defined. It should be fixed in 3.20.
|
||||
add_library(WaylandProtocols_xml OBJECT)
|
||||
set_property(TARGET WaylandProtocols_xml PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(WaylandProtocols_xml Qt::Core Wayland::Server)
|
||||
target_link_libraries(kwin PRIVATE WaylandProtocols_xml)
|
||||
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${Wayland_DATADIR}/wayland.xml
|
||||
BASENAME wayland
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml
|
||||
BASENAME kde-output-device-v2
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml
|
||||
BASENAME kde-output-management-v2
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/plasma-shell.xml
|
||||
BASENAME plasma-shell
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/plasma-virtual-desktop.xml
|
||||
BASENAME org-kde-plasma-virtual-desktop
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/plasma-window-management.xml
|
||||
BASENAME plasma-window-management
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/idle.xml
|
||||
BASENAME idle
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/fake-input.xml
|
||||
BASENAME fake-input
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/shadow.xml
|
||||
BASENAME shadow
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/dpms.xml
|
||||
BASENAME dpms
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/blur.xml
|
||||
BASENAME blur
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/contrast.xml
|
||||
BASENAME contrast
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml
|
||||
BASENAME relative-pointer-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/slide.xml
|
||||
BASENAME slide
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/server-decoration.xml
|
||||
BASENAME server-decoration
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v1.xml
|
||||
BASENAME text-input-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/text-input-unstable-v2.xml
|
||||
BASENAME text-input-unstable-v2
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/text-input/text-input-unstable-v3.xml
|
||||
BASENAME text-input-unstable-v3
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/pointer-gestures/pointer-gestures-unstable-v1.xml
|
||||
BASENAME pointer-gestures-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml
|
||||
BASENAME pointer-constraints-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml
|
||||
BASENAME xdg-foreign-unstable-v2
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
|
||||
BASENAME idle-inhibit-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/appmenu.xml
|
||||
BASENAME appmenu
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/server-decoration-palette.xml
|
||||
BASENAME server-decoration-palette
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/xdg-output/xdg-output-unstable-v1.xml
|
||||
BASENAME xdg-output-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml
|
||||
BASENAME xdg-shell
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml
|
||||
BASENAME xdg-decoration-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/keystate.xml
|
||||
BASENAME keystate
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml
|
||||
BASENAME linux-dmabuf-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/tablet/tablet-unstable-v2.xml
|
||||
BASENAME tablet-unstable-v2
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/wlr-data-control-unstable-v1.xml
|
||||
BASENAME wlr-data-control-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
|
||||
BASENAME wlr-layer-shell-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml
|
||||
BASENAME keyboard-shortcuts-inhibit-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/stable/viewporter/viewporter.xml
|
||||
BASENAME viewporter
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/primary-selection/primary-selection-unstable-v1.xml
|
||||
BASENAME wp-primary-selection-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/zkde-screencast-unstable-v1.xml
|
||||
BASENAME zkde-screencast-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/input-method/input-method-unstable-v1.xml
|
||||
BASENAME input-method-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
|
||||
BASENAME xdg-activation-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/drm-lease/drm-lease-v1.xml
|
||||
BASENAME drm-lease-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-lockscreen-overlay-v1.xml
|
||||
BASENAME kde-lockscreen-overlay-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/ext-idle-notify/ext-idle-notify-v1.xml
|
||||
BASENAME ext-idle-notify-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/tearing-control/tearing-control-v1.xml
|
||||
BASENAME tearing-control-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/xwayland-keyboard-grab/xwayland-keyboard-grab-unstable-v1.xml
|
||||
BASENAME xwayland-keyboard-grab-unstable-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/content-type/content-type-v1.xml
|
||||
BASENAME content-type-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xwayland-shell/xwayland-shell-v1.xml
|
||||
BASENAME xwayland-shell-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-order-v1.xml
|
||||
BASENAME kde-output-order-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
|
||||
BASENAME fractional-scale-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/wayland-drm.xml
|
||||
BASENAME drm
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-screen-edge-v1.xml
|
||||
BASENAME kde-screen-edge-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/cursor-shape/cursor-shape-v1.xml
|
||||
BASENAME cursor-shape-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml
|
||||
BASENAME xdg-toplevel-drag-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/frog-color-management-v1.xml
|
||||
BASENAME frog-color-management-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/stable/presentation-time/presentation-time.xml
|
||||
BASENAME presentation-time
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
|
||||
BASENAME security-context-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/color-management-v1.xml
|
||||
BASENAME color-management-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
|
||||
BASENAME xdg-dialog-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml
|
||||
BASENAME linux-drm-syncobj-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-external-brightness-v1.xml
|
||||
BASENAME kde-external-brightness-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/alpha-modifier/alpha-modifier-v1.xml
|
||||
BASENAME alpha-modifier-v1
|
||||
)
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-system-bell/xdg-system-bell-v1.xml
|
||||
BASENAME xdg-system-bell-v1
|
||||
)
|
||||
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PRIVATE_CODE
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml
|
||||
BASENAME xdg-toplevel-icon-v1
|
||||
)
|
||||
|
||||
target_sources(kwin PRIVATE
|
||||
abstract_data_source.cpp
|
||||
abstract_drop_handler.cpp
|
||||
alphamodifier_v1.cpp
|
||||
appmenu.cpp
|
||||
blur.cpp
|
||||
clientconnection.cpp
|
||||
colormanagement_v1.cpp
|
||||
compositor.cpp
|
||||
contenttype_v1.cpp
|
||||
contrast.cpp
|
||||
cursorshape_v1.cpp
|
||||
datacontroldevice_v1.cpp
|
||||
datacontroldevicemanager_v1.cpp
|
||||
datacontroloffer_v1.cpp
|
||||
datacontrolsource_v1.cpp
|
||||
datadevice.cpp
|
||||
datadevicemanager.cpp
|
||||
dataoffer.cpp
|
||||
datasource.cpp
|
||||
display.cpp
|
||||
dpms.cpp
|
||||
drmclientbuffer.cpp
|
||||
drmlease_v1.cpp
|
||||
externalbrightness_v1.cpp
|
||||
filtered_display.cpp
|
||||
fixes.cpp
|
||||
fractionalscale_v1.cpp
|
||||
frog_colormanagement_v1.cpp
|
||||
idle.cpp
|
||||
idleinhibit_v1.cpp
|
||||
idlenotify_v1.cpp
|
||||
inputmethod_v1.cpp
|
||||
keyboard.cpp
|
||||
keyboard_shortcuts_inhibit_v1.cpp
|
||||
keystate.cpp
|
||||
layershell_v1.cpp
|
||||
linux_drm_syncobj_v1.cpp
|
||||
linuxdmabufv1clientbuffer.cpp
|
||||
lockscreen_overlay_v1.cpp
|
||||
output.cpp
|
||||
output_order_v1.cpp
|
||||
outputdevice_v2.cpp
|
||||
outputmanagement_v2.cpp
|
||||
plasmashell.cpp
|
||||
plasmavirtualdesktop.cpp
|
||||
plasmawindowmanagement.cpp
|
||||
pointer.cpp
|
||||
pointerconstraints_v1.cpp
|
||||
pointergestures_v1.cpp
|
||||
presentationtime.cpp
|
||||
primaryselectiondevice_v1.cpp
|
||||
primaryselectiondevicemanager_v1.cpp
|
||||
primaryselectionoffer_v1.cpp
|
||||
primaryselectionsource_v1.cpp
|
||||
region.cpp
|
||||
relativepointer_v1.cpp
|
||||
screencast_v1.cpp
|
||||
screenedge_v1.cpp
|
||||
seat.cpp
|
||||
securitycontext_v1.cpp
|
||||
server_decoration.cpp
|
||||
server_decoration_palette.cpp
|
||||
shadow.cpp
|
||||
shmclientbuffer.cpp
|
||||
slide.cpp
|
||||
subcompositor.cpp
|
||||
surface.cpp
|
||||
tablet_v2.cpp
|
||||
tearingcontrol_v1.cpp
|
||||
textinput.cpp
|
||||
textinput_v1.cpp
|
||||
textinput_v2.cpp
|
||||
textinput_v3.cpp
|
||||
touch.cpp
|
||||
transaction.cpp
|
||||
viewporter.cpp
|
||||
xdgactivation_v1.cpp
|
||||
xdgdecoration_v1.cpp
|
||||
xdgdialog_v1.cpp
|
||||
xdgforeign_v2.cpp
|
||||
xdgoutput_v1.cpp
|
||||
xdgshell.cpp
|
||||
xdgsystembell_v1.cpp
|
||||
xdgtopleveldrag_v1.cpp
|
||||
xdgtoplevelicon_v1.cpp
|
||||
xwaylandkeyboardgrab_v1.cpp
|
||||
xwaylandshell_v1.cpp
|
||||
)
|
||||
|
||||
install(FILES
|
||||
alphamodifier_v1.h
|
||||
appmenu.h
|
||||
blur.h
|
||||
clientconnection.h
|
||||
colormanagement_v1.h
|
||||
compositor.h
|
||||
contenttype_v1.h
|
||||
contrast.h
|
||||
cursorshape_v1.h
|
||||
datacontroldevice_v1.h
|
||||
datacontroldevicemanager_v1.h
|
||||
datacontroloffer_v1.h
|
||||
datacontrolsource_v1.h
|
||||
datadevice.h
|
||||
datadevicemanager.h
|
||||
dataoffer.h
|
||||
datasource.h
|
||||
display.h
|
||||
dpms.h
|
||||
drmlease_v1.h
|
||||
externalbrightness_v1.h
|
||||
fractionalscale_v1.h
|
||||
frog_colormanagement_v1.h
|
||||
idle.h
|
||||
idleinhibit_v1.h
|
||||
idlenotify_v1.h
|
||||
inputmethod_v1.h
|
||||
keyboard.h
|
||||
keyboard_shortcuts_inhibit_v1.h
|
||||
keystate.h
|
||||
layershell_v1.h
|
||||
linux_drm_syncobj_v1.h
|
||||
lockscreen_overlay_v1.h
|
||||
output.h
|
||||
output_order_v1.h
|
||||
outputdevice_v2.h
|
||||
outputmanagement_v2.h
|
||||
plasmashell.h
|
||||
plasmavirtualdesktop.h
|
||||
plasmawindowmanagement.h
|
||||
pointer.h
|
||||
pointerconstraints_v1.h
|
||||
pointergestures_v1.h
|
||||
presentationtime.h
|
||||
primaryselectiondevice_v1.h
|
||||
primaryselectiondevicemanager_v1.h
|
||||
primaryselectionoffer_v1.h
|
||||
primaryselectionsource_v1.h
|
||||
quirks.h
|
||||
relativepointer_v1.h
|
||||
screencast_v1.h
|
||||
screenedge_v1.h
|
||||
seat.h
|
||||
securitycontext_v1.h
|
||||
server_decoration.h
|
||||
server_decoration_palette.h
|
||||
shadow.h
|
||||
slide.h
|
||||
subcompositor.h
|
||||
surface.h
|
||||
tablet_v2.h
|
||||
tearingcontrol_v1.h
|
||||
textinput_v1.h
|
||||
textinput_v2.h
|
||||
textinput_v3.h
|
||||
touch.h
|
||||
viewporter.h
|
||||
xdgactivation_v1.h
|
||||
xdgdecoration_v1.h
|
||||
xdgdialog_v1.h
|
||||
xdgforeign_v2.h
|
||||
xdgoutput_v1.h
|
||||
xdgshell.h
|
||||
xdgsystembell_v1.h
|
||||
xdgtoplevelicon_v1.h
|
||||
xwaylandkeyboardgrab_v1.h
|
||||
xwaylandshell_v1.h
|
||||
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-alpha-modifier-v1.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-color-management-v1.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-content-type-v1.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-frog-color-management-v1.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-kde-external-brightness-v1.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-linux-drm-syncobj-v1.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-presentation-time.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-alpha-modifier-v1-server-protocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-color-management-v1-server-protocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-content-type-v1-server-protocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-frog-color-management-v1-server-protocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-kde-external-brightness-v1-server-protocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-linux-drm-syncobj-v1-server-protocol.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/wayland-presentation-time-server-protocol.h
|
||||
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kwin/wayland COMPONENT Devel)
|
||||
@@ -0,0 +1,84 @@
|
||||
# History
|
||||
|
||||
We started out with one method of generting classes. We then ported to a new approach of using QtWaylandScanner to reduce a lot of boiler plate.
|
||||
|
||||
New classes should use the new approach.
|
||||
|
||||
# New Approach
|
||||
|
||||
A public facing PIMPL class which should inherit from QObject.
|
||||
A private class that should inherit from QtWaylandServer::interface_name which is auto-generated. This will manage individual resources, handle callbacks and QString conversions.
|
||||
|
||||
Class Names should map to the interface name in UpperCamelCase.
|
||||
Where a V1 exists in the interface name, this should be mirrored in the file and class name.
|
||||
|
||||
An implementation should handle all versions of a given interface, but not different interfaces which represent different versions.
|
||||
|
||||
(i.e zxdg_output_manager_v1 versions 1 2 and 3 would be wrapped in one class XdgOutputManagerV1Interface. A zxdg_output_manager_v2 protocol would be exposed as a new public class XdgOutputManagerV2Interface)
|
||||
|
||||
# Implementations
|
||||
|
||||
There are 3 modes of operation happening within the exported classes
|
||||
|
||||
The generated classes can behave in all these modes, it is up to our implementation to use the correct methods.
|
||||
|
||||
## Globals
|
||||
e.g BlurManager
|
||||
|
||||
This represents an object listed by the Display class.
|
||||
Use the interface_name::(wl_display*, int version) constructor within the private class to create an instance.
|
||||
|
||||
|
||||
## Server-managed multicasting resources
|
||||
e.g XdgOutput
|
||||
|
||||
This is where one QObject represents multiple Resources.
|
||||
|
||||
Use the method
|
||||
```cpp
|
||||
QtWaylandServer::interface_name::add(client, id, version)
|
||||
```
|
||||
|
||||
to create a a new Resource instance managed by this object.
|
||||
|
||||
Use the event method with the wl_resource* overload to send events.
|
||||
|
||||
```cpp
|
||||
for (auto resource : resourceMap())
|
||||
{
|
||||
send_some_method(resource->handle, arg1, arg2);
|
||||
}
|
||||
```
|
||||
|
||||
methods to send requests to all clients.
|
||||
|
||||
## Client-owned Resources:
|
||||
|
||||
e.g BlurInterface
|
||||
|
||||
This is where one instance of our public class represents a single resource. Typically the lifespan of the exported class matches our resource.
|
||||
|
||||
In the private class use the QtWaylandServer::interface_name(wl_resource*) constructor to create a wrapper bound to a specific resource.
|
||||
|
||||
Use
|
||||
```cpp
|
||||
send_some_method(arg1, args2)
|
||||
```
|
||||
methods of the privateClass to send events to the resource set in the constructor
|
||||
|
||||
|
||||
## Other hooks
|
||||
|
||||
`_bind_resource` is called whenever a resource is bound. This exists for all generated classes in all the operating modes
|
||||
|
||||
`_destroy_resource` is a hook called whenever a resource has been unbound.
|
||||
Note one should not call wl_resource_destroy in this hook.
|
||||
|
||||
## Resource destructors
|
||||
|
||||
destructors (tagged with type="destructor" in the XML) are not handled specially in QtWayland it is up to the class implementation to implement the event handler and call
|
||||
```cpp
|
||||
wl_resource_destroy(resource->handle)
|
||||
```
|
||||
in the relevant method
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "abstract_data_source.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
AbstractDataSource::AbstractDataSource(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractDataSource::setKeyboardModifiers(Qt::KeyboardModifiers heldModifiers)
|
||||
{
|
||||
if (m_heldModifiers == heldModifiers) {
|
||||
return;
|
||||
}
|
||||
m_heldModifiers = heldModifiers;
|
||||
Q_EMIT keyboardModifiersChanged();
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers AbstractDataSource::keyboardModifiers() const
|
||||
{
|
||||
return m_heldModifiers;
|
||||
}
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_abstract_data_source.cpp"
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include "clientconnection.h"
|
||||
#include "datadevicemanager.h"
|
||||
|
||||
struct wl_client;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
/**
|
||||
* @brief The AbstractDataSource class abstracts the data that
|
||||
* can be transferred to another client.
|
||||
*
|
||||
* It loosely maps to DataDeviceInterface
|
||||
*/
|
||||
|
||||
// Anything related to selections are pure virtual, content relating
|
||||
// to drag and drop has a default implementation
|
||||
|
||||
class KWIN_EXPORT AbstractDataSource : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual bool isAccepted() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void accept(const QString &mimeType)
|
||||
{
|
||||
};
|
||||
virtual void requestData(const QString &mimeType, qint32 fd) = 0;
|
||||
virtual void cancel() = 0;
|
||||
|
||||
virtual QStringList mimeTypes() const = 0;
|
||||
|
||||
/**
|
||||
* @returns The Drag and Drop actions supported by this DataSourceInterface.
|
||||
*/
|
||||
virtual DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const
|
||||
{
|
||||
return {};
|
||||
};
|
||||
virtual DataDeviceManagerInterface::DnDAction selectedDndAction() const
|
||||
{
|
||||
return DataDeviceManagerInterface::DnDAction::None;
|
||||
}
|
||||
/**
|
||||
* The user performed the drop action during a drag and drop operation.
|
||||
*/
|
||||
virtual void dropPerformed()
|
||||
{
|
||||
m_dndDropped = true;
|
||||
}
|
||||
/**
|
||||
* The drop destination finished interoperating with this data source.
|
||||
*/
|
||||
virtual void dndFinished()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* This event indicates the @p action selected by the compositor after matching the
|
||||
* source/destination side actions. Only one action (or none) will be offered here.
|
||||
*/
|
||||
virtual void dndAction(DataDeviceManagerInterface::DnDAction action)
|
||||
{
|
||||
};
|
||||
|
||||
bool isDndCancelled() const
|
||||
{
|
||||
return m_dndCancelled;
|
||||
}
|
||||
|
||||
bool isDropPerformed() const
|
||||
{
|
||||
return m_dndDropped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a user stops clicking but it is not accepted by a client.
|
||||
*/
|
||||
virtual void dndCancelled()
|
||||
{
|
||||
m_dndCancelled = true;
|
||||
}
|
||||
|
||||
virtual wl_client *client() const
|
||||
{
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from kwin core code, this updates the keyboard modifiers currently pressed
|
||||
* which can be used to determine the best DND action
|
||||
*/
|
||||
void setKeyboardModifiers(Qt::KeyboardModifiers heldModifiers);
|
||||
Qt::KeyboardModifiers keyboardModifiers() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void aboutToBeDestroyed();
|
||||
|
||||
void mimeTypeOffered(const QString &);
|
||||
void supportedDragAndDropActionsChanged();
|
||||
void keyboardModifiersChanged();
|
||||
void dndActionChanged();
|
||||
void acceptedChanged();
|
||||
|
||||
protected:
|
||||
explicit AbstractDataSource(QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
Qt::KeyboardModifiers m_heldModifiers;
|
||||
bool m_dndCancelled = false;
|
||||
bool m_dndDropped = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "abstract_drop_handler.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
AbstractDropHandler::AbstractDropHandler(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_abstract_drop_handler.cpp"
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class SurfaceInterface;
|
||||
|
||||
class KWIN_EXPORT AbstractDropHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AbstractDropHandler(QObject *parent = nullptr);
|
||||
virtual void updateDragTarget(SurfaceInterface *surface, quint32 serial) = 0;
|
||||
virtual void drop() = 0;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "alphamodifier_v1.h"
|
||||
|
||||
#include "display.h"
|
||||
#include "surface.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_version = 1;
|
||||
|
||||
AlphaModifierManagerV1::AlphaModifierManagerV1(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWaylandServer::wp_alpha_modifier_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void AlphaModifierManagerV1::wp_alpha_modifier_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void AlphaModifierManagerV1::wp_alpha_modifier_v1_get_surface(Resource *resource, uint32_t id, ::wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *surf = SurfaceInterface::get(surface);
|
||||
SurfaceInterfacePrivate *priv = SurfaceInterfacePrivate::get(surf);
|
||||
if (priv->alphaModifier) {
|
||||
wl_resource_post_error(surface, error_already_constructed, "wl_surface already has an alpha modifier surface");
|
||||
return;
|
||||
}
|
||||
new AlphaModifierSurfaceV1(resource->client(), id, resource->version(), surf);
|
||||
}
|
||||
|
||||
AlphaModifierSurfaceV1::AlphaModifierSurfaceV1(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface)
|
||||
: QtWaylandServer::wp_alpha_modifier_surface_v1(client, id, version)
|
||||
, m_surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
AlphaModifierSurfaceV1::~AlphaModifierSurfaceV1()
|
||||
{
|
||||
if (m_surface) {
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->alphaMultiplier = 1;
|
||||
priv->pending->alphaMultiplierIsSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
void AlphaModifierSurfaceV1::wp_alpha_modifier_surface_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void AlphaModifierSurfaceV1::wp_alpha_modifier_surface_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void AlphaModifierSurfaceV1::wp_alpha_modifier_surface_v1_set_multiplier(Resource *resource, uint32_t factor)
|
||||
{
|
||||
if (!m_surface) {
|
||||
wl_resource_post_error(resource->handle, error_no_surface, "wl_surface was destroyed before a set_multiplier request");
|
||||
return;
|
||||
}
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->alphaMultiplier = factor / double(std::numeric_limits<uint32_t>::max());
|
||||
priv->pending->alphaMultiplierIsSet = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
#include "wayland/qwayland-server-alpha-modifier-v1.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
|
||||
class KWIN_EXPORT AlphaModifierManagerV1 : public QObject, private QtWaylandServer::wp_alpha_modifier_v1
|
||||
{
|
||||
public:
|
||||
explicit AlphaModifierManagerV1(Display *display, QObject *parent);
|
||||
|
||||
private:
|
||||
void wp_alpha_modifier_v1_destroy(Resource *resource) override;
|
||||
void wp_alpha_modifier_v1_get_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override;
|
||||
};
|
||||
|
||||
class AlphaModifierSurfaceV1 : private QtWaylandServer::wp_alpha_modifier_surface_v1
|
||||
{
|
||||
public:
|
||||
explicit AlphaModifierSurfaceV1(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface);
|
||||
~AlphaModifierSurfaceV1() override;
|
||||
|
||||
private:
|
||||
void wp_alpha_modifier_surface_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_alpha_modifier_surface_v1_destroy(Resource *resource) override;
|
||||
void wp_alpha_modifier_surface_v1_set_multiplier(Resource *resource, uint32_t factor) override;
|
||||
|
||||
const QPointer<SurfaceInterface> m_surface;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 David Edmundson <kde@davidedmundson.co.uk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "appmenu.h"
|
||||
#include "display.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "qwayland-server-appmenu.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 2;
|
||||
|
||||
class AppMenuManagerInterfacePrivate : public QtWaylandServer::org_kde_kwin_appmenu_manager
|
||||
{
|
||||
public:
|
||||
AppMenuManagerInterfacePrivate(AppMenuManagerInterface *q, Display *d);
|
||||
|
||||
QList<AppMenuInterface *> appmenus;
|
||||
AppMenuManagerInterface *q;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_appmenu_manager_release(Resource *resource) override;
|
||||
void org_kde_kwin_appmenu_manager_create(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
};
|
||||
|
||||
void AppMenuManagerInterfacePrivate::org_kde_kwin_appmenu_manager_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void AppMenuManagerInterfacePrivate::org_kde_kwin_appmenu_manager_create(Resource *resource, uint32_t id, wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *s = SurfaceInterface::get(surface);
|
||||
if (!s) {
|
||||
wl_resource_post_error(resource->handle, 0, "Invalid surface");
|
||||
return;
|
||||
}
|
||||
wl_resource *appmenu_resource = wl_resource_create(resource->client(), &org_kde_kwin_appmenu_interface, resource->version(), id);
|
||||
if (!appmenu_resource) {
|
||||
wl_client_post_no_memory(resource->client());
|
||||
return;
|
||||
}
|
||||
auto appmenu = new AppMenuInterface(s, appmenu_resource);
|
||||
|
||||
appmenus.append(appmenu);
|
||||
QObject::connect(appmenu, &QObject::destroyed, q, [=, this]() {
|
||||
appmenus.removeOne(appmenu);
|
||||
});
|
||||
Q_EMIT q->appMenuCreated(appmenu);
|
||||
}
|
||||
|
||||
AppMenuManagerInterfacePrivate::AppMenuManagerInterfacePrivate(AppMenuManagerInterface *_q, Display *d)
|
||||
: QtWaylandServer::org_kde_kwin_appmenu_manager(*d, s_version)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
class AppMenuInterfacePrivate : public QtWaylandServer::org_kde_kwin_appmenu
|
||||
{
|
||||
public:
|
||||
AppMenuInterfacePrivate(AppMenuInterface *q, SurfaceInterface *surface, wl_resource *resource);
|
||||
|
||||
AppMenuInterface *q;
|
||||
QPointer<SurfaceInterface> surface;
|
||||
AppMenuInterface::InterfaceAddress address;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_appmenu_destroy_resource(Resource *resource) override;
|
||||
void org_kde_kwin_appmenu_set_address(Resource *resource, const QString &service_name, const QString &object_path) override;
|
||||
void org_kde_kwin_appmenu_release(Resource *resource) override;
|
||||
};
|
||||
|
||||
AppMenuInterfacePrivate::AppMenuInterfacePrivate(AppMenuInterface *_q, SurfaceInterface *s, wl_resource *resource)
|
||||
: QtWaylandServer::org_kde_kwin_appmenu(resource)
|
||||
, q(_q)
|
||||
, surface(s)
|
||||
{
|
||||
}
|
||||
|
||||
void AppMenuInterfacePrivate::org_kde_kwin_appmenu_destroy_resource(QtWaylandServer::org_kde_kwin_appmenu::Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
void AppMenuInterfacePrivate::org_kde_kwin_appmenu_set_address(Resource *resource, const QString &service_name, const QString &object_path)
|
||||
{
|
||||
if (address.serviceName == service_name && address.objectPath == object_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
address.serviceName = service_name;
|
||||
address.objectPath = object_path;
|
||||
Q_EMIT q->addressChanged(address);
|
||||
}
|
||||
|
||||
void AppMenuInterfacePrivate::org_kde_kwin_appmenu_release(QtWaylandServer::org_kde_kwin_appmenu::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
AppMenuManagerInterface::AppMenuManagerInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new AppMenuManagerInterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
AppMenuManagerInterface::~AppMenuManagerInterface()
|
||||
{
|
||||
}
|
||||
|
||||
AppMenuInterface *AppMenuManagerInterface::appMenuForSurface(SurfaceInterface *surface)
|
||||
{
|
||||
for (AppMenuInterface *menu : d->appmenus) {
|
||||
if (menu->surface() == surface) {
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AppMenuInterface::AppMenuInterface(SurfaceInterface *surface, wl_resource *resource)
|
||||
: QObject()
|
||||
, d(new AppMenuInterfacePrivate(this, surface, resource))
|
||||
{
|
||||
}
|
||||
|
||||
AppMenuInterface::~AppMenuInterface()
|
||||
{
|
||||
}
|
||||
|
||||
AppMenuInterface::InterfaceAddress AppMenuInterface::address() const
|
||||
{
|
||||
return d->address;
|
||||
}
|
||||
|
||||
SurfaceInterface *AppMenuInterface::surface() const
|
||||
{
|
||||
return d->surface.data();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 David Edmundson <kde@davidedmundson.co.uk>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
class AppMenuInterface;
|
||||
|
||||
class AppMenuManagerInterfacePrivate;
|
||||
class AppMenuInterfacePrivate;
|
||||
|
||||
/**
|
||||
* Provides the DBus service name and object path to a AppMenu DBus interface.
|
||||
*
|
||||
* This global can be used for clients to bind AppmenuInterface instances
|
||||
* and notifies when a new one is created
|
||||
*/
|
||||
class KWIN_EXPORT AppMenuManagerInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AppMenuManagerInterface(Display *display, QObject *parent = nullptr);
|
||||
~AppMenuManagerInterface() override;
|
||||
/**
|
||||
* Returns any existing appMenu for a given surface
|
||||
* This returns a null pointer if no AppMenuInterface exists.
|
||||
*/
|
||||
AppMenuInterface *appMenuForSurface(SurfaceInterface *);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted whenever a new AppmenuInterface is created.
|
||||
*/
|
||||
void appMenuCreated(KWin::AppMenuInterface *);
|
||||
|
||||
private:
|
||||
std::unique_ptr<AppMenuManagerInterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides the DBus service name and object path to a AppMenu DBus interface.
|
||||
* This interface is attached to a wl_surface and provides access to where
|
||||
* the AppMenu DBus interface is registered.
|
||||
*/
|
||||
class KWIN_EXPORT AppMenuInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Structure containing DBus service name and path
|
||||
*/
|
||||
struct InterfaceAddress
|
||||
{
|
||||
/** Service name of host with the AppMenu object*/
|
||||
QString serviceName;
|
||||
/** Object path of the AppMenu interface*/
|
||||
QString objectPath;
|
||||
};
|
||||
~AppMenuInterface() override;
|
||||
|
||||
/**
|
||||
* @returns the service name and object path or empty strings if unset
|
||||
*/
|
||||
InterfaceAddress address() const;
|
||||
|
||||
/**
|
||||
* @returns The SurfaceInterface this AppmenuInterface references.
|
||||
*/
|
||||
SurfaceInterface *surface() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the address changes or is first received
|
||||
*/
|
||||
void addressChanged(KWin::AppMenuInterface::InterfaceAddress);
|
||||
|
||||
private:
|
||||
explicit AppMenuInterface(SurfaceInterface *s, wl_resource *resource);
|
||||
friend class AppMenuManagerInterfacePrivate;
|
||||
|
||||
std::unique_ptr<AppMenuInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "blur.h"
|
||||
#include "display.h"
|
||||
#include "region_p.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
#include "qwayland-server-blur.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
class BlurManagerInterfacePrivate : public QtWaylandServer::org_kde_kwin_blur_manager
|
||||
{
|
||||
public:
|
||||
BlurManagerInterfacePrivate(BlurManagerInterface *q, Display *d);
|
||||
|
||||
BlurManagerInterface *q;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_blur_manager_destroy_global() override;
|
||||
void org_kde_kwin_blur_manager_create(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
void org_kde_kwin_blur_manager_unset(Resource *resource, wl_resource *surface) override;
|
||||
};
|
||||
|
||||
BlurManagerInterfacePrivate::BlurManagerInterfacePrivate(BlurManagerInterface *_q, Display *d)
|
||||
: QtWaylandServer::org_kde_kwin_blur_manager(*d, s_version)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
void BlurManagerInterfacePrivate::org_kde_kwin_blur_manager_destroy_global()
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
void BlurManagerInterfacePrivate::org_kde_kwin_blur_manager_unset(Resource *resource, wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *s = SurfaceInterface::get(surface);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(s);
|
||||
surfacePrivate->setBlur(QPointer<BlurInterface>());
|
||||
}
|
||||
|
||||
void BlurManagerInterfacePrivate::org_kde_kwin_blur_manager_create(Resource *resource, uint32_t id, wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *s = SurfaceInterface::get(surface);
|
||||
if (!s) {
|
||||
wl_resource_post_error(resource->handle, 0, "Invalid surface");
|
||||
return;
|
||||
}
|
||||
wl_resource *blur_resource = wl_resource_create(resource->client(), &org_kde_kwin_blur_interface, resource->version(), id);
|
||||
if (!blur_resource) {
|
||||
wl_client_post_no_memory(resource->client());
|
||||
return;
|
||||
}
|
||||
auto blur = new BlurInterface(blur_resource);
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(s);
|
||||
surfacePrivate->setBlur(blur);
|
||||
}
|
||||
|
||||
BlurManagerInterface::BlurManagerInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new BlurManagerInterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
BlurManagerInterface::~BlurManagerInterface()
|
||||
{
|
||||
}
|
||||
|
||||
void BlurManagerInterface::remove()
|
||||
{
|
||||
d->globalRemove();
|
||||
}
|
||||
|
||||
class BlurInterfacePrivate : public QtWaylandServer::org_kde_kwin_blur
|
||||
{
|
||||
public:
|
||||
BlurInterfacePrivate(BlurInterface *q, wl_resource *resource);
|
||||
QRegion pendingRegion;
|
||||
QRegion currentRegion;
|
||||
|
||||
BlurInterface *q;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_blur_destroy_resource(Resource *resource) override;
|
||||
void org_kde_kwin_blur_commit(Resource *resource) override;
|
||||
void org_kde_kwin_blur_set_region(Resource *resource, wl_resource *region) override;
|
||||
void org_kde_kwin_blur_release(Resource *resource) override;
|
||||
};
|
||||
|
||||
void BlurInterfacePrivate::org_kde_kwin_blur_commit(Resource *resource)
|
||||
{
|
||||
currentRegion = pendingRegion;
|
||||
}
|
||||
|
||||
void BlurInterfacePrivate::org_kde_kwin_blur_set_region(Resource *resource, wl_resource *region)
|
||||
{
|
||||
RegionInterface *r = RegionInterface::get(region);
|
||||
if (r) {
|
||||
pendingRegion = r->region();
|
||||
} else {
|
||||
pendingRegion = QRegion();
|
||||
}
|
||||
}
|
||||
|
||||
void BlurInterfacePrivate::org_kde_kwin_blur_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void BlurInterfacePrivate::org_kde_kwin_blur_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
BlurInterfacePrivate::BlurInterfacePrivate(BlurInterface *_q, wl_resource *resource)
|
||||
: QtWaylandServer::org_kde_kwin_blur(resource)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
BlurInterface::BlurInterface(wl_resource *resource)
|
||||
: QObject()
|
||||
, d(new BlurInterfacePrivate(this, resource))
|
||||
{
|
||||
}
|
||||
|
||||
BlurInterface::~BlurInterface() = default;
|
||||
|
||||
QRegion BlurInterface::region()
|
||||
{
|
||||
return d->currentRegion;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_blur.cpp"
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class BlurManagerInterfacePrivate;
|
||||
class BlurInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Represents the Global for org_kde_kwin_blur_manager interface.
|
||||
*
|
||||
* This class creates BlurInterfaces and attaches them to SurfaceInterfaces.
|
||||
*
|
||||
* @see BlurInterface
|
||||
* @see SurfaceInterface
|
||||
*/
|
||||
class KWIN_EXPORT BlurManagerInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BlurManagerInterface(Display *display, QObject *parent = nullptr);
|
||||
~BlurManagerInterface() override;
|
||||
|
||||
void remove();
|
||||
|
||||
private:
|
||||
std::unique_ptr<BlurManagerInterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the Resource for the org_kde_kwin_blur interface.
|
||||
*
|
||||
* Instances of this class are only generated by the BlurManagerInterface.
|
||||
* The BlurInterface gets attached to a SurfaceInterface and can be assessed
|
||||
* from there using @link SurfaceInterface::blur() @endlink. Please note that
|
||||
* the BlurInterface is only available on the SurfaceInterface after it has been
|
||||
* committed.
|
||||
*
|
||||
* Lifespan matches the underlying client resource
|
||||
*
|
||||
* @see BlurManagerInterface
|
||||
* @see SurfaceInterface
|
||||
*/
|
||||
class KWIN_EXPORT BlurInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~BlurInterface() override;
|
||||
|
||||
/**
|
||||
* @returns The region or the SurfaceInterface which should be blurred, null Region implies complete surface.
|
||||
*/
|
||||
QRegion region();
|
||||
|
||||
private:
|
||||
explicit BlurInterface(wl_resource *resource);
|
||||
friend class BlurManagerInterfacePrivate;
|
||||
|
||||
std::unique_ptr<BlurInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "clientconnection.h"
|
||||
#include "display.h"
|
||||
#include "utils/executable_path.h"
|
||||
// Qt
|
||||
#include <QFileInfo>
|
||||
#include <QList>
|
||||
// Wayland
|
||||
#include <wayland-server.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ClientConnectionPrivate
|
||||
{
|
||||
public:
|
||||
ClientConnectionPrivate(wl_client *c, Display *display, ClientConnection *q);
|
||||
~ClientConnectionPrivate();
|
||||
|
||||
wl_client *client;
|
||||
Display *display;
|
||||
pid_t pid = 0;
|
||||
uid_t user = 0;
|
||||
gid_t group = 0;
|
||||
QString executablePath;
|
||||
QString securityContextAppId;
|
||||
qreal scaleOverride = 1.0;
|
||||
|
||||
private:
|
||||
static void destroyListenerCallback(wl_listener *listener, void *data);
|
||||
static void destroyLateListenerCallback(wl_listener *listener, void *data);
|
||||
ClientConnection *q;
|
||||
wl_listener destroyListener;
|
||||
wl_listener destroyLateListener;
|
||||
static QList<ClientConnectionPrivate *> s_allClients;
|
||||
};
|
||||
|
||||
QList<ClientConnectionPrivate *> ClientConnectionPrivate::s_allClients;
|
||||
|
||||
ClientConnectionPrivate::ClientConnectionPrivate(wl_client *c, Display *display, ClientConnection *q)
|
||||
: client(c)
|
||||
, display(display)
|
||||
, q(q)
|
||||
{
|
||||
s_allClients << this;
|
||||
|
||||
destroyListener.notify = destroyListenerCallback;
|
||||
wl_client_add_destroy_listener(c, &destroyListener);
|
||||
|
||||
destroyLateListener.notify = destroyLateListenerCallback;
|
||||
wl_client_add_destroy_late_listener(c, &destroyLateListener);
|
||||
|
||||
wl_client_get_credentials(client, &pid, &user, &group);
|
||||
executablePath = executablePathFromPid(pid);
|
||||
}
|
||||
|
||||
ClientConnectionPrivate::~ClientConnectionPrivate()
|
||||
{
|
||||
s_allClients.removeAt(s_allClients.indexOf(this));
|
||||
}
|
||||
|
||||
void ClientConnectionPrivate::destroyListenerCallback(wl_listener *listener, void *data)
|
||||
{
|
||||
wl_client *client = reinterpret_cast<wl_client *>(data);
|
||||
auto it = std::find_if(s_allClients.constBegin(), s_allClients.constEnd(), [client](ClientConnectionPrivate *c) {
|
||||
return c->client == client;
|
||||
});
|
||||
Q_ASSERT(it != s_allClients.constEnd());
|
||||
auto p = (*it);
|
||||
auto q = p->q;
|
||||
|
||||
Q_EMIT q->aboutToBeDestroyed();
|
||||
wl_list_remove(&p->destroyListener.link);
|
||||
Q_EMIT q->disconnected(q);
|
||||
}
|
||||
|
||||
void ClientConnectionPrivate::destroyLateListenerCallback(wl_listener *listener, void *data)
|
||||
{
|
||||
wl_client *client = reinterpret_cast<wl_client *>(data);
|
||||
auto it = std::find_if(s_allClients.constBegin(), s_allClients.constEnd(), [client](ClientConnectionPrivate *c) {
|
||||
return c->client == client;
|
||||
});
|
||||
Q_ASSERT(it != s_allClients.constEnd());
|
||||
auto p = (*it);
|
||||
auto q = p->q;
|
||||
|
||||
wl_list_remove(&p->destroyLateListener.link);
|
||||
delete q;
|
||||
}
|
||||
|
||||
ClientConnection::ClientConnection(wl_client *c, Display *parent)
|
||||
: QObject(parent)
|
||||
, d(new ClientConnectionPrivate(c, parent, this))
|
||||
{
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection() = default;
|
||||
|
||||
void ClientConnection::flush()
|
||||
{
|
||||
wl_client_flush(d->client);
|
||||
}
|
||||
|
||||
void ClientConnection::destroy()
|
||||
{
|
||||
wl_client_destroy(d->client);
|
||||
}
|
||||
|
||||
wl_resource *ClientConnection::getResource(quint32 id) const
|
||||
{
|
||||
return wl_client_get_object(d->client, id);
|
||||
}
|
||||
|
||||
wl_client *ClientConnection::client() const
|
||||
{
|
||||
return d->client;
|
||||
}
|
||||
|
||||
ClientConnection::operator wl_client *()
|
||||
{
|
||||
return d->client;
|
||||
}
|
||||
|
||||
ClientConnection::operator wl_client *() const
|
||||
{
|
||||
return d->client;
|
||||
}
|
||||
|
||||
Display *ClientConnection::display() const
|
||||
{
|
||||
return d->display;
|
||||
}
|
||||
|
||||
gid_t ClientConnection::groupId() const
|
||||
{
|
||||
return d->group;
|
||||
}
|
||||
|
||||
pid_t ClientConnection::processId() const
|
||||
{
|
||||
return d->pid;
|
||||
}
|
||||
|
||||
uid_t ClientConnection::userId() const
|
||||
{
|
||||
return d->user;
|
||||
}
|
||||
|
||||
QString ClientConnection::executablePath() const
|
||||
{
|
||||
return d->executablePath;
|
||||
}
|
||||
|
||||
void ClientConnection::setScaleOverride(qreal scaleOveride)
|
||||
{
|
||||
Q_ASSERT(scaleOveride != 0);
|
||||
d->scaleOverride = scaleOveride;
|
||||
Q_EMIT scaleOverrideChanged();
|
||||
}
|
||||
|
||||
qreal ClientConnection::scaleOverride() const
|
||||
{
|
||||
return d->scaleOverride;
|
||||
}
|
||||
|
||||
void ClientConnection::setSecurityContextAppId(const QString &appId)
|
||||
{
|
||||
d->securityContextAppId = appId;
|
||||
}
|
||||
|
||||
QString ClientConnection::securityContextAppId() const
|
||||
{
|
||||
return d->securityContextAppId;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_clientconnection.cpp"
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
struct wl_client;
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ClientConnectionPrivate;
|
||||
class Display;
|
||||
|
||||
/**
|
||||
* @brief Convenient Class which represents a wl_client.
|
||||
*
|
||||
* The ClientConnection gets automatically created for a wl_client. In particular,
|
||||
* the signal @link Display::clientConnected @endlink will be emitted.
|
||||
*
|
||||
* @see Display
|
||||
*/
|
||||
class KWIN_EXPORT ClientConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~ClientConnection();
|
||||
|
||||
/**
|
||||
* Flushes the connection to this client. Ensures that all events are pushed to the client.
|
||||
*/
|
||||
void flush();
|
||||
/**
|
||||
* Get the wl_resource associated with the given @p id.
|
||||
*/
|
||||
wl_resource *getResource(quint32 id) const;
|
||||
|
||||
/**
|
||||
* @returns the native wl_client this ClientConnection represents.
|
||||
*/
|
||||
wl_client *client() const;
|
||||
/**
|
||||
* @returns The Display this ClientConnection is connected to
|
||||
*/
|
||||
Display *display() const;
|
||||
|
||||
/**
|
||||
* The pid of the ClientConnection endpoint.
|
||||
*
|
||||
* Please note: if the ClientConnection got created with @link Display::createClient @endlink
|
||||
* the pid will be identical to the process running the KWin::Display.
|
||||
*
|
||||
* @returns The pid of the connection.
|
||||
*/
|
||||
pid_t processId() const;
|
||||
/**
|
||||
* The uid of the ClientConnection endpoint.
|
||||
*
|
||||
* Please note: if the ClientConnection got created with @link Display::createClient @endlink
|
||||
* the uid will be identical to the process running the KWin::Display.
|
||||
*
|
||||
* @returns The uid of the connection.
|
||||
*/
|
||||
uid_t userId() const;
|
||||
/**
|
||||
* The gid of the ClientConnection endpoint.
|
||||
*
|
||||
* Please note: if the ClientConnection got created with @link Display::createClient @endlink
|
||||
* the gid will be identical to the process running the KWin::Display.
|
||||
*
|
||||
* @returns The gid of the connection.
|
||||
*/
|
||||
gid_t groupId() const;
|
||||
|
||||
/**
|
||||
* The absolute path to the executable.
|
||||
*
|
||||
* Please note: if the ClientConnection got created with @link Display::createClient @endlink
|
||||
* the executablePath will be identical to the process running the KWin::Display.
|
||||
*
|
||||
* If the executable path cannot be resolved an empty QString is returned.
|
||||
*
|
||||
* @see processId
|
||||
*/
|
||||
QString executablePath() const;
|
||||
|
||||
/**
|
||||
* Cast operator the native wl_client this ClientConnection represents.
|
||||
*/
|
||||
operator wl_client *();
|
||||
/**
|
||||
* Cast operator the native wl_client this ClientConnection represents.
|
||||
*/
|
||||
operator wl_client *() const;
|
||||
|
||||
/**
|
||||
* Destroys this ClientConnection.
|
||||
* This is a convenient wrapper around wl_client_destroy. The use case is in combination
|
||||
* with ClientConnections created through @link Display::createClient @endlink. E.g. once
|
||||
* the process for the ClientConnection exited, the ClientConnection needs to be destroyed, too.
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* Set an additional mapping between kwin's logical co-ordinate space and
|
||||
* the client's logical co-ordinate space.
|
||||
*
|
||||
* This is used in the same way as if the client was setting the
|
||||
* surface.buffer_scale on every surface i.e a value of 2.0 will make
|
||||
* the windows appear smaller on a regular DPI monitor.
|
||||
*
|
||||
* Only the minimal set of protocols used by xwayland have support.
|
||||
*
|
||||
* Buffer sizes are unaffected.
|
||||
*/
|
||||
void setScaleOverride(qreal scaleOverride);
|
||||
qreal scaleOverride() const;
|
||||
|
||||
void setSecurityContextAppId(const QString &appId);
|
||||
QString securityContextAppId() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when the client is about to be destroyed.
|
||||
*/
|
||||
void aboutToBeDestroyed();
|
||||
/**
|
||||
* Signal emitted when the ClientConnection got disconnected from the server.
|
||||
*/
|
||||
void disconnected(KWin::ClientConnection *);
|
||||
|
||||
void scaleOverrideChanged();
|
||||
|
||||
private:
|
||||
friend class Display;
|
||||
explicit ClientConnection(wl_client *c, Display *parent);
|
||||
std::unique_ptr<ClientConnectionPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::ClientConnection *)
|
||||
@@ -0,0 +1,588 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "colormanagement_v1.h"
|
||||
#include "display.h"
|
||||
#include "surface.h"
|
||||
#include "surface_p.h"
|
||||
#include "utils/resource.h"
|
||||
#include "wayland/output.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
ColorManagerV1::ColorManagerV1(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWaylandServer::wp_color_manager_v1(*display, 1)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_bind_resource(Resource *resource)
|
||||
{
|
||||
send_supported_feature(resource->handle, feature::feature_parametric);
|
||||
send_supported_feature(resource->handle, feature::feature_extended_target_volume);
|
||||
send_supported_feature(resource->handle, feature::feature_set_mastering_display_primaries);
|
||||
send_supported_feature(resource->handle, feature::feature_set_primaries);
|
||||
send_supported_feature(resource->handle, feature::feature_set_luminances);
|
||||
send_supported_feature(resource->handle, feature::feature_windows_scrgb);
|
||||
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_srgb);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_pal_m);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_pal);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_ntsc);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_generic_film);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_bt2020);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_cie1931_xyz);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_dci_p3);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_display_p3);
|
||||
send_supported_primaries_named(resource->handle, primaries::primaries_adobe_rgb);
|
||||
|
||||
send_supported_tf_named(resource->handle, transfer_function::transfer_function_gamma22);
|
||||
send_supported_tf_named(resource->handle, transfer_function::transfer_function_srgb);
|
||||
send_supported_tf_named(resource->handle, transfer_function::transfer_function_st2084_pq);
|
||||
send_supported_tf_named(resource->handle, transfer_function::transfer_function_ext_linear);
|
||||
|
||||
send_supported_intent(resource->handle, render_intent::render_intent_perceptual);
|
||||
send_supported_intent(resource->handle, render_intent::render_intent_relative);
|
||||
send_supported_intent(resource->handle, render_intent::render_intent_absolute);
|
||||
send_supported_intent(resource->handle, render_intent::render_intent_relative_bpc);
|
||||
// TODO implement saturation intent
|
||||
|
||||
send_done(resource->handle);
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_get_output(Resource *resource, uint32_t id, struct ::wl_resource *output)
|
||||
{
|
||||
new ColorManagementOutputV1(resource->client(), id, resource->version(), OutputInterface::get(output)->handle());
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_get_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface)
|
||||
{
|
||||
const auto surf = SurfaceInterface::get(surface);
|
||||
const auto priv = SurfaceInterfacePrivate::get(surf);
|
||||
if (priv->colorSurface) {
|
||||
wl_resource_post_error(resource->handle, error_surface_exists, "there's already a color management surface for this wl_surface");
|
||||
return;
|
||||
}
|
||||
priv->colorSurface = new ColorSurfaceV1(resource->client(), id, resource->version(), surf);
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_get_surface_feedback(Resource *resource, uint32_t id, struct ::wl_resource *surface)
|
||||
{
|
||||
const auto surf = SurfaceInterface::get(surface);
|
||||
const auto priv = SurfaceInterfacePrivate::get(surf);
|
||||
priv->colorFeedbackSurfaces.push_back(new ColorFeedbackSurfaceV1(resource->client(), id, resource->version(), surf));
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_create_icc_creator(Resource *resource, uint32_t obj)
|
||||
{
|
||||
wl_resource_post_error(resource->handle, error::error_unsupported_feature, "ICC profiles are not supported");
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_create_parametric_creator(Resource *resource, uint32_t obj)
|
||||
{
|
||||
new ColorParametricCreatorV1(resource->client(), obj, resource->version());
|
||||
}
|
||||
|
||||
void ColorManagerV1::wp_color_manager_v1_create_windows_scrgb(Resource *resource, uint32_t image_description)
|
||||
{
|
||||
const auto scrgb = ColorDescription{
|
||||
NamedColorimetry::BT709,
|
||||
TransferFunction(TransferFunction::linear, 0, 80),
|
||||
80,
|
||||
0,
|
||||
std::nullopt,
|
||||
std::nullopt,
|
||||
Colorimetry::fromName(NamedColorimetry::BT2020),
|
||||
Colorimetry::fromName(NamedColorimetry::BT709),
|
||||
};
|
||||
new ImageDescriptionV1(resource->client(), image_description, resource->version(), scrgb);
|
||||
}
|
||||
|
||||
ColorFeedbackSurfaceV1::ColorFeedbackSurfaceV1(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface)
|
||||
: QtWaylandServer::wp_color_management_surface_feedback_v1(client, id, version)
|
||||
, m_surface(surface)
|
||||
, m_preferred(SurfaceInterfacePrivate::get(surface)->preferredColorDescription.value_or(ColorDescription::sRGB))
|
||||
{
|
||||
}
|
||||
|
||||
ColorFeedbackSurfaceV1::~ColorFeedbackSurfaceV1()
|
||||
{
|
||||
if (m_surface) {
|
||||
SurfaceInterfacePrivate::get(m_surface)->colorFeedbackSurfaces.removeOne(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorFeedbackSurfaceV1::setPreferredColorDescription(const ColorDescription &descr)
|
||||
{
|
||||
if (m_preferred != descr) {
|
||||
m_preferred = descr;
|
||||
send_preferred_changed(resource()->handle, ImageDescriptionV1::s_idCounter++);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorFeedbackSurfaceV1::wp_color_management_surface_feedback_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ColorFeedbackSurfaceV1::wp_color_management_surface_feedback_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ColorFeedbackSurfaceV1::wp_color_management_surface_feedback_v1_get_preferred(Resource *resource, uint32_t image_description)
|
||||
{
|
||||
new ImageDescriptionV1(resource->client(), image_description, resource->version(), m_preferred);
|
||||
}
|
||||
|
||||
void ColorFeedbackSurfaceV1::wp_color_management_surface_feedback_v1_get_preferred_parametric(Resource *resource, uint32_t image_description)
|
||||
{
|
||||
new ImageDescriptionV1(resource->client(), image_description, resource->version(), m_preferred);
|
||||
}
|
||||
|
||||
ColorSurfaceV1::ColorSurfaceV1(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface)
|
||||
: QtWaylandServer::wp_color_management_surface_v1(client, id, version)
|
||||
, m_surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
ColorSurfaceV1::~ColorSurfaceV1()
|
||||
{
|
||||
if (m_surface) {
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->colorDescription = ColorDescription::sRGB;
|
||||
priv->pending->colorDescriptionIsSet = true;
|
||||
priv->colorSurface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorSurfaceV1::wp_color_management_surface_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ColorSurfaceV1::wp_color_management_surface_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
static std::optional<RenderingIntent> waylandToKwinIntent(uint32_t intent)
|
||||
{
|
||||
switch (intent) {
|
||||
case QtWaylandServer::wp_color_manager_v1::render_intent::render_intent_perceptual:
|
||||
return RenderingIntent::Perceptual;
|
||||
case QtWaylandServer::wp_color_manager_v1::render_intent::render_intent_relative:
|
||||
return RenderingIntent::RelativeColorimetric;
|
||||
case QtWaylandServer::wp_color_manager_v1::render_intent::render_intent_absolute:
|
||||
return RenderingIntent::AbsoluteColorimetric;
|
||||
case QtWaylandServer::wp_color_manager_v1::render_intent::render_intent_relative_bpc:
|
||||
return RenderingIntent::RelativeColorimetricWithBPC;
|
||||
case QtWaylandServer::wp_color_manager_v1::render_intent::render_intent_saturation:
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ColorSurfaceV1::wp_color_management_surface_v1_set_image_description(Resource *resource, struct ::wl_resource *image_description, uint32_t render_intent)
|
||||
{
|
||||
if (!m_surface) {
|
||||
return;
|
||||
}
|
||||
const std::optional<RenderingIntent> intent = waylandToKwinIntent(render_intent);
|
||||
if (!intent) {
|
||||
wl_resource_post_error(resource->handle, WP_COLOR_MANAGER_V1_ERROR_UNSUPPORTED_FEATURE, "rendering intent is not supported");
|
||||
return;
|
||||
}
|
||||
const auto description = ImageDescriptionV1::get(image_description);
|
||||
if (!description->description().has_value()) {
|
||||
wl_resource_post_error(resource->handle, error_image_description, "failed image description can't be used");
|
||||
return;
|
||||
}
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->colorDescription = *ImageDescriptionV1::get(image_description)->description();
|
||||
priv->pending->renderingIntent = *intent;
|
||||
priv->pending->colorDescriptionIsSet = true;
|
||||
}
|
||||
|
||||
void ColorSurfaceV1::wp_color_management_surface_v1_unset_image_description(Resource *resource)
|
||||
{
|
||||
if (!m_surface) {
|
||||
return;
|
||||
}
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->colorDescription = ColorDescription::sRGB;
|
||||
priv->pending->colorDescriptionIsSet = true;
|
||||
}
|
||||
|
||||
ColorParametricCreatorV1::ColorParametricCreatorV1(wl_client *client, uint32_t id, uint32_t version)
|
||||
: QtWaylandServer::wp_image_description_creator_params_v1(client, id, version)
|
||||
{
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_create(Resource *resource, uint32_t image_description)
|
||||
{
|
||||
if (!m_colorimetry || !m_transferFunctionType) {
|
||||
wl_resource_post_error(resource->handle, error::error_incomplete_set, "colorimetry or transfer function missing");
|
||||
return;
|
||||
}
|
||||
|
||||
TransferFunction func{*m_transferFunctionType};
|
||||
double referenceLuminance = TransferFunction::defaultReferenceLuminanceFor(func.type);
|
||||
if (m_transferFunctionLuminances) {
|
||||
// PQ is special cased to require (max - min) == 10'000
|
||||
if (m_transferFunctionType == TransferFunction::PerceptualQuantizer) {
|
||||
func.minLuminance = m_transferFunctionLuminances->min;
|
||||
func.maxLuminance = m_transferFunctionLuminances->min + 10'000;
|
||||
} else {
|
||||
func.minLuminance = m_transferFunctionLuminances->min;
|
||||
func.maxLuminance = m_transferFunctionLuminances->max;
|
||||
}
|
||||
referenceLuminance = m_transferFunctionLuminances->reference;
|
||||
}
|
||||
|
||||
std::optional<double> maxFrameAverageLuminance = m_maxFall ? m_maxFall : m_maxMasteringLuminance;
|
||||
std::optional<double> maxHdrLuminance = m_maxCll ? m_maxCll : m_maxMasteringLuminance;
|
||||
// some applications provide truly nonsensical luminance values, like 10 million nits.
|
||||
// this is just a basic sanity check to not make use of that and instead assume HGIG values
|
||||
// which are fine for most HDR games and videos
|
||||
const bool hasSaneMetadata = m_maxFall <= 10'000 && m_maxCll <= 10'000 && m_maxMasteringLuminance <= 10'000;
|
||||
if (!hasSaneMetadata) {
|
||||
maxFrameAverageLuminance = 600;
|
||||
maxHdrLuminance = 1'000;
|
||||
m_minMasteringLuminance = func.minLuminance;
|
||||
}
|
||||
if (Colorimetry::isValid(m_colorimetry->red().toxy(), m_colorimetry->green().toxy(), m_colorimetry->blue().toxy(), m_colorimetry->white().toxy())) {
|
||||
new ImageDescriptionV1(resource->client(), image_description, resource->version(), ColorDescription(*m_colorimetry, func, referenceLuminance, m_minMasteringLuminance.value_or(func.minLuminance), maxFrameAverageLuminance, maxHdrLuminance, m_masteringColorimetry, Colorimetry::fromName(NamedColorimetry::BT709)));
|
||||
} else {
|
||||
new ImageDescriptionV1(resource->client(), image_description, resource->version(), std::nullopt);
|
||||
}
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_tf_named(Resource *resource, uint32_t tf)
|
||||
{
|
||||
if (m_transferFunctionType) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "transfer function is already set");
|
||||
return;
|
||||
}
|
||||
switch (tf) {
|
||||
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB:
|
||||
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22:
|
||||
m_transferFunctionType = TransferFunction::gamma22;
|
||||
return;
|
||||
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ:
|
||||
m_transferFunctionType = TransferFunction::PerceptualQuantizer;
|
||||
return;
|
||||
case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR:
|
||||
m_transferFunctionType = TransferFunction::linear;
|
||||
return;
|
||||
default:
|
||||
// TODO add more transfer functions
|
||||
wl_resource_post_error(resource->handle, error::error_invalid_tf, "unsupported named transfer function");
|
||||
}
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_tf_power(Resource *resource, uint32_t eexp)
|
||||
{
|
||||
wl_resource_post_error(resource->handle, error::error_invalid_tf, "power transfer functions are not supported");
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_primaries_named(Resource *resource, uint32_t primaries)
|
||||
{
|
||||
if (m_colorimetry) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "primaries are already set");
|
||||
return;
|
||||
}
|
||||
switch (primaries) {
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_srgb:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::BT709);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal_m:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::PAL_M);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::PAL);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_ntsc:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::NTSC);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_generic_film:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::GenericFilm);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_bt2020:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::BT2020);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_cie1931_xyz:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::CIEXYZ);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_dci_p3:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::DCIP3);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_display_p3:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::DisplayP3);
|
||||
return;
|
||||
case QtWaylandServer::wp_color_manager_v1::primaries::primaries_adobe_rgb:
|
||||
m_colorimetry = Colorimetry::fromName(NamedColorimetry::AdobeRGB);
|
||||
return;
|
||||
default:
|
||||
wl_resource_post_error(resource->handle, error::error_invalid_primaries_named, "unsupported named primaries");
|
||||
}
|
||||
}
|
||||
|
||||
constexpr double s_primaryUnit = 1.0 / 1'000'000.0;
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_primaries(Resource *resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y)
|
||||
{
|
||||
if (m_colorimetry) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "primaries are already set");
|
||||
return;
|
||||
}
|
||||
m_colorimetry = Colorimetry{
|
||||
xy{r_x * s_primaryUnit, r_y * s_primaryUnit},
|
||||
xy{g_x * s_primaryUnit, g_y * s_primaryUnit},
|
||||
xy{b_x * s_primaryUnit, b_y * s_primaryUnit},
|
||||
xy{w_x * s_primaryUnit, w_y * s_primaryUnit},
|
||||
};
|
||||
}
|
||||
|
||||
constexpr double s_minLuminanceUnit = 1.0 / 10'000.0;
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_luminances(Resource *resource, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum)
|
||||
{
|
||||
if (max_lum < min_lum * s_minLuminanceUnit) {
|
||||
wl_resource_post_error(resource->handle, error::error_invalid_luminance, "max. luminance is lower than the min. luminance");
|
||||
return;
|
||||
}
|
||||
if (reference_lum < min_lum * s_minLuminanceUnit) {
|
||||
wl_resource_post_error(resource->handle, error::error_invalid_luminance, "reference luminance is lower than the min. luminance");
|
||||
return;
|
||||
}
|
||||
m_transferFunctionLuminances = Luminances{
|
||||
.min = min_lum * s_minLuminanceUnit,
|
||||
.max = double(max_lum),
|
||||
.reference = double(reference_lum),
|
||||
};
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_mastering_display_primaries(Resource *resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y)
|
||||
{
|
||||
if (m_masteringColorimetry) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "mastering display primaries are already set");
|
||||
return;
|
||||
}
|
||||
m_masteringColorimetry = Colorimetry{
|
||||
xy{r_x * s_primaryUnit, r_y * s_primaryUnit},
|
||||
xy{g_x * s_primaryUnit, g_y * s_primaryUnit},
|
||||
xy{b_x * s_primaryUnit, b_y * s_primaryUnit},
|
||||
xy{w_x * s_primaryUnit, w_y * s_primaryUnit},
|
||||
};
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_mastering_luminance(Resource *resource, uint32_t min_lum, uint32_t max_lum)
|
||||
{
|
||||
if (m_minMasteringLuminance) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "mastering luminance is already set");
|
||||
return;
|
||||
}
|
||||
if (min_lum * s_minLuminanceUnit >= max_lum) {
|
||||
wl_resource_post_error(resource->handle, error::error_invalid_luminance, "min_lum can't be higher or equal to max_lum");
|
||||
return;
|
||||
}
|
||||
m_minMasteringLuminance = min_lum * s_minLuminanceUnit;
|
||||
if (max_lum > 0) {
|
||||
m_maxMasteringLuminance = max_lum;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_max_cll(Resource *resource, uint32_t max_cll)
|
||||
{
|
||||
if (m_maxCll) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "max_cll is already set");
|
||||
return;
|
||||
}
|
||||
if (max_cll > 0) {
|
||||
m_maxCll = max_cll;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorParametricCreatorV1::wp_image_description_creator_params_v1_set_max_fall(Resource *resource, uint32_t max_fall)
|
||||
{
|
||||
if (m_maxFall) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_set, "max_fall is already set");
|
||||
return;
|
||||
}
|
||||
if (max_fall) {
|
||||
m_maxFall = max_fall;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t ImageDescriptionV1::s_idCounter = 1;
|
||||
|
||||
ImageDescriptionV1::ImageDescriptionV1(wl_client *client, uint32_t id, uint32_t version, const std::optional<ColorDescription> &color)
|
||||
: QtWaylandServer::wp_image_description_v1(client, id, version)
|
||||
, m_description(color)
|
||||
{
|
||||
if (color.has_value()) {
|
||||
send_ready(resource()->handle, s_idCounter++);
|
||||
} else {
|
||||
send_failed(resource()->handle, cause::cause_unsupported, "The provided image description failed to verify as usable");
|
||||
}
|
||||
}
|
||||
|
||||
void ImageDescriptionV1::wp_image_description_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ImageDescriptionV1::wp_image_description_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
static uint32_t kwinTFtoProtoTF(TransferFunction tf)
|
||||
{
|
||||
switch (tf.type) {
|
||||
case TransferFunction::sRGB:
|
||||
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB;
|
||||
case TransferFunction::linear:
|
||||
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ;
|
||||
case TransferFunction::gamma22:
|
||||
return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
static uint32_t kwinPrimariesToProtoPrimaires(NamedColorimetry primaries)
|
||||
{
|
||||
switch (primaries) {
|
||||
case NamedColorimetry::BT709:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_srgb;
|
||||
case NamedColorimetry::PAL_M:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal_m;
|
||||
case NamedColorimetry::PAL:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_pal;
|
||||
case NamedColorimetry::NTSC:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_ntsc;
|
||||
case NamedColorimetry::GenericFilm:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_generic_film;
|
||||
case NamedColorimetry::BT2020:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_bt2020;
|
||||
case NamedColorimetry::CIEXYZ:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_cie1931_xyz;
|
||||
case NamedColorimetry::DCIP3:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_dci_p3;
|
||||
case NamedColorimetry::DisplayP3:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_display_p3;
|
||||
case NamedColorimetry::AdobeRGB:
|
||||
return QtWaylandServer::wp_color_manager_v1::primaries::primaries_adobe_rgb;
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
void ImageDescriptionV1::wp_image_description_v1_get_information(Resource *qtResource, uint32_t information)
|
||||
{
|
||||
if (!m_description.has_value()) {
|
||||
wl_resource_post_error(qtResource->handle, error_no_information, "Can't request information from a failed image description");
|
||||
return;
|
||||
}
|
||||
auto resource = wl_resource_create(qtResource->client(), &wp_image_description_info_v1_interface, qtResource->version(), information);
|
||||
const auto round = [](float f) {
|
||||
return std::clamp(std::round(f / s_primaryUnit), 0.0, 1.0 / s_primaryUnit);
|
||||
};
|
||||
const xyY containerRed = m_description->containerColorimetry().red().toxyY();
|
||||
const xyY containerGreen = m_description->containerColorimetry().green().toxyY();
|
||||
const xyY containerBlue = m_description->containerColorimetry().blue().toxyY();
|
||||
const xyY containerWhite = m_description->containerColorimetry().white().toxyY();
|
||||
wp_image_description_info_v1_send_primaries(resource,
|
||||
round(containerRed.x), round(containerRed.y),
|
||||
round(containerGreen.x), round(containerGreen.y),
|
||||
round(containerBlue.x), round(containerBlue.y),
|
||||
round(containerWhite.x), round(containerWhite.y));
|
||||
if (auto name = m_description->containerColorimetry().name()) {
|
||||
wp_image_description_info_v1_send_primaries_named(resource, kwinPrimariesToProtoPrimaires(*name));
|
||||
}
|
||||
if (auto m = m_description->masteringColorimetry()) {
|
||||
const xyY masterRed = m->red().toxyY();
|
||||
const xyY masterGreen = m->green().toxyY();
|
||||
const xyY masterBlue = m->blue().toxyY();
|
||||
const xyY masterWhite = m->white().toxyY();
|
||||
wp_image_description_info_v1_send_target_primaries(resource,
|
||||
round(masterRed.x), round(masterRed.y),
|
||||
round(masterGreen.x), round(masterGreen.y),
|
||||
round(masterBlue.x), round(masterBlue.y),
|
||||
round(masterWhite.x), round(masterWhite.y));
|
||||
}
|
||||
if (auto maxfall = m_description->maxAverageLuminance()) {
|
||||
wp_image_description_info_v1_send_target_max_fall(resource, *maxfall);
|
||||
}
|
||||
if (auto maxcll = m_description->maxHdrLuminance()) {
|
||||
wp_image_description_info_v1_send_target_max_cll(resource, *maxcll);
|
||||
}
|
||||
wp_image_description_info_v1_send_luminances(resource, std::round(m_description->transferFunction().minLuminance / s_minLuminanceUnit), std::round(m_description->transferFunction().maxLuminance), std::round(m_description->referenceLuminance()));
|
||||
wp_image_description_info_v1_send_target_luminance(resource, m_description->minLuminance(), m_description->maxHdrLuminance().value_or(800));
|
||||
wp_image_description_info_v1_send_tf_named(resource, kwinTFtoProtoTF(m_description->transferFunction()));
|
||||
wp_image_description_info_v1_send_done(resource);
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
const std::optional<ColorDescription> &ImageDescriptionV1::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
ImageDescriptionV1 *ImageDescriptionV1::get(wl_resource *resource)
|
||||
{
|
||||
if (auto resourceContainer = Resource::fromResource(resource)) {
|
||||
return static_cast<ImageDescriptionV1 *>(resourceContainer->object());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ColorManagementOutputV1::ColorManagementOutputV1(wl_client *client, uint32_t id, uint32_t version, Output *output)
|
||||
: QtWaylandServer::wp_color_management_output_v1(client, id, version)
|
||||
, m_output(output)
|
||||
, m_colorDescription(output->colorDescription())
|
||||
{
|
||||
connect(output, &Output::colorDescriptionChanged, this, &ColorManagementOutputV1::colorDescriptionChanged);
|
||||
}
|
||||
|
||||
void ColorManagementOutputV1::wp_color_management_output_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ColorManagementOutputV1::wp_color_management_output_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ColorManagementOutputV1::wp_color_management_output_v1_get_image_description(Resource *resource, uint32_t image_description)
|
||||
{
|
||||
new ImageDescriptionV1(resource->client(), image_description, resource->version(), m_colorDescription);
|
||||
}
|
||||
|
||||
void ColorManagementOutputV1::colorDescriptionChanged()
|
||||
{
|
||||
m_colorDescription = m_output->colorDescription();
|
||||
send_image_description_changed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_colormanagement_v1.cpp"
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
#include "core/colorspace.h"
|
||||
|
||||
#include "wayland/qwayland-server-color-management-v1.h"
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
class Output;
|
||||
|
||||
class ColorManagerV1 : public QObject, private QtWaylandServer::wp_color_manager_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColorManagerV1(Display *display, QObject *parent);
|
||||
|
||||
private:
|
||||
void wp_color_manager_v1_bind_resource(Resource *resource) override;
|
||||
void wp_color_manager_v1_destroy(Resource *resource) override;
|
||||
void wp_color_manager_v1_get_output(Resource *resource, uint32_t id, struct ::wl_resource *output) override;
|
||||
void wp_color_manager_v1_get_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface) override;
|
||||
void wp_color_manager_v1_get_surface_feedback(Resource *resource, uint32_t id, struct ::wl_resource *surface) override;
|
||||
void wp_color_manager_v1_create_icc_creator(Resource *resource, uint32_t obj) override;
|
||||
void wp_color_manager_v1_create_parametric_creator(Resource *resource, uint32_t obj) override;
|
||||
void wp_color_manager_v1_create_windows_scrgb(Resource *resource, uint32_t image_description) override;
|
||||
};
|
||||
|
||||
class ColorFeedbackSurfaceV1 : private QtWaylandServer::wp_color_management_surface_feedback_v1
|
||||
{
|
||||
public:
|
||||
explicit ColorFeedbackSurfaceV1(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface);
|
||||
~ColorFeedbackSurfaceV1() override;
|
||||
|
||||
void setPreferredColorDescription(const ColorDescription &descr);
|
||||
|
||||
private:
|
||||
void wp_color_management_surface_feedback_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_color_management_surface_feedback_v1_destroy(Resource *resource) override;
|
||||
void wp_color_management_surface_feedback_v1_get_preferred(Resource *resource, uint32_t image_description) override;
|
||||
void wp_color_management_surface_feedback_v1_get_preferred_parametric(Resource *resource, uint32_t image_description) override;
|
||||
|
||||
QPointer<SurfaceInterface> m_surface;
|
||||
ColorDescription m_preferred;
|
||||
};
|
||||
|
||||
class ColorSurfaceV1 : private QtWaylandServer::wp_color_management_surface_v1
|
||||
{
|
||||
public:
|
||||
explicit ColorSurfaceV1(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface);
|
||||
~ColorSurfaceV1() override;
|
||||
|
||||
private:
|
||||
void wp_color_management_surface_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_color_management_surface_v1_destroy(Resource *resource) override;
|
||||
void wp_color_management_surface_v1_set_image_description(Resource *resource, struct ::wl_resource *image_description, uint32_t render_intent) override;
|
||||
void wp_color_management_surface_v1_unset_image_description(Resource *resource) override;
|
||||
|
||||
QPointer<SurfaceInterface> m_surface;
|
||||
};
|
||||
|
||||
class ColorParametricCreatorV1 : private QtWaylandServer::wp_image_description_creator_params_v1
|
||||
{
|
||||
public:
|
||||
explicit ColorParametricCreatorV1(wl_client *client, uint32_t id, uint32_t version);
|
||||
|
||||
private:
|
||||
void wp_image_description_creator_params_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_image_description_creator_params_v1_create(Resource *resource, uint32_t image_description) override;
|
||||
void wp_image_description_creator_params_v1_set_tf_named(Resource *resource, uint32_t tf) override;
|
||||
void wp_image_description_creator_params_v1_set_tf_power(Resource *resource, uint32_t eexp) override;
|
||||
void wp_image_description_creator_params_v1_set_primaries_named(Resource *resource, uint32_t primaries) override;
|
||||
void wp_image_description_creator_params_v1_set_primaries(Resource *resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) override;
|
||||
void wp_image_description_creator_params_v1_set_mastering_display_primaries(Resource *resource, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y) override;
|
||||
void wp_image_description_creator_params_v1_set_mastering_luminance(Resource *resource, uint32_t min_lum, uint32_t max_lum) override;
|
||||
void wp_image_description_creator_params_v1_set_max_cll(Resource *resource, uint32_t max_cll) override;
|
||||
void wp_image_description_creator_params_v1_set_max_fall(Resource *resource, uint32_t max_fall) override;
|
||||
void wp_image_description_creator_params_v1_set_luminances(Resource *resource, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum) override;
|
||||
|
||||
std::optional<Colorimetry> m_colorimetry;
|
||||
std::optional<TransferFunction::Type> m_transferFunctionType;
|
||||
struct Luminances
|
||||
{
|
||||
double min;
|
||||
double max;
|
||||
double reference;
|
||||
};
|
||||
std::optional<Luminances> m_transferFunctionLuminances;
|
||||
|
||||
// mastering display information
|
||||
std::optional<Colorimetry> m_masteringColorimetry;
|
||||
std::optional<double> m_minMasteringLuminance;
|
||||
std::optional<double> m_maxMasteringLuminance;
|
||||
std::optional<double> m_maxCll;
|
||||
std::optional<double> m_maxFall;
|
||||
};
|
||||
|
||||
class ImageDescriptionV1 : private QtWaylandServer::wp_image_description_v1
|
||||
{
|
||||
public:
|
||||
explicit ImageDescriptionV1(wl_client *client, uint32_t id, uint32_t version, const std::optional<ColorDescription> &color);
|
||||
|
||||
const std::optional<ColorDescription> &description() const;
|
||||
|
||||
static ImageDescriptionV1 *get(wl_resource *resource);
|
||||
static uint64_t s_idCounter;
|
||||
|
||||
private:
|
||||
void wp_image_description_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_image_description_v1_destroy(Resource *resource) override;
|
||||
void wp_image_description_v1_get_information(Resource *resource, uint32_t information) override;
|
||||
|
||||
const std::optional<ColorDescription> m_description;
|
||||
};
|
||||
|
||||
class ColorManagementOutputV1 : public QObject, private QtWaylandServer::wp_color_management_output_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColorManagementOutputV1(wl_client *client, uint32_t id, uint32_t version, Output *output);
|
||||
|
||||
private:
|
||||
void colorDescriptionChanged();
|
||||
void wp_color_management_output_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_color_management_output_v1_destroy(Resource *resource) override;
|
||||
void wp_color_management_output_v1_get_image_description(Resource *resource, uint32_t image_description) override;
|
||||
|
||||
Output *const m_output;
|
||||
ColorDescription m_colorDescription;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "compositor.h"
|
||||
#include "display.h"
|
||||
#include "region_p.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include "qwayland-server-wayland.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const int s_version = 6;
|
||||
|
||||
class CompositorInterfacePrivate : public QtWaylandServer::wl_compositor
|
||||
{
|
||||
public:
|
||||
CompositorInterfacePrivate(CompositorInterface *q, Display *display);
|
||||
|
||||
CompositorInterface *q;
|
||||
Display *display;
|
||||
|
||||
protected:
|
||||
void compositor_create_surface(Resource *resource, uint32_t id) override;
|
||||
void compositor_create_region(Resource *resource, uint32_t id) override;
|
||||
};
|
||||
|
||||
CompositorInterfacePrivate::CompositorInterfacePrivate(CompositorInterface *q, Display *display)
|
||||
: QtWaylandServer::wl_compositor(*display, s_version)
|
||||
, q(q)
|
||||
, display(display)
|
||||
{
|
||||
}
|
||||
|
||||
void CompositorInterfacePrivate::compositor_create_surface(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *surfaceResource = wl_resource_create(resource->client(), &wl_surface_interface, resource->version(), id);
|
||||
if (!surfaceResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
Q_EMIT q->surfaceCreated(new SurfaceInterface(q, surfaceResource));
|
||||
}
|
||||
|
||||
void CompositorInterfacePrivate::compositor_create_region(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *regionResource = wl_resource_create(resource->client(), &wl_region_interface, resource->version(), id);
|
||||
if (!regionResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
new RegionInterface(regionResource);
|
||||
}
|
||||
|
||||
CompositorInterface::CompositorInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new CompositorInterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
CompositorInterface::~CompositorInterface()
|
||||
{
|
||||
}
|
||||
|
||||
Display *CompositorInterface::display() const
|
||||
{
|
||||
return d->display;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "wayland/moc_compositor.cpp"
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include "surface.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class CompositorInterfacePrivate;
|
||||
class Display;
|
||||
|
||||
/**
|
||||
* The CompositorInterface global allows clients to create surfaces and region objects.
|
||||
*
|
||||
* The CompositorInterface corresponds to the Wayland interface @c wl_compositor.
|
||||
*/
|
||||
class KWIN_EXPORT CompositorInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CompositorInterface(Display *display, QObject *parent = nullptr);
|
||||
~CompositorInterface() override;
|
||||
|
||||
/**
|
||||
* Returns the Display object for this CompositorInterface.
|
||||
*/
|
||||
Display *display() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when a new SurfaceInterface @a surface has been created.
|
||||
*/
|
||||
void surfaceCreated(KWin::SurfaceInterface *surface);
|
||||
|
||||
private:
|
||||
std::unique_ptr<CompositorInterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "contenttype_v1.h"
|
||||
|
||||
#include "display.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_version = 1;
|
||||
|
||||
static ContentType waylandToKwinContentType(uint32_t type)
|
||||
{
|
||||
using Type = QtWaylandServer::wp_content_type_v1::type;
|
||||
switch (type) {
|
||||
case Type::type_photo:
|
||||
return ContentType::Photo;
|
||||
case Type::type_video:
|
||||
return ContentType::Video;
|
||||
case Type::type_game:
|
||||
return ContentType::Game;
|
||||
default:
|
||||
return ContentType::None;
|
||||
}
|
||||
}
|
||||
|
||||
ContentTypeManagerV1Interface::ContentTypeManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWaylandServer::wp_content_type_manager_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void ContentTypeManagerV1Interface::wp_content_type_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ContentTypeManagerV1Interface::wp_content_type_manager_v1_get_surface_content_type(Resource *resource, uint32_t id, struct ::wl_resource *wlSurface)
|
||||
{
|
||||
SurfaceInterface *surface = SurfaceInterface::get(wlSurface);
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
|
||||
if (surfacePrivate->contentTypeInterface) {
|
||||
wl_resource_post_error(resource->handle, error_already_constructed, "Surface already has a wp_content_type_v1");
|
||||
return;
|
||||
}
|
||||
surfacePrivate->contentTypeInterface = new ContentTypeV1Interface(surface, resource->client(), id);
|
||||
}
|
||||
|
||||
ContentTypeV1Interface::ContentTypeV1Interface(SurfaceInterface *surface, wl_client *client, uint32_t id)
|
||||
: QtWaylandServer::wp_content_type_v1(client, id, s_version)
|
||||
, m_surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
void ContentTypeV1Interface::wp_content_type_v1_set_content_type(Resource *, uint32_t content_type)
|
||||
{
|
||||
if (!m_surface) {
|
||||
return;
|
||||
}
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
|
||||
surfacePrivate->pending->contentType = waylandToKwinContentType(content_type);
|
||||
surfacePrivate->pending->contentTypeIsSet = true;
|
||||
}
|
||||
|
||||
void ContentTypeV1Interface::wp_content_type_v1_destroy(Resource *resource)
|
||||
{
|
||||
if (m_surface) {
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(m_surface);
|
||||
surfacePrivate->pending->contentType = ContentType::None;
|
||||
surfacePrivate->pending->contentTypeIsSet = true;
|
||||
}
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ContentTypeV1Interface::wp_content_type_v1_destroy_resource(Resource *)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_contenttype_v1.cpp"
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "surface.h"
|
||||
|
||||
#include "wayland/qwayland-server-content-type-v1.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class ContentTypeV1Interface;
|
||||
class Display;
|
||||
|
||||
class ContentTypeManagerV1Interface : public QObject, private QtWaylandServer::wp_content_type_manager_v1
|
||||
{
|
||||
public:
|
||||
ContentTypeManagerV1Interface(Display *display, QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
void wp_content_type_manager_v1_destroy(QtWaylandServer::wp_content_type_manager_v1::Resource *resource) override;
|
||||
void wp_content_type_manager_v1_get_surface_content_type(Resource *resource, uint32_t id, struct ::wl_resource *surface) override;
|
||||
};
|
||||
|
||||
class ContentTypeV1Interface : public QObject, private QtWaylandServer::wp_content_type_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ContentTypeV1Interface(SurfaceInterface *surface, wl_client *client, uint32_t id);
|
||||
|
||||
private:
|
||||
void wp_content_type_v1_set_content_type(QtWaylandServer::wp_content_type_v1::Resource *resource, uint32_t content_type) override;
|
||||
void wp_content_type_v1_destroy(QtWaylandServer::wp_content_type_v1::Resource *resource) override;
|
||||
void wp_content_type_v1_destroy_resource(QtWaylandServer::wp_content_type_v1::Resource *resource) override;
|
||||
|
||||
const QPointer<SurfaceInterface> m_surface;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "contrast.h"
|
||||
#include "display.h"
|
||||
#include "region_p.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
#include <qwayland-server-contrast.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 2;
|
||||
|
||||
class ContrastManagerInterfacePrivate : public QtWaylandServer::org_kde_kwin_contrast_manager
|
||||
{
|
||||
public:
|
||||
ContrastManagerInterfacePrivate(ContrastManagerInterface *q, Display *display);
|
||||
|
||||
ContrastManagerInterface *q;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_contrast_manager_destroy_global() override;
|
||||
void org_kde_kwin_contrast_manager_create(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
void org_kde_kwin_contrast_manager_unset(Resource *resource, wl_resource *surface) override;
|
||||
};
|
||||
|
||||
ContrastManagerInterfacePrivate::ContrastManagerInterfacePrivate(ContrastManagerInterface *q, Display *display)
|
||||
: QtWaylandServer::org_kde_kwin_contrast_manager(*display, s_version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void ContrastManagerInterfacePrivate::org_kde_kwin_contrast_manager_destroy_global()
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
void ContrastManagerInterfacePrivate::org_kde_kwin_contrast_manager_create(Resource *resource, uint32_t id, wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *s = SurfaceInterface::get(surface);
|
||||
if (!s) {
|
||||
wl_resource_post_error(resource->handle, 0, "Invalid surface");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *contrast_resource = wl_resource_create(resource->client(), &org_kde_kwin_contrast_interface, resource->version(), id);
|
||||
if (!contrast_resource) {
|
||||
wl_client_post_no_memory(resource->client());
|
||||
return;
|
||||
}
|
||||
auto contrast = new ContrastInterface(contrast_resource);
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(s);
|
||||
surfacePrivate->setContrast(contrast);
|
||||
}
|
||||
|
||||
void ContrastManagerInterfacePrivate::org_kde_kwin_contrast_manager_unset(Resource *resource, wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *s = SurfaceInterface::get(surface);
|
||||
if (!s) {
|
||||
wl_resource_post_error(resource->handle, 0, "Invalid surface");
|
||||
return;
|
||||
}
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(s);
|
||||
surfacePrivate->setContrast(QPointer<ContrastInterface>());
|
||||
}
|
||||
|
||||
ContrastManagerInterface::ContrastManagerInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new ContrastManagerInterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
ContrastManagerInterface::~ContrastManagerInterface()
|
||||
{
|
||||
}
|
||||
|
||||
void ContrastManagerInterface::remove()
|
||||
{
|
||||
d->globalRemove();
|
||||
}
|
||||
|
||||
class ContrastInterfacePrivate : public QtWaylandServer::org_kde_kwin_contrast
|
||||
{
|
||||
public:
|
||||
ContrastInterfacePrivate(ContrastInterface *_q, wl_resource *resource);
|
||||
|
||||
QRegion pendingRegion;
|
||||
QRegion currentRegion;
|
||||
qreal pendingContrast;
|
||||
qreal currentContrast;
|
||||
qreal pendingIntensity;
|
||||
qreal currentIntensity;
|
||||
qreal pendingSaturation;
|
||||
qreal currentSaturation;
|
||||
QColor currentFrost;
|
||||
QColor pendingFrost;
|
||||
ContrastInterface *q;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_contrast_commit(Resource *resource) override;
|
||||
void org_kde_kwin_contrast_set_region(Resource *resource, wl_resource *region) override;
|
||||
void org_kde_kwin_contrast_set_contrast(Resource *resource, wl_fixed_t contrast) override;
|
||||
void org_kde_kwin_contrast_set_intensity(Resource *resource, wl_fixed_t intensity) override;
|
||||
void org_kde_kwin_contrast_set_saturation(Resource *resource, wl_fixed_t saturation) override;
|
||||
void org_kde_kwin_contrast_set_frost(Resource *resource, int r, int g, int b, int a) override;
|
||||
void org_kde_kwin_contrast_unset_frost(Resource *resource) override;
|
||||
void org_kde_kwin_contrast_release(Resource *resource) override;
|
||||
void org_kde_kwin_contrast_destroy_resource(Resource *resource) override;
|
||||
};
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_commit(Resource *resource)
|
||||
{
|
||||
currentRegion = pendingRegion;
|
||||
currentContrast = pendingContrast;
|
||||
currentIntensity = pendingIntensity;
|
||||
currentSaturation = pendingSaturation;
|
||||
currentFrost = pendingFrost;
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_set_region(Resource *resource, wl_resource *region)
|
||||
{
|
||||
RegionInterface *r = RegionInterface::get(region);
|
||||
if (r) {
|
||||
pendingRegion = r->region();
|
||||
} else {
|
||||
pendingRegion = QRegion();
|
||||
}
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_set_contrast(Resource *resource, wl_fixed_t contrast)
|
||||
{
|
||||
pendingContrast = wl_fixed_to_double(contrast);
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_set_intensity(Resource *resource, wl_fixed_t intensity)
|
||||
{
|
||||
pendingIntensity = wl_fixed_to_double(intensity);
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_set_saturation(Resource *resource, wl_fixed_t saturation)
|
||||
{
|
||||
pendingSaturation = wl_fixed_to_double(saturation);
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_set_frost(Resource *resource, int r, int g, int b, int a)
|
||||
{
|
||||
pendingFrost = QColor(r, g, b, a);
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_unset_frost(Resource *resource)
|
||||
{
|
||||
pendingFrost = {};
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ContrastInterfacePrivate::org_kde_kwin_contrast_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
ContrastInterfacePrivate::ContrastInterfacePrivate(ContrastInterface *_q, wl_resource *resource)
|
||||
: QtWaylandServer::org_kde_kwin_contrast(resource)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
ContrastInterface::ContrastInterface(wl_resource *resource)
|
||||
: QObject()
|
||||
, d(new ContrastInterfacePrivate(this, resource))
|
||||
{
|
||||
}
|
||||
|
||||
ContrastInterface::~ContrastInterface() = default;
|
||||
|
||||
QRegion ContrastInterface::region() const
|
||||
{
|
||||
return d->currentRegion;
|
||||
}
|
||||
|
||||
qreal ContrastInterface::contrast() const
|
||||
{
|
||||
return d->currentContrast;
|
||||
}
|
||||
|
||||
qreal ContrastInterface::intensity() const
|
||||
{
|
||||
return d->currentIntensity;
|
||||
}
|
||||
|
||||
qreal ContrastInterface::saturation() const
|
||||
{
|
||||
return d->currentSaturation;
|
||||
}
|
||||
|
||||
QColor ContrastInterface::frost() const
|
||||
{
|
||||
return d->currentFrost;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_contrast.cpp"
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <QColor>
|
||||
#include <QObject>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class ContrastManagerInterfacePrivate;
|
||||
class ContrastInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Represents the Global for org_kde_kwin_contrast_manager interface.
|
||||
*
|
||||
* This class creates ContrastInterfaces and attaches them to SurfaceInterfaces.
|
||||
*
|
||||
* @see ContrastInterface
|
||||
* @see SurfaceInterface
|
||||
*/
|
||||
class KWIN_EXPORT ContrastManagerInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ContrastManagerInterface(Display *display, QObject *parent = nullptr);
|
||||
~ContrastManagerInterface() override;
|
||||
|
||||
void remove();
|
||||
|
||||
private:
|
||||
std::unique_ptr<ContrastManagerInterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents the Resource for the org_kde_kwin_contrast interface.
|
||||
*
|
||||
* Instances of this class are only generated by the ContrastManagerInterface.
|
||||
* The ContrastInterface gets attached to a SurfaceInterface and can be assessed
|
||||
* from there using @link SurfaceInterface::contrast() @endlink. Please note that
|
||||
* the ContrastInterface is only available on the SurfaceInterface after it has been
|
||||
* committed.
|
||||
*
|
||||
* @see ContrastManagerInterface
|
||||
* @see SurfaceInterface
|
||||
*/
|
||||
class KWIN_EXPORT ContrastInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~ContrastInterface() override;
|
||||
|
||||
QRegion region() const;
|
||||
qreal contrast() const;
|
||||
qreal intensity() const;
|
||||
qreal saturation() const;
|
||||
QColor frost() const;
|
||||
|
||||
private:
|
||||
explicit ContrastInterface(wl_resource *resource);
|
||||
friend class ContrastManagerInterfacePrivate;
|
||||
|
||||
std::unique_ptr<ContrastInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "wayland/cursorshape_v1.h"
|
||||
#include "wayland/clientconnection.h"
|
||||
#include "wayland/display.h"
|
||||
#include "wayland/pointer.h"
|
||||
#include "wayland/surface.h"
|
||||
#include "wayland/tablet_v2.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
#include "qwayland-server-cursor-shape-v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr int s_version = 1;
|
||||
|
||||
class CursorShapeManagerV1InterfacePrivate : public QtWaylandServer::wp_cursor_shape_manager_v1
|
||||
{
|
||||
public:
|
||||
CursorShapeManagerV1InterfacePrivate(Display *display);
|
||||
|
||||
protected:
|
||||
void wp_cursor_shape_manager_v1_destroy(Resource *resource) override;
|
||||
void wp_cursor_shape_manager_v1_get_pointer(Resource *resource, uint32_t cursor_shape_device, struct ::wl_resource *pointer) override;
|
||||
void wp_cursor_shape_manager_v1_get_tablet_tool_v2(Resource *resource, uint32_t cursor_shape_device, struct ::wl_resource *tablet_tool) override;
|
||||
};
|
||||
|
||||
class CursorShapeDeviceV1Interface : public QtWaylandServer::wp_cursor_shape_device_v1
|
||||
{
|
||||
public:
|
||||
CursorShapeDeviceV1Interface(PointerInterface *pointer, wl_resource *resource);
|
||||
CursorShapeDeviceV1Interface(TabletToolV2Interface *tabletTool, wl_resource *resource);
|
||||
|
||||
QPointer<PointerInterface> pointer;
|
||||
QPointer<TabletToolV2Interface> tabletTool;
|
||||
|
||||
protected:
|
||||
void wp_cursor_shape_device_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_cursor_shape_device_v1_destroy(Resource *resource) override;
|
||||
void wp_cursor_shape_device_v1_set_shape(Resource *resource, uint32_t serial, uint32_t shape) override;
|
||||
};
|
||||
|
||||
CursorShapeManagerV1InterfacePrivate::CursorShapeManagerV1InterfacePrivate(Display *display)
|
||||
: QtWaylandServer::wp_cursor_shape_manager_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void CursorShapeManagerV1InterfacePrivate::wp_cursor_shape_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void CursorShapeManagerV1InterfacePrivate::wp_cursor_shape_manager_v1_get_pointer(Resource *resource, uint32_t cursor_shape_device, struct ::wl_resource *pointer)
|
||||
{
|
||||
wl_resource *device = wl_resource_create(resource->client(), &wp_cursor_shape_device_v1_interface, resource->version(), cursor_shape_device);
|
||||
if (!device) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
new CursorShapeDeviceV1Interface(PointerInterface::get(pointer), device);
|
||||
}
|
||||
|
||||
void CursorShapeManagerV1InterfacePrivate::wp_cursor_shape_manager_v1_get_tablet_tool_v2(Resource *resource, uint32_t cursor_shape_device, struct ::wl_resource *tablet_tool)
|
||||
{
|
||||
wl_resource *device = wl_resource_create(resource->client(), &wp_cursor_shape_device_v1_interface, resource->version(), cursor_shape_device);
|
||||
if (!device) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
new CursorShapeDeviceV1Interface(TabletToolV2Interface::get(tablet_tool), device);
|
||||
}
|
||||
|
||||
CursorShapeManagerV1Interface::CursorShapeManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(std::make_unique<CursorShapeManagerV1InterfacePrivate>(display))
|
||||
{
|
||||
}
|
||||
|
||||
CursorShapeManagerV1Interface::~CursorShapeManagerV1Interface()
|
||||
{
|
||||
}
|
||||
|
||||
CursorShapeDeviceV1Interface::CursorShapeDeviceV1Interface(PointerInterface *pointer, wl_resource *resource)
|
||||
: QtWaylandServer::wp_cursor_shape_device_v1(resource)
|
||||
, pointer(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
CursorShapeDeviceV1Interface::CursorShapeDeviceV1Interface(TabletToolV2Interface *tabletTool, wl_resource *resource)
|
||||
: QtWaylandServer::wp_cursor_shape_device_v1(resource)
|
||||
, tabletTool(tabletTool)
|
||||
{
|
||||
}
|
||||
|
||||
void CursorShapeDeviceV1Interface::wp_cursor_shape_device_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CursorShapeDeviceV1Interface::wp_cursor_shape_device_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
static QByteArray shapeName(uint32_t shape)
|
||||
{
|
||||
switch (shape) {
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_default:
|
||||
return QByteArrayLiteral("default");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_context_menu:
|
||||
return QByteArrayLiteral("context-menu");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_help:
|
||||
return QByteArrayLiteral("help");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_pointer:
|
||||
return QByteArrayLiteral("pointer");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_progress:
|
||||
return QByteArrayLiteral("progress");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_wait:
|
||||
return QByteArrayLiteral("wait");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_cell:
|
||||
return QByteArrayLiteral("cell");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_crosshair:
|
||||
return QByteArrayLiteral("crosshair");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_text:
|
||||
return QByteArrayLiteral("text");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_vertical_text:
|
||||
return QByteArrayLiteral("vertical-text");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_alias:
|
||||
return QByteArrayLiteral("alias");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_copy:
|
||||
return QByteArrayLiteral("copy");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_move:
|
||||
return QByteArrayLiteral("move");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_no_drop:
|
||||
return QByteArrayLiteral("no-drop");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_not_allowed:
|
||||
return QByteArrayLiteral("not-allowed");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_grab:
|
||||
return QByteArrayLiteral("grab");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_grabbing:
|
||||
return QByteArrayLiteral("grabbing");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_e_resize:
|
||||
return QByteArrayLiteral("e-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_n_resize:
|
||||
return QByteArrayLiteral("n-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_ne_resize:
|
||||
return QByteArrayLiteral("ne-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_nw_resize:
|
||||
return QByteArrayLiteral("nw-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_s_resize:
|
||||
return QByteArrayLiteral("s-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_se_resize:
|
||||
return QByteArrayLiteral("se-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_sw_resize:
|
||||
return QByteArrayLiteral("sw-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_w_resize:
|
||||
return QByteArrayLiteral("w-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_ew_resize:
|
||||
return QByteArrayLiteral("ew-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_ns_resize:
|
||||
return QByteArrayLiteral("ns-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_nesw_resize:
|
||||
return QByteArrayLiteral("nesw-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_nwse_resize:
|
||||
return QByteArrayLiteral("nwse-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_col_resize:
|
||||
return QByteArrayLiteral("col-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_row_resize:
|
||||
return QByteArrayLiteral("row-resize");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_all_scroll:
|
||||
return QByteArrayLiteral("all-scroll");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_zoom_in:
|
||||
return QByteArrayLiteral("zoom-in");
|
||||
case QtWaylandServer::wp_cursor_shape_device_v1::shape_zoom_out:
|
||||
return QByteArrayLiteral("zoom-out");
|
||||
default:
|
||||
return QByteArrayLiteral("default");
|
||||
}
|
||||
}
|
||||
|
||||
void CursorShapeDeviceV1Interface::wp_cursor_shape_device_v1_set_shape(Resource *resource, uint32_t serial, uint32_t shape)
|
||||
{
|
||||
if (shape < shape_default || shape > shape_zoom_out) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_shape, "unknown cursor shape");
|
||||
return;
|
||||
}
|
||||
if (pointer) {
|
||||
if (!pointer->focusedSurface() || pointer->focusedSurface()->client()->client() != resource->client()) {
|
||||
return;
|
||||
}
|
||||
if (pointer->focusedSerial() == serial) {
|
||||
Q_EMIT pointer->cursorChanged(shapeName(shape));
|
||||
}
|
||||
} else if (tabletTool) {
|
||||
if (!tabletTool->currentSurface() || tabletTool->currentSurface()->client()->client() != resource->client()) {
|
||||
return;
|
||||
}
|
||||
if (tabletTool->proximitySerial() == serial) {
|
||||
Q_EMIT tabletTool->cursorChanged(shapeName(shape));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class CursorShapeManagerV1InterfacePrivate;
|
||||
class Display;
|
||||
|
||||
class KWIN_EXPORT CursorShapeManagerV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CursorShapeManagerV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~CursorShapeManagerV1Interface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CursorShapeManagerV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "datacontroldevice_v1.h"
|
||||
#include "datacontroldevicemanager_v1.h"
|
||||
#include "datacontroloffer_v1.h"
|
||||
#include "datacontrolsource_v1.h"
|
||||
#include "display.h"
|
||||
#include "seat.h"
|
||||
#include "seat_p.h"
|
||||
#include "surface.h"
|
||||
|
||||
// Wayland
|
||||
#include <qwayland-server-wlr-data-control-unstable-v1.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataControlDeviceV1InterfacePrivate : public QtWaylandServer::zwlr_data_control_device_v1
|
||||
{
|
||||
public:
|
||||
DataControlDeviceV1InterfacePrivate(DataControlDeviceV1Interface *q, SeatInterface *seat, wl_resource *resource);
|
||||
|
||||
DataControlOfferV1Interface *createDataOffer(AbstractDataSource *source);
|
||||
|
||||
DataControlDeviceV1Interface *q;
|
||||
QPointer<SeatInterface> seat;
|
||||
QPointer<DataControlSourceV1Interface> selection;
|
||||
QPointer<DataControlSourceV1Interface> primarySelection;
|
||||
|
||||
protected:
|
||||
void zwlr_data_control_device_v1_destroy_resource(Resource *resource) override;
|
||||
void zwlr_data_control_device_v1_set_selection(Resource *resource, wl_resource *source) override;
|
||||
void zwlr_data_control_device_v1_set_primary_selection(Resource *resource, struct ::wl_resource *source) override;
|
||||
void zwlr_data_control_device_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
DataControlDeviceV1InterfacePrivate::DataControlDeviceV1InterfacePrivate(DataControlDeviceV1Interface *_q, SeatInterface *seat, wl_resource *resource)
|
||||
: QtWaylandServer::zwlr_data_control_device_v1(resource)
|
||||
, q(_q)
|
||||
, seat(seat)
|
||||
{
|
||||
}
|
||||
|
||||
void DataControlDeviceV1InterfacePrivate::zwlr_data_control_device_v1_set_selection(Resource *resource, wl_resource *source)
|
||||
{
|
||||
DataControlSourceV1Interface *dataSource = nullptr;
|
||||
|
||||
if (source) {
|
||||
dataSource = DataControlSourceV1Interface::get(source);
|
||||
Q_ASSERT(dataSource);
|
||||
if (dataSource == seat->selection() || dataSource == seat->primarySelection()) {
|
||||
wl_resource_post_error(resource->handle, error::error_used_source, "source given to set_selection was already used before");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (selection) {
|
||||
selection->cancel();
|
||||
}
|
||||
selection = dataSource;
|
||||
Q_EMIT q->selectionChanged(selection);
|
||||
}
|
||||
|
||||
void DataControlDeviceV1InterfacePrivate::zwlr_data_control_device_v1_set_primary_selection(Resource *resource, wl_resource *source)
|
||||
{
|
||||
DataControlSourceV1Interface *dataSource = nullptr;
|
||||
|
||||
if (source) {
|
||||
dataSource = DataControlSourceV1Interface::get(source);
|
||||
Q_ASSERT(dataSource);
|
||||
if (dataSource == seat->selection() || dataSource == seat->primarySelection()) {
|
||||
wl_resource_post_error(resource->handle, error::error_used_source, "source given to set_primary_selection was already used before");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (primarySelection) {
|
||||
primarySelection->cancel();
|
||||
}
|
||||
primarySelection = dataSource;
|
||||
Q_EMIT q->primarySelectionChanged(primarySelection);
|
||||
}
|
||||
|
||||
void DataControlDeviceV1InterfacePrivate::zwlr_data_control_device_v1_destroy(QtWaylandServer::zwlr_data_control_device_v1::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
DataControlOfferV1Interface *DataControlDeviceV1InterfacePrivate::createDataOffer(AbstractDataSource *source)
|
||||
{
|
||||
if (!source) {
|
||||
// a data offer can only exist together with a source
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &zwlr_data_control_offer_v1_interface, resource()->version(), 0);
|
||||
if (!data_offer_resource) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataControlOfferV1Interface *offer = new DataControlOfferV1Interface(source, data_offer_resource);
|
||||
send_data_offer(offer->resource());
|
||||
offer->sendAllOffers();
|
||||
return offer;
|
||||
}
|
||||
|
||||
void DataControlDeviceV1InterfacePrivate::zwlr_data_control_device_v1_destroy_resource(QtWaylandServer::zwlr_data_control_device_v1::Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
DataControlDeviceV1Interface::DataControlDeviceV1Interface(SeatInterface *seat, wl_resource *resource)
|
||||
: QObject()
|
||||
, d(new DataControlDeviceV1InterfacePrivate(this, seat, resource))
|
||||
{
|
||||
SeatInterfacePrivate *seatPrivate = SeatInterfacePrivate::get(seat);
|
||||
seatPrivate->registerDataControlDevice(this);
|
||||
}
|
||||
|
||||
DataControlDeviceV1Interface::~DataControlDeviceV1Interface() = default;
|
||||
|
||||
SeatInterface *DataControlDeviceV1Interface::seat() const
|
||||
{
|
||||
return d->seat;
|
||||
}
|
||||
|
||||
DataControlSourceV1Interface *DataControlDeviceV1Interface::selection() const
|
||||
{
|
||||
return d->selection;
|
||||
}
|
||||
|
||||
DataControlSourceV1Interface *DataControlDeviceV1Interface::primarySelection() const
|
||||
{
|
||||
return d->primarySelection;
|
||||
}
|
||||
|
||||
void DataControlDeviceV1Interface::sendSelection(AbstractDataSource *other)
|
||||
{
|
||||
DataControlOfferV1Interface *offer = d->createDataOffer(other);
|
||||
d->send_selection(offer ? offer->resource() : nullptr);
|
||||
}
|
||||
|
||||
void DataControlDeviceV1Interface::sendPrimarySelection(AbstractDataSource *other)
|
||||
{
|
||||
if (d->resource()->version() >= ZWLR_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION) {
|
||||
DataControlOfferV1Interface *offer = d->createDataOffer(other);
|
||||
d->send_primary_selection(offer ? offer->resource() : nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_datacontroldevice_v1.cpp"
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractDataSource;
|
||||
class DataControlDeviceManagerV1Interface;
|
||||
class DataControlDeviceV1InterfacePrivate;
|
||||
class DataControlOfferV1Interface;
|
||||
class DataControlSourceV1Interface;
|
||||
class SeatInterface;
|
||||
class SurfaceInterface;
|
||||
|
||||
/**
|
||||
* The DataControlDeviceV1Interface extensions allows clients to manage seat's current selection.
|
||||
*
|
||||
* DataControlDeviceV1Interface corresponds to the Wayland interface @c zwlr_data_control_device_v1.
|
||||
*/
|
||||
class KWIN_EXPORT DataControlDeviceV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~DataControlDeviceV1Interface() override;
|
||||
|
||||
SeatInterface *seat() const;
|
||||
DataControlSourceV1Interface *selection() const;
|
||||
DataControlSourceV1Interface *primarySelection() const;
|
||||
|
||||
void sendSelection(AbstractDataSource *other);
|
||||
|
||||
void sendPrimarySelection(AbstractDataSource *other);
|
||||
|
||||
Q_SIGNALS:
|
||||
void selectionChanged(KWin::DataControlSourceV1Interface *dataSource);
|
||||
|
||||
void primarySelectionChanged(KWin::DataControlSourceV1Interface *dataSource);
|
||||
|
||||
private:
|
||||
friend class DataControlDeviceManagerV1InterfacePrivate;
|
||||
explicit DataControlDeviceV1Interface(SeatInterface *seat, wl_resource *resource);
|
||||
|
||||
std::unique_ptr<DataControlDeviceV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::DataControlDeviceV1Interface *)
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "datacontroldevicemanager_v1.h"
|
||||
#include "datacontroldevice_v1.h"
|
||||
#include "datacontrolsource_v1.h"
|
||||
#include "display.h"
|
||||
#include "seat_p.h"
|
||||
// Wayland
|
||||
#include <qwayland-server-wlr-data-control-unstable-v1.h>
|
||||
|
||||
static const int s_version = 2;
|
||||
namespace KWin
|
||||
{
|
||||
class DataControlDeviceManagerV1InterfacePrivate : public QtWaylandServer::zwlr_data_control_manager_v1
|
||||
{
|
||||
public:
|
||||
DataControlDeviceManagerV1InterfacePrivate(DataControlDeviceManagerV1Interface *q, Display *d);
|
||||
|
||||
DataControlDeviceManagerV1Interface *q;
|
||||
|
||||
protected:
|
||||
void zwlr_data_control_manager_v1_create_data_source(Resource *resource, uint32_t id) override;
|
||||
void zwlr_data_control_manager_v1_get_data_device(Resource *resource, uint32_t id, wl_resource *seat) override;
|
||||
void zwlr_data_control_manager_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
DataControlDeviceManagerV1InterfacePrivate::DataControlDeviceManagerV1InterfacePrivate(DataControlDeviceManagerV1Interface *q, Display *d)
|
||||
: QtWaylandServer::zwlr_data_control_manager_v1(*d, s_version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void DataControlDeviceManagerV1InterfacePrivate::zwlr_data_control_manager_v1_create_data_source(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *data_source_resource = wl_resource_create(resource->client(), &zwlr_data_control_source_v1_interface, resource->version(), id);
|
||||
if (!data_source_resource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
DataControlSourceV1Interface *dataSource = new DataControlSourceV1Interface(q, data_source_resource);
|
||||
Q_EMIT q->dataSourceCreated(dataSource);
|
||||
}
|
||||
|
||||
void DataControlDeviceManagerV1InterfacePrivate::zwlr_data_control_manager_v1_get_data_device(Resource *resource, uint32_t id, wl_resource *seat)
|
||||
{
|
||||
SeatInterface *s = SeatInterface::get(seat);
|
||||
Q_ASSERT(s);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *data_device_resource = wl_resource_create(resource->client(), &zwlr_data_control_device_v1_interface, resource->version(), id);
|
||||
if (!data_device_resource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
DataControlDeviceV1Interface *dataDevice = new DataControlDeviceV1Interface(s, data_device_resource);
|
||||
Q_EMIT q->dataDeviceCreated(dataDevice);
|
||||
}
|
||||
|
||||
void DataControlDeviceManagerV1InterfacePrivate::zwlr_data_control_manager_v1_destroy(QtWaylandServer::zwlr_data_control_manager_v1::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
DataControlDeviceManagerV1Interface::DataControlDeviceManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new DataControlDeviceManagerV1InterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
DataControlDeviceManagerV1Interface::~DataControlDeviceManagerV1Interface() = default;
|
||||
|
||||
}
|
||||
|
||||
#include "moc_datacontroldevicemanager_v1.cpp"
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class DataControlSourceV1Interface;
|
||||
class DataControlDeviceManagerV1InterfacePrivate;
|
||||
class DataControlDeviceV1Interface;
|
||||
|
||||
/**
|
||||
* The DataControlDeviceManagerV1Interface provides a way for privileged clients such as clipboard
|
||||
* managers to manage the current selection.
|
||||
*
|
||||
* DataControlDeviceManagerV1Interface corresponds to the Wayland interface @c zwlr_data_control_manager_v1.
|
||||
*/
|
||||
class KWIN_EXPORT DataControlDeviceManagerV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DataControlDeviceManagerV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~DataControlDeviceManagerV1Interface() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void dataSourceCreated(KWin::DataControlSourceV1Interface *dataSource);
|
||||
void dataDeviceCreated(KWin::DataControlDeviceV1Interface *dataDevice);
|
||||
|
||||
private:
|
||||
std::unique_ptr<DataControlDeviceManagerV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "datacontroloffer_v1.h"
|
||||
#include "datacontroldevice_v1.h"
|
||||
#include "datacontrolsource_v1.h"
|
||||
// Qt
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
// Wayland
|
||||
#include <qwayland-server-wlr-data-control-unstable-v1.h>
|
||||
// system
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataControlOfferV1InterfacePrivate : public QtWaylandServer::zwlr_data_control_offer_v1
|
||||
{
|
||||
public:
|
||||
DataControlOfferV1InterfacePrivate(DataControlOfferV1Interface *q, AbstractDataSource *source, wl_resource *resource);
|
||||
|
||||
DataControlOfferV1Interface *q;
|
||||
QPointer<AbstractDataSource> source;
|
||||
|
||||
protected:
|
||||
void zwlr_data_control_offer_v1_receive(Resource *resource, const QString &mime_type, int32_t fd) override;
|
||||
void zwlr_data_control_offer_v1_destroy(Resource *resource) override;
|
||||
void zwlr_data_control_offer_v1_destroy_resource(Resource *resource) override;
|
||||
};
|
||||
|
||||
DataControlOfferV1InterfacePrivate::DataControlOfferV1InterfacePrivate(DataControlOfferV1Interface *_q, AbstractDataSource *source, wl_resource *resource)
|
||||
: QtWaylandServer::zwlr_data_control_offer_v1(resource)
|
||||
, q(_q)
|
||||
, source(source)
|
||||
{
|
||||
}
|
||||
|
||||
void DataControlOfferV1InterfacePrivate::zwlr_data_control_offer_v1_destroy(QtWaylandServer::zwlr_data_control_offer_v1::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DataControlOfferV1InterfacePrivate::zwlr_data_control_offer_v1_destroy_resource(QtWaylandServer::zwlr_data_control_offer_v1::Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
void DataControlOfferV1InterfacePrivate::zwlr_data_control_offer_v1_receive(Resource *resource, const QString &mimeType, qint32 fd)
|
||||
{
|
||||
if (!source) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
source->requestData(mimeType, fd);
|
||||
}
|
||||
|
||||
DataControlOfferV1Interface::DataControlOfferV1Interface(AbstractDataSource *source, wl_resource *resource)
|
||||
: QObject()
|
||||
, d(new DataControlOfferV1InterfacePrivate(this, source, resource))
|
||||
{
|
||||
Q_ASSERT(source);
|
||||
connect(source, &AbstractDataSource::mimeTypeOffered, this, [this](const QString &mimeType) {
|
||||
d->send_offer(mimeType);
|
||||
});
|
||||
}
|
||||
|
||||
DataControlOfferV1Interface::~DataControlOfferV1Interface() = default;
|
||||
|
||||
void DataControlOfferV1Interface::sendAllOffers()
|
||||
{
|
||||
Q_ASSERT(d->source);
|
||||
for (const QString &mimeType : d->source->mimeTypes()) {
|
||||
d->send_offer(mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
wl_resource *DataControlOfferV1Interface::resource() const
|
||||
{
|
||||
return d->resource()->handle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_datacontroloffer_v1.cpp"
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "datacontroldevicemanager_v1.h"
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractDataSource;
|
||||
class DataControlDeviceV1Interface;
|
||||
class DataControlSourceV1Interface;
|
||||
class DataControlOfferV1InterfacePrivate;
|
||||
|
||||
/**
|
||||
* The DataControlOfferV1Interface extension represents a piece of data offered for transfer.
|
||||
*
|
||||
* DataControlOfferV1Interface corresponds to the Wayland interface @c zwlr_data_control_offer_v1.
|
||||
*/
|
||||
class KWIN_EXPORT DataControlOfferV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~DataControlOfferV1Interface() override;
|
||||
|
||||
void sendAllOffers();
|
||||
wl_resource *resource() const;
|
||||
|
||||
private:
|
||||
friend class DataControlDeviceV1InterfacePrivate;
|
||||
explicit DataControlOfferV1Interface(AbstractDataSource *source, wl_resource *resource);
|
||||
|
||||
std::unique_ptr<DataControlOfferV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::DataControlOfferV1Interface *)
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "datacontrolsource_v1.h"
|
||||
#include "clientconnection.h"
|
||||
#include "datacontroldevicemanager_v1.h"
|
||||
#include "utils/resource.h"
|
||||
// Qt
|
||||
#include <QStringList>
|
||||
// Wayland
|
||||
#include <qwayland-server-wlr-data-control-unstable-v1.h>
|
||||
// system
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataControlSourceV1InterfacePrivate : public QtWaylandServer::zwlr_data_control_source_v1
|
||||
{
|
||||
public:
|
||||
DataControlSourceV1InterfacePrivate(DataControlSourceV1Interface *q, ::wl_resource *resource);
|
||||
|
||||
QStringList mimeTypes;
|
||||
DataControlSourceV1Interface *q;
|
||||
|
||||
protected:
|
||||
void zwlr_data_control_source_v1_destroy_resource(Resource *resource) override;
|
||||
void zwlr_data_control_source_v1_offer(Resource *resource, const QString &mime_type) override;
|
||||
void zwlr_data_control_source_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
DataControlSourceV1InterfacePrivate::DataControlSourceV1InterfacePrivate(DataControlSourceV1Interface *_q, ::wl_resource *resource)
|
||||
: QtWaylandServer::zwlr_data_control_source_v1(resource)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
void DataControlSourceV1InterfacePrivate::zwlr_data_control_source_v1_destroy_resource(QtWaylandServer::zwlr_data_control_source_v1::Resource *resource)
|
||||
{
|
||||
Q_EMIT q->aboutToBeDestroyed();
|
||||
delete q;
|
||||
}
|
||||
|
||||
void DataControlSourceV1InterfacePrivate::zwlr_data_control_source_v1_offer(Resource *, const QString &mimeType)
|
||||
{
|
||||
mimeTypes << mimeType;
|
||||
Q_EMIT q->mimeTypeOffered(mimeType);
|
||||
}
|
||||
|
||||
void DataControlSourceV1InterfacePrivate::zwlr_data_control_source_v1_destroy(QtWaylandServer::zwlr_data_control_source_v1::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
DataControlSourceV1Interface::DataControlSourceV1Interface(DataControlDeviceManagerV1Interface *parent, ::wl_resource *resource)
|
||||
: AbstractDataSource(parent)
|
||||
, d(new DataControlSourceV1InterfacePrivate(this, resource))
|
||||
{
|
||||
}
|
||||
|
||||
DataControlSourceV1Interface::~DataControlSourceV1Interface() = default;
|
||||
|
||||
void DataControlSourceV1Interface::requestData(const QString &mimeType, qint32 fd)
|
||||
{
|
||||
d->send_send(mimeType, fd);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void DataControlSourceV1Interface::cancel()
|
||||
{
|
||||
d->send_cancelled();
|
||||
}
|
||||
|
||||
QStringList DataControlSourceV1Interface::mimeTypes() const
|
||||
{
|
||||
return d->mimeTypes;
|
||||
}
|
||||
|
||||
wl_client *DataControlSourceV1Interface::client() const
|
||||
{
|
||||
return d->resource()->client();
|
||||
}
|
||||
|
||||
DataControlSourceV1Interface *DataControlSourceV1Interface::get(wl_resource *native)
|
||||
{
|
||||
if (auto sourcePrivate = resource_cast<DataControlSourceV1InterfacePrivate *>(native)) {
|
||||
return sourcePrivate->q;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_datacontrolsource_v1.cpp"
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include "abstract_data_source.h"
|
||||
#include "datacontroldevicemanager_v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataControlSourceV1InterfacePrivate;
|
||||
|
||||
/**
|
||||
* The DataControlSourceV1Interface class represents the source side in a data transfer.
|
||||
*
|
||||
* DataControlSourceV1Interface corresponds to the wayland interface zwlr_data_control_source_v1.
|
||||
*/
|
||||
class KWIN_EXPORT DataControlSourceV1Interface : public AbstractDataSource
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~DataControlSourceV1Interface() override;
|
||||
|
||||
void requestData(const QString &mimeType, qint32 fd) override;
|
||||
void cancel() override;
|
||||
|
||||
QStringList mimeTypes() const override;
|
||||
wl_client *client() const override;
|
||||
|
||||
static DataControlSourceV1Interface *get(wl_resource *native);
|
||||
|
||||
private:
|
||||
friend class DataControlDeviceManagerV1InterfacePrivate;
|
||||
explicit DataControlSourceV1Interface(DataControlDeviceManagerV1Interface *parent, ::wl_resource *resource);
|
||||
|
||||
std::unique_ptr<DataControlSourceV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::DataControlSourceV1Interface *)
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "datadevice.h"
|
||||
#include "datadevice_p.h"
|
||||
#include "datadevicemanager.h"
|
||||
#include "dataoffer.h"
|
||||
#include "datasource.h"
|
||||
#include "display.h"
|
||||
#include "pointer.h"
|
||||
#include "seat.h"
|
||||
#include "seat_p.h"
|
||||
#include "surface.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DragAndDropIconPrivate
|
||||
{
|
||||
public:
|
||||
explicit DragAndDropIconPrivate(SurfaceInterface *surface);
|
||||
|
||||
QPointer<SurfaceInterface> surface;
|
||||
QPoint position;
|
||||
};
|
||||
|
||||
DragAndDropIconPrivate::DragAndDropIconPrivate(SurfaceInterface *surface)
|
||||
: surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface)
|
||||
: QObject(surface)
|
||||
, d(new DragAndDropIconPrivate(surface))
|
||||
{
|
||||
connect(surface, &SurfaceInterface::committed, this, &DragAndDropIcon::commit);
|
||||
}
|
||||
|
||||
DragAndDropIcon::~DragAndDropIcon()
|
||||
{
|
||||
}
|
||||
|
||||
SurfaceRole *DragAndDropIcon::role()
|
||||
{
|
||||
static SurfaceRole role(QByteArrayLiteral("dnd_icon"));
|
||||
return &role;
|
||||
}
|
||||
|
||||
void DragAndDropIcon::commit()
|
||||
{
|
||||
d->position += d->surface->offset();
|
||||
Q_EMIT changed();
|
||||
}
|
||||
|
||||
QPoint DragAndDropIcon::position() const
|
||||
{
|
||||
return d->position;
|
||||
}
|
||||
|
||||
SurfaceInterface *DragAndDropIcon::surface() const
|
||||
{
|
||||
return d->surface;
|
||||
}
|
||||
|
||||
DataDeviceInterfacePrivate *DataDeviceInterfacePrivate::get(DataDeviceInterface *device)
|
||||
{
|
||||
return device->d.get();
|
||||
}
|
||||
|
||||
DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource)
|
||||
: QtWaylandServer::wl_data_device(resource)
|
||||
, seat(seat)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
|
||||
wl_resource *sourceResource,
|
||||
wl_resource *originResource,
|
||||
wl_resource *iconResource,
|
||||
uint32_t serial)
|
||||
{
|
||||
SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
|
||||
DataSourceInterface *dataSource = nullptr;
|
||||
if (sourceResource) {
|
||||
dataSource = DataSourceInterface::get(sourceResource);
|
||||
}
|
||||
|
||||
const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
|
||||
if (!pointerGrab) {
|
||||
// Client doesn't have pointer grab.
|
||||
const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->isSurfaceTouched(focusSurface);
|
||||
if (!touchGrab) {
|
||||
// Client neither has pointer nor touch grab. No drag start allowed.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DragAndDropIcon *dragIcon = nullptr;
|
||||
if (SurfaceInterface *iconSurface = SurfaceInterface::get(iconResource)) {
|
||||
if (const SurfaceRole *role = iconSurface->role()) {
|
||||
if (role != DragAndDropIcon::role()) {
|
||||
wl_resource_post_error(resource->handle, 0,
|
||||
"the wl_surface already has a role assigned %s", role->name().constData());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
iconSurface->setRole(DragAndDropIcon::role());
|
||||
}
|
||||
|
||||
// drag icon lifespan is mapped to surface lifespan
|
||||
dragIcon = new DragAndDropIcon(iconSurface);
|
||||
}
|
||||
drag.serial = serial;
|
||||
Q_EMIT q->dragStarted(dataSource, focusSurface, serial, dragIcon);
|
||||
}
|
||||
|
||||
void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
|
||||
{
|
||||
DataSourceInterface *dataSource = DataSourceInterface::get(source);
|
||||
|
||||
if (dataSource && dataSource->supportedDragAndDropActions() && wl_resource_get_version(dataSource->resource()) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
|
||||
wl_resource_post_error(dataSource->resource(), QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataSource && dataSource->xdgToplevelDrag()) {
|
||||
wl_resource_post_error(resource->handle, QtWaylandServer::wl_data_source::error_invalid_source, "Data source is for drag and drop");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selection == dataSource) {
|
||||
return;
|
||||
}
|
||||
if (selection) {
|
||||
selection->cancel();
|
||||
}
|
||||
selection = dataSource;
|
||||
Q_EMIT q->selectionChanged(selection, serial);
|
||||
}
|
||||
|
||||
void DataDeviceInterfacePrivate::data_device_release(QtWaylandServer::wl_data_device::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
DataOfferInterface *DataDeviceInterfacePrivate::createDataOffer(AbstractDataSource *source)
|
||||
{
|
||||
if (!source) {
|
||||
// a data offer can only exist together with a source
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &wl_data_offer_interface, resource()->version(), 0);
|
||||
if (!data_offer_resource) {
|
||||
wl_resource_post_no_memory(resource()->handle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataOfferInterface *offer = new DataOfferInterface(source, data_offer_resource);
|
||||
send_data_offer(offer->resource());
|
||||
offer->sendAllOffers();
|
||||
return offer;
|
||||
}
|
||||
|
||||
void DataDeviceInterfacePrivate::data_device_destroy_resource(QtWaylandServer::wl_data_device::Resource *resource)
|
||||
{
|
||||
Q_EMIT q->aboutToBeDestroyed();
|
||||
delete q;
|
||||
}
|
||||
|
||||
DataDeviceInterface::DataDeviceInterface(SeatInterface *seat, wl_resource *resource)
|
||||
: AbstractDropHandler(nullptr)
|
||||
, d(new DataDeviceInterfacePrivate(seat, this, resource))
|
||||
{
|
||||
SeatInterfacePrivate *seatPrivate = SeatInterfacePrivate::get(seat);
|
||||
seatPrivate->registerDataDevice(this);
|
||||
}
|
||||
|
||||
DataDeviceInterface::~DataDeviceInterface() = default;
|
||||
|
||||
SeatInterface *DataDeviceInterface::seat() const
|
||||
{
|
||||
return d->seat;
|
||||
}
|
||||
|
||||
DataSourceInterface *DataDeviceInterface::selection() const
|
||||
{
|
||||
return d->selection;
|
||||
}
|
||||
|
||||
void DataDeviceInterface::sendSelection(AbstractDataSource *other)
|
||||
{
|
||||
auto r = other ? d->createDataOffer(other) : nullptr;
|
||||
d->send_selection(r ? r->resource() : nullptr);
|
||||
}
|
||||
|
||||
void DataDeviceInterface::drop()
|
||||
{
|
||||
d->send_drop();
|
||||
d->drag.surface = nullptr; // prevent sending wl_data_device.leave event
|
||||
|
||||
disconnect(d->drag.posConnection);
|
||||
d->drag.posConnection = QMetaObject::Connection();
|
||||
disconnect(d->drag.destroyConnection);
|
||||
d->drag.destroyConnection = QMetaObject::Connection();
|
||||
|
||||
if (d->seat->dragSource()->selectedDndAction() != DataDeviceManagerInterface::DnDAction::Ask) {
|
||||
disconnect(d->drag.sourceActionConnection);
|
||||
d->drag.sourceActionConnection = QMetaObject::Connection();
|
||||
disconnect(d->drag.targetActionConnection);
|
||||
d->drag.targetActionConnection = QMetaObject::Connection();
|
||||
disconnect(d->drag.keyboardModifiersConnection);
|
||||
d->drag.keyboardModifiersConnection = QMetaObject::Connection();
|
||||
}
|
||||
}
|
||||
|
||||
static DataDeviceManagerInterface::DnDAction chooseDndAction(AbstractDataSource *source, DataOfferInterface *offer, Qt::KeyboardModifiers keyboardModifiers)
|
||||
{
|
||||
// first compositor picks an action if modifiers are pressed and it's supported both sides
|
||||
if (keyboardModifiers.testFlag(Qt::ControlModifier)) {
|
||||
if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy) && offer->supportedDragAndDropActions().has_value() && offer->supportedDragAndDropActions()->testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
|
||||
return DataDeviceManagerInterface::DnDAction::Copy;
|
||||
}
|
||||
}
|
||||
if (keyboardModifiers.testFlag(Qt::ShiftModifier)) {
|
||||
if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move) && offer->supportedDragAndDropActions().has_value() && offer->supportedDragAndDropActions()->testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
|
||||
return DataDeviceManagerInterface::DnDAction::Move;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise we pick the preferred action from the target if the source supported it
|
||||
if (offer->preferredDragAndDropAction().has_value()) {
|
||||
if (source->supportedDragAndDropActions().testFlag(*offer->preferredDragAndDropAction())) {
|
||||
return *offer->preferredDragAndDropAction();
|
||||
}
|
||||
}
|
||||
|
||||
// finally pick something everyone supports in a deterministic fashion
|
||||
if (offer->supportedDragAndDropActions().has_value()) {
|
||||
for (const auto &action : {DataDeviceManagerInterface::DnDAction::Copy, DataDeviceManagerInterface::DnDAction::Move, DataDeviceManagerInterface::DnDAction::Ask}) {
|
||||
if (source->supportedDragAndDropActions().testFlag(action) && offer->supportedDragAndDropActions()->testFlag(action)) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DataDeviceManagerInterface::DnDAction::None;
|
||||
}
|
||||
|
||||
void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 serial)
|
||||
{
|
||||
if (d->drag.surface == surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->drag.surface) {
|
||||
d->send_leave();
|
||||
|
||||
if (d->drag.posConnection) {
|
||||
disconnect(d->drag.posConnection);
|
||||
d->drag.posConnection = QMetaObject::Connection();
|
||||
}
|
||||
disconnect(d->drag.destroyConnection);
|
||||
d->drag.destroyConnection = QMetaObject::Connection();
|
||||
d->drag.surface = nullptr;
|
||||
if (d->drag.sourceActionConnection) {
|
||||
disconnect(d->drag.sourceActionConnection);
|
||||
d->drag.sourceActionConnection = QMetaObject::Connection();
|
||||
}
|
||||
if (d->drag.targetActionConnection) {
|
||||
disconnect(d->drag.targetActionConnection);
|
||||
d->drag.targetActionConnection = QMetaObject::Connection();
|
||||
}
|
||||
if (d->drag.keyboardModifiersConnection) {
|
||||
disconnect(d->drag.keyboardModifiersConnection);
|
||||
d->drag.keyboardModifiersConnection = QMetaObject::Connection();
|
||||
}
|
||||
// don't update serial, we need it
|
||||
}
|
||||
auto dragSource = d->seat->dragSource();
|
||||
if (!surface || !dragSource) {
|
||||
if (auto s = dragSource) {
|
||||
s->dndAction(DataDeviceManagerInterface::DnDAction::None);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dragSource) {
|
||||
dragSource->accept(QString());
|
||||
}
|
||||
DataOfferInterface *offer = d->createDataOffer(dragSource);
|
||||
offer->sendSourceActions();
|
||||
|
||||
d->drag.surface = surface;
|
||||
if (d->seat->isDragPointer()) {
|
||||
d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] {
|
||||
const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
|
||||
d->send_motion(d->seat->timestamp().count(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
|
||||
});
|
||||
} else if (d->seat->isDragTouch()) {
|
||||
// When dragging from one window to another, we may end up in a data_device
|
||||
// that didn't get "data_device_start_drag". In that case, the internal
|
||||
// touch point serial will be incorrect and we need to update it to the
|
||||
// serial from the seat.
|
||||
SeatInterfacePrivate *seatPrivate = SeatInterfacePrivate::get(seat());
|
||||
if (seatPrivate->drag.dragImplicitGrabSerial != d->drag.serial) {
|
||||
d->drag.serial = seatPrivate->drag.dragImplicitGrabSerial.value();
|
||||
}
|
||||
|
||||
d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this, [this](qint32 id, quint32 serial, const QPointF &globalPosition) {
|
||||
if (serial != d->drag.serial) {
|
||||
// different touch down has been moved
|
||||
return;
|
||||
}
|
||||
const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition);
|
||||
d->send_motion(d->seat->timestamp().count(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()));
|
||||
});
|
||||
}
|
||||
d->drag.destroyConnection = connect(d->drag.surface, &SurfaceInterface::aboutToBeDestroyed, this, [this] {
|
||||
d->send_leave();
|
||||
if (d->drag.posConnection) {
|
||||
disconnect(d->drag.posConnection);
|
||||
}
|
||||
if (d->drag.sourceActionConnection) {
|
||||
disconnect(d->drag.sourceActionConnection);
|
||||
}
|
||||
if (d->drag.targetActionConnection) {
|
||||
disconnect(d->drag.targetActionConnection);
|
||||
}
|
||||
d->drag = DataDeviceInterfacePrivate::Drag();
|
||||
});
|
||||
|
||||
QPointF pos;
|
||||
if (d->seat->isDragPointer()) {
|
||||
pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
|
||||
} else if (d->seat->isDragTouch()) {
|
||||
pos = d->seat->dragSurfaceTransformation().map(d->seat->firstTouchPointPosition(surface));
|
||||
}
|
||||
d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
|
||||
if (offer) {
|
||||
auto matchOffers = [this, dragSource, offer] {
|
||||
Qt::KeyboardModifiers keyboardModifiers;
|
||||
if (d->seat->isDrag()) { // ignore keyboard modifiers when in "ask" negotiation
|
||||
keyboardModifiers = dragSource->keyboardModifiers();
|
||||
}
|
||||
|
||||
const DataDeviceManagerInterface::DnDAction action = chooseDndAction(dragSource, offer, keyboardModifiers);
|
||||
offer->dndAction(action);
|
||||
dragSource->dndAction(action);
|
||||
};
|
||||
matchOffers();
|
||||
d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, dragSource, matchOffers);
|
||||
d->drag.sourceActionConnection = connect(dragSource, &AbstractDataSource::supportedDragAndDropActionsChanged, offer, matchOffers);
|
||||
d->drag.keyboardModifiersConnection = connect(dragSource, &AbstractDataSource::keyboardModifiersChanged, offer, matchOffers);
|
||||
}
|
||||
}
|
||||
|
||||
wl_client *DataDeviceInterface::client()
|
||||
{
|
||||
return d->resource()->client();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_datadevice.cpp"
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
#include "abstract_drop_handler.h"
|
||||
|
||||
struct wl_client;
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataDeviceManagerInterface;
|
||||
class DataOfferInterface;
|
||||
class DataSourceInterface;
|
||||
class AbstractDataSource;
|
||||
class SeatInterface;
|
||||
class SurfaceInterface;
|
||||
class SurfaceRole;
|
||||
class DataDeviceInterfacePrivate;
|
||||
class DragAndDropIconPrivate;
|
||||
|
||||
/**
|
||||
* The DragAndDropIcon class represents a drag-and-drop icon.
|
||||
*
|
||||
* Note that the lifetime of the drag-and-drop icon is bound to the lifetime of the underlying
|
||||
* icon surface.
|
||||
*/
|
||||
class KWIN_EXPORT DragAndDropIcon : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~DragAndDropIcon() override;
|
||||
|
||||
static SurfaceRole *role();
|
||||
|
||||
/**
|
||||
* Returns the position of the icon relative to the cursor's hotspot.
|
||||
*/
|
||||
QPoint position() const;
|
||||
|
||||
/**
|
||||
* Returns the underlying icon surface. This function always returns a valid surface.
|
||||
*/
|
||||
SurfaceInterface *surface() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed();
|
||||
|
||||
private:
|
||||
void commit();
|
||||
|
||||
explicit DragAndDropIcon(SurfaceInterface *surface);
|
||||
friend class DataDeviceInterfacePrivate;
|
||||
std::unique_ptr<DragAndDropIconPrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DataDeviceInterface allows clients to share data by copy-and-paste and drag-and-drop.
|
||||
*
|
||||
* The data device is per seat.
|
||||
* Copy-and-paste use the selection functions.
|
||||
*
|
||||
* Represents the Resource for the wl_data_device interface.
|
||||
*
|
||||
* @see SeatInterface
|
||||
* @see DataSourceInterface
|
||||
*/
|
||||
class KWIN_EXPORT DataDeviceInterface : public AbstractDropHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~DataDeviceInterface();
|
||||
|
||||
SeatInterface *seat() const;
|
||||
|
||||
DataSourceInterface *selection() const;
|
||||
|
||||
void sendSelection(KWin::AbstractDataSource *other);
|
||||
/**
|
||||
* The event is sent when a drag-and-drop operation is ended because the implicit grab is removed.
|
||||
*/
|
||||
void drop() override;
|
||||
/**
|
||||
* Updates the SurfaceInterface to which drag motion events are sent.
|
||||
*
|
||||
* If a SurfaceInterface was registered in this DataDeviceInterface for drag motion events, it
|
||||
* will be sent a leave event.
|
||||
*
|
||||
* If @p surface is not null it will be sent a drag enter event.
|
||||
*
|
||||
* @param surface The SurfaceInterface which gets motion events
|
||||
* @param serial The serial to be used for enter/leave
|
||||
*/
|
||||
void updateDragTarget(SurfaceInterface *surface, quint32 serial) override;
|
||||
|
||||
wl_client *client();
|
||||
|
||||
Q_SIGNALS:
|
||||
void aboutToBeDestroyed();
|
||||
void dragStarted(AbstractDataSource *source, SurfaceInterface *originSurface, quint32 serial, DragAndDropIcon *dragIcon);
|
||||
void selectionChanged(KWin::DataSourceInterface *, quint32 serial);
|
||||
|
||||
private:
|
||||
friend class DataDeviceManagerInterfacePrivate;
|
||||
explicit DataDeviceInterface(SeatInterface *seat, wl_resource *resource);
|
||||
std::unique_ptr<DataDeviceInterfacePrivate> d;
|
||||
friend class DataDeviceInterfacePrivate;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::DataDeviceInterface *)
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
#include "qwayland-server-wayland.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class AbstractDataSource;
|
||||
class DataDeviceInterface;
|
||||
class DataOfferInterface;
|
||||
class DataSourceInterface;
|
||||
class DragAndDropIcon;
|
||||
class SeatInterface;
|
||||
class SurfaceInterface;
|
||||
|
||||
class DataDeviceInterfacePrivate : public QtWaylandServer::wl_data_device
|
||||
{
|
||||
public:
|
||||
static DataDeviceInterfacePrivate *get(DataDeviceInterface *device);
|
||||
|
||||
DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource);
|
||||
|
||||
DataOfferInterface *createDataOffer(AbstractDataSource *source);
|
||||
|
||||
SeatInterface *seat;
|
||||
DataDeviceInterface *q;
|
||||
QPointer<DataSourceInterface> selection;
|
||||
|
||||
struct Drag
|
||||
{
|
||||
SurfaceInterface *surface = nullptr;
|
||||
QMetaObject::Connection destroyConnection;
|
||||
QMetaObject::Connection posConnection;
|
||||
QMetaObject::Connection sourceActionConnection;
|
||||
QMetaObject::Connection targetActionConnection;
|
||||
QMetaObject::Connection keyboardModifiersConnection;
|
||||
quint32 serial = 0;
|
||||
};
|
||||
Drag drag;
|
||||
|
||||
protected:
|
||||
void data_device_destroy_resource(Resource *resource) override;
|
||||
void data_device_start_drag(Resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) override;
|
||||
void data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial) override;
|
||||
void data_device_release(Resource *resource) override;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "datadevicemanager.h"
|
||||
#include "datasource.h"
|
||||
#include "display.h"
|
||||
#include "seat_p.h"
|
||||
// Wayland
|
||||
#include <qwayland-server-wayland.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 3;
|
||||
|
||||
class DataDeviceManagerInterfacePrivate : public QtWaylandServer::wl_data_device_manager
|
||||
{
|
||||
public:
|
||||
DataDeviceManagerInterfacePrivate(DataDeviceManagerInterface *q, Display *d);
|
||||
|
||||
DataDeviceManagerInterface *q;
|
||||
|
||||
private:
|
||||
void createDataSource(wl_client *client, wl_resource *resource, uint32_t id);
|
||||
void getDataDevice(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *seat);
|
||||
|
||||
protected:
|
||||
void data_device_manager_create_data_source(Resource *resource, uint32_t id) override;
|
||||
void data_device_manager_get_data_device(Resource *resource, uint32_t id, wl_resource *seat) override;
|
||||
};
|
||||
|
||||
DataDeviceManagerInterfacePrivate::DataDeviceManagerInterfacePrivate(DataDeviceManagerInterface *q, Display *d)
|
||||
: QtWaylandServer::wl_data_device_manager(*d, s_version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void DataDeviceManagerInterfacePrivate::data_device_manager_create_data_source(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *data_source_resource = wl_resource_create(resource->client(), &wl_data_source_interface, resource->version(), id);
|
||||
if (!data_source_resource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
DataSourceInterface *dataSource = new DataSourceInterface(data_source_resource);
|
||||
Q_EMIT q->dataSourceCreated(dataSource);
|
||||
}
|
||||
|
||||
void DataDeviceManagerInterfacePrivate::data_device_manager_get_data_device(Resource *resource, uint32_t id, wl_resource *seat)
|
||||
{
|
||||
SeatInterface *s = SeatInterface::get(seat);
|
||||
Q_ASSERT(s);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *data_device_resource = wl_resource_create(resource->client(), &wl_data_device_interface, resource->version(), id);
|
||||
if (!data_device_resource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
DataDeviceInterface *dataDevice = new DataDeviceInterface(s, data_device_resource);
|
||||
Q_EMIT q->dataDeviceCreated(dataDevice);
|
||||
}
|
||||
|
||||
DataDeviceManagerInterface::DataDeviceManagerInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new DataDeviceManagerInterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
DataDeviceManagerInterface::~DataDeviceManagerInterface() = default;
|
||||
|
||||
}
|
||||
|
||||
#include "moc_datadevicemanager.cpp"
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
#include "datadevice.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class DataSourceInterface;
|
||||
class DataDeviceManagerInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Represents the Global for wl_data_device_manager interface.
|
||||
*
|
||||
*/
|
||||
class KWIN_EXPORT DataDeviceManagerInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DataDeviceManagerInterface(Display *display, QObject *parent = nullptr);
|
||||
~DataDeviceManagerInterface() override;
|
||||
|
||||
/**
|
||||
* Drag and Drop actions supported by the DataSourceInterface.
|
||||
*/
|
||||
enum class DnDAction {
|
||||
None = 0,
|
||||
Copy = 1 << 0,
|
||||
Move = 1 << 1,
|
||||
Ask = 1 << 2,
|
||||
};
|
||||
Q_DECLARE_FLAGS(DnDActions, DnDAction)
|
||||
|
||||
Q_SIGNALS:
|
||||
void dataSourceCreated(KWin::DataSourceInterface *);
|
||||
void dataDeviceCreated(KWin::DataDeviceInterface *);
|
||||
|
||||
private:
|
||||
std::unique_ptr<DataDeviceManagerInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::DataDeviceManagerInterface::DnDActions)
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "dataoffer.h"
|
||||
#include "datadevice.h"
|
||||
#include "datasource.h"
|
||||
|
||||
// Qt
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
// Wayland
|
||||
#include <qwayland-server-wayland.h>
|
||||
// system
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataOfferInterfacePrivate : public QtWaylandServer::wl_data_offer
|
||||
{
|
||||
public:
|
||||
DataOfferInterfacePrivate(AbstractDataSource *source, DataOfferInterface *q, wl_resource *resource);
|
||||
DataOfferInterface *q;
|
||||
QPointer<AbstractDataSource> source;
|
||||
|
||||
std::optional<DataDeviceManagerInterface::DnDActions> supportedDnDActions = std::nullopt;
|
||||
std::optional<DataDeviceManagerInterface::DnDAction> preferredDnDAction = std::nullopt;
|
||||
|
||||
protected:
|
||||
void data_offer_destroy_resource(Resource *resource) override;
|
||||
void data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type) override;
|
||||
void data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd) override;
|
||||
void data_offer_destroy(Resource *resource) override;
|
||||
void data_offer_finish(Resource *resource) override;
|
||||
void data_offer_set_actions(Resource *resource, uint32_t dnd_actions, uint32_t preferred_action) override;
|
||||
};
|
||||
|
||||
DataOfferInterfacePrivate::DataOfferInterfacePrivate(AbstractDataSource *_source, DataOfferInterface *_q, wl_resource *resource)
|
||||
: QtWaylandServer::wl_data_offer(resource)
|
||||
, q(_q)
|
||||
, source(_source)
|
||||
{
|
||||
// defaults are set to sensible values for < version 3 interfaces
|
||||
if (wl_resource_get_version(resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) {
|
||||
supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy | DataDeviceManagerInterface::DnDAction::Move;
|
||||
preferredDnDAction = DataDeviceManagerInterface::DnDAction::Copy;
|
||||
}
|
||||
}
|
||||
|
||||
void DataOfferInterfacePrivate::data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type)
|
||||
{
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
source->accept(mime_type);
|
||||
}
|
||||
|
||||
void DataOfferInterfacePrivate::data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd)
|
||||
{
|
||||
if (!source) {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
source->requestData(mime_type, fd);
|
||||
}
|
||||
|
||||
void DataOfferInterfacePrivate::data_offer_destroy(QtWaylandServer::wl_data_offer::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DataOfferInterfacePrivate::data_offer_finish(Resource *resource)
|
||||
{
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
source->dndFinished();
|
||||
source.clear();
|
||||
// TODO: It is a client error to perform other requests than wl_data_offer.destroy after this one
|
||||
}
|
||||
|
||||
void DataOfferInterfacePrivate::data_offer_set_actions(Resource *resource, uint32_t dnd_actions, uint32_t preferred_action)
|
||||
{
|
||||
// TODO: check it's drag and drop, otherwise send error
|
||||
// verify that the no other actions are sent
|
||||
if (dnd_actions
|
||||
& ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move
|
||||
| QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_action_mask, "Invalid action mask");
|
||||
return;
|
||||
}
|
||||
|
||||
if (preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_copy
|
||||
&& preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_move
|
||||
&& preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_ask
|
||||
&& preferred_action != QtWaylandServer::wl_data_device_manager::dnd_action_none) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_action, "Invalid preferred action");
|
||||
return;
|
||||
}
|
||||
|
||||
DataDeviceManagerInterface::DnDActions supportedActions;
|
||||
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
|
||||
supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
|
||||
}
|
||||
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
|
||||
supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
|
||||
}
|
||||
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
|
||||
supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
|
||||
}
|
||||
|
||||
DataDeviceManagerInterface::DnDAction preferredAction = DataDeviceManagerInterface::DnDAction::None;
|
||||
if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
|
||||
preferredAction = DataDeviceManagerInterface::DnDAction::Copy;
|
||||
} else if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_move) {
|
||||
preferredAction = DataDeviceManagerInterface::DnDAction::Move;
|
||||
} else if (preferred_action == QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
|
||||
preferredAction = DataDeviceManagerInterface::DnDAction::Ask;
|
||||
}
|
||||
|
||||
if (supportedDnDActions != supportedActions || preferredDnDAction != preferredAction) {
|
||||
supportedDnDActions = supportedActions;
|
||||
preferredDnDAction = preferredAction;
|
||||
Q_EMIT q->dragAndDropActionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void DataOfferInterface::sendSourceActions()
|
||||
{
|
||||
if (!d->source) {
|
||||
return;
|
||||
}
|
||||
if (d->resource()->version() < WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) {
|
||||
return;
|
||||
}
|
||||
uint32_t wlActions = QtWaylandServer::wl_data_device_manager::dnd_action_none;
|
||||
const auto actions = d->source->supportedDragAndDropActions();
|
||||
if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
|
||||
wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_copy;
|
||||
}
|
||||
if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
|
||||
wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_move;
|
||||
}
|
||||
if (actions.testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
|
||||
wlActions |= QtWaylandServer::wl_data_device_manager::dnd_action_ask;
|
||||
}
|
||||
d->send_source_actions(wlActions);
|
||||
}
|
||||
|
||||
void DataOfferInterfacePrivate::data_offer_destroy_resource(QtWaylandServer::wl_data_offer::Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
DataOfferInterface::DataOfferInterface(AbstractDataSource *source, wl_resource *resource)
|
||||
: QObject(nullptr)
|
||||
, d(new DataOfferInterfacePrivate(source, this, resource))
|
||||
{
|
||||
Q_ASSERT(source);
|
||||
connect(source, &DataSourceInterface::mimeTypeOffered, this, [this](const QString &mimeType) {
|
||||
d->send_offer(mimeType);
|
||||
});
|
||||
}
|
||||
|
||||
DataOfferInterface::~DataOfferInterface()
|
||||
{
|
||||
if (d->source && d->source->isDropPerformed()) {
|
||||
d->source->dndFinished();
|
||||
d->source.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void DataOfferInterface::sendAllOffers()
|
||||
{
|
||||
for (const QString &mimeType : d->source->mimeTypes()) {
|
||||
d->send_offer(mimeType);
|
||||
}
|
||||
}
|
||||
|
||||
wl_resource *DataOfferInterface::resource() const
|
||||
{
|
||||
return d->resource()->handle;
|
||||
}
|
||||
|
||||
std::optional<DataDeviceManagerInterface::DnDActions> DataOfferInterface::supportedDragAndDropActions() const
|
||||
{
|
||||
return d->supportedDnDActions;
|
||||
}
|
||||
|
||||
std::optional<DataDeviceManagerInterface::DnDAction> DataOfferInterface::preferredDragAndDropAction() const
|
||||
{
|
||||
return d->preferredDnDAction;
|
||||
}
|
||||
|
||||
void DataOfferInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
|
||||
{
|
||||
if (d->resource()->version() < WL_DATA_OFFER_ACTION_SINCE_VERSION) {
|
||||
return;
|
||||
}
|
||||
uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
|
||||
if (action == DataDeviceManagerInterface::DnDAction::Copy) {
|
||||
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
|
||||
} else if (action == DataDeviceManagerInterface::DnDAction::Move) {
|
||||
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
|
||||
} else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
|
||||
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
|
||||
}
|
||||
d->send_action(wlAction);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_dataoffer.cpp"
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "datadevicemanager.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataDeviceInterface;
|
||||
class AbstractDataSource;
|
||||
class DataOfferInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Represents the Resource for the wl_data_offer interface.
|
||||
*
|
||||
*/
|
||||
class KWIN_EXPORT DataOfferInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~DataOfferInterface();
|
||||
|
||||
void sendAllOffers();
|
||||
void sendSourceActions();
|
||||
wl_resource *resource() const;
|
||||
|
||||
/**
|
||||
* @returns The Drag and Drop actions supported by this DataOfferInterface.
|
||||
*/
|
||||
std::optional<DataDeviceManagerInterface::DnDActions> supportedDragAndDropActions() const;
|
||||
|
||||
/**
|
||||
* @returns The preferred Drag and Drop action of this DataOfferInterface.
|
||||
*/
|
||||
std::optional<DataDeviceManagerInterface::DnDAction> preferredDragAndDropAction() const;
|
||||
|
||||
/**
|
||||
* This event indicates the @p action selected by the compositor after matching the
|
||||
* source/destination side actions. Only one action (or none) will be offered here.
|
||||
*/
|
||||
void dndAction(DataDeviceManagerInterface::DnDAction action);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted whenever the supported or preferred Drag and Drop actions changed.
|
||||
*/
|
||||
void dragAndDropActionsChanged();
|
||||
|
||||
private:
|
||||
friend class DataDeviceInterfacePrivate;
|
||||
explicit DataOfferInterface(AbstractDataSource *source, wl_resource *resource);
|
||||
|
||||
std::unique_ptr<DataOfferInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::DataOfferInterface *)
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "datasource.h"
|
||||
#include "clientconnection.h"
|
||||
#include "datadevicemanager.h"
|
||||
#include "datasource_p.h"
|
||||
#include "utils/resource.h"
|
||||
// Qt
|
||||
#include <QStringList>
|
||||
// Wayland
|
||||
#include <qwayland-server-wayland.h>
|
||||
// system
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
DataSourceInterfacePrivate::DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource)
|
||||
: QtWaylandServer::wl_data_source(resource)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
void DataSourceInterfacePrivate::data_source_destroy_resource(Resource *resource)
|
||||
{
|
||||
Q_EMIT q->aboutToBeDestroyed();
|
||||
delete q;
|
||||
}
|
||||
|
||||
void DataSourceInterfacePrivate::data_source_offer(QtWaylandServer::wl_data_source::Resource *resource, const QString &mime_type)
|
||||
{
|
||||
mimeTypes << mime_type;
|
||||
Q_EMIT q->mimeTypeOffered(mime_type);
|
||||
}
|
||||
|
||||
void DataSourceInterfacePrivate::data_source_destroy(QtWaylandServer::wl_data_source::Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DataSourceInterfacePrivate::offer(const QString &mimeType)
|
||||
{
|
||||
mimeTypes << mimeType;
|
||||
Q_EMIT q->mimeTypeOffered(mimeType);
|
||||
}
|
||||
|
||||
void DataSourceInterfacePrivate::data_source_set_actions(Resource *resource, uint32_t dnd_actions)
|
||||
{
|
||||
// verify that the no other actions are sent
|
||||
if (dnd_actions
|
||||
& ~(QtWaylandServer::wl_data_device_manager::dnd_action_copy | QtWaylandServer::wl_data_device_manager::dnd_action_move
|
||||
| QtWaylandServer::wl_data_device_manager::dnd_action_ask)) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_action_mask, "Invalid action mask");
|
||||
return;
|
||||
}
|
||||
DataDeviceManagerInterface::DnDActions supportedActions;
|
||||
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_copy) {
|
||||
supportedActions |= DataDeviceManagerInterface::DnDAction::Copy;
|
||||
}
|
||||
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_move) {
|
||||
supportedActions |= DataDeviceManagerInterface::DnDAction::Move;
|
||||
}
|
||||
if (dnd_actions & QtWaylandServer::wl_data_device_manager::dnd_action_ask) {
|
||||
supportedActions |= DataDeviceManagerInterface::DnDAction::Ask;
|
||||
}
|
||||
if (supportedDnDActions != supportedActions) {
|
||||
supportedDnDActions = supportedActions;
|
||||
Q_EMIT q->supportedDragAndDropActionsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
DataSourceInterfacePrivate *DataSourceInterfacePrivate::get(DataSourceInterface *dataSource)
|
||||
{
|
||||
return dataSource->d.get();
|
||||
}
|
||||
|
||||
DataSourceInterface::DataSourceInterface(wl_resource *resource)
|
||||
: d(new DataSourceInterfacePrivate(this, resource))
|
||||
{
|
||||
if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
|
||||
d->supportedDnDActions = DataDeviceManagerInterface::DnDAction::Copy;
|
||||
}
|
||||
}
|
||||
|
||||
DataSourceInterface::~DataSourceInterface() = default;
|
||||
|
||||
void DataSourceInterface::accept(const QString &mimeType)
|
||||
{
|
||||
d->send_target(mimeType);
|
||||
d->isAccepted = !mimeType.isNull();
|
||||
Q_EMIT acceptedChanged();
|
||||
}
|
||||
|
||||
void DataSourceInterface::requestData(const QString &mimeType, qint32 fd)
|
||||
{
|
||||
d->send_send(mimeType, int32_t(fd));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void DataSourceInterface::cancel()
|
||||
{
|
||||
d->send_cancelled();
|
||||
}
|
||||
|
||||
QStringList DataSourceInterface::mimeTypes() const
|
||||
{
|
||||
return d->mimeTypes;
|
||||
}
|
||||
|
||||
DataSourceInterface *DataSourceInterface::get(wl_resource *native)
|
||||
{
|
||||
if (auto sourcePrivate = resource_cast<DataSourceInterfacePrivate *>(native)) {
|
||||
return sourcePrivate->q;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DataDeviceManagerInterface::DnDActions DataSourceInterface::supportedDragAndDropActions() const
|
||||
{
|
||||
return d->supportedDnDActions;
|
||||
}
|
||||
|
||||
DataDeviceManagerInterface::DnDAction DataSourceInterface::selectedDndAction() const
|
||||
{
|
||||
return d->selectedDndAction;
|
||||
}
|
||||
|
||||
void DataSourceInterface::dropPerformed()
|
||||
{
|
||||
AbstractDataSource::dropPerformed();
|
||||
if (d->resource()->version() < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
|
||||
return;
|
||||
}
|
||||
d->send_dnd_drop_performed();
|
||||
}
|
||||
|
||||
void DataSourceInterface::dndFinished()
|
||||
{
|
||||
AbstractDataSource::dndFinished();
|
||||
if (d->resource()->version() < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
|
||||
return;
|
||||
}
|
||||
d->send_dnd_finished();
|
||||
}
|
||||
|
||||
void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
|
||||
{
|
||||
d->selectedDndAction = action;
|
||||
Q_EMIT dndActionChanged();
|
||||
|
||||
if (d->resource()->version() < WL_DATA_SOURCE_ACTION_SINCE_VERSION) {
|
||||
return;
|
||||
}
|
||||
uint32_t wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_none;
|
||||
if (action == DataDeviceManagerInterface::DnDAction::Copy) {
|
||||
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_copy;
|
||||
} else if (action == DataDeviceManagerInterface::DnDAction::Move) {
|
||||
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_move;
|
||||
} else if (action == DataDeviceManagerInterface::DnDAction::Ask) {
|
||||
wlAction = QtWaylandServer::wl_data_device_manager::dnd_action_ask;
|
||||
}
|
||||
d->send_action(wlAction);
|
||||
}
|
||||
|
||||
void DataSourceInterface::dndCancelled()
|
||||
{
|
||||
AbstractDataSource::dndCancelled();
|
||||
// for v3 or less, cancel should not be called after a failed drag operation
|
||||
if (wl_resource_get_version(resource()) < 3) {
|
||||
return;
|
||||
}
|
||||
d->send_cancelled();
|
||||
}
|
||||
|
||||
wl_resource *DataSourceInterface::resource() const
|
||||
{
|
||||
return d->resource()->handle;
|
||||
}
|
||||
|
||||
wl_client *DataSourceInterface::client() const
|
||||
{
|
||||
return d->resource()->client();
|
||||
}
|
||||
|
||||
bool DataSourceInterface::isAccepted() const
|
||||
{
|
||||
return d->isAccepted;
|
||||
}
|
||||
|
||||
XdgToplevelDragV1Interface *DataSourceInterface::xdgToplevelDrag() const
|
||||
{
|
||||
return d->xdgToplevelDrag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_datasource.cpp"
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include "abstract_data_source.h"
|
||||
#include "datadevicemanager.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataSourceInterfacePrivate;
|
||||
class XdgToplevelDragV1Interface;
|
||||
|
||||
/**
|
||||
* @brief Represents the Resource for the wl_data_source interface.
|
||||
*/
|
||||
class KWIN_EXPORT DataSourceInterface : public AbstractDataSource
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~DataSourceInterface();
|
||||
|
||||
void accept(const QString &mimeType) override;
|
||||
void requestData(const QString &mimeType, qint32 fd) override;
|
||||
void cancel() override;
|
||||
|
||||
QStringList mimeTypes() const override;
|
||||
|
||||
static DataSourceInterface *get(wl_resource *native);
|
||||
|
||||
/**
|
||||
* @returns The Drag and Drop actions supported by this DataSourceInterface.
|
||||
*/
|
||||
DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const override;
|
||||
DataDeviceManagerInterface::DnDAction selectedDndAction() const override;
|
||||
void dropPerformed() override;
|
||||
void dndFinished() override;
|
||||
void dndAction(DataDeviceManagerInterface::DnDAction action) override;
|
||||
void dndCancelled() override;
|
||||
|
||||
wl_resource *resource() const;
|
||||
|
||||
wl_client *client() const override;
|
||||
|
||||
bool isAccepted() const override;
|
||||
|
||||
XdgToplevelDragV1Interface *xdgToplevelDrag() const;
|
||||
|
||||
private:
|
||||
friend class DataDeviceManagerInterfacePrivate;
|
||||
friend class DataSourceInterfacePrivate;
|
||||
explicit DataSourceInterface(wl_resource *resource);
|
||||
|
||||
std::unique_ptr<DataSourceInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::DataSourceInterface *)
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qwayland-server-wayland.h>
|
||||
|
||||
#include "datadevicemanager.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class DataSourceInterface;
|
||||
class XdgToplevelDragV1Interface;
|
||||
|
||||
class DataSourceInterfacePrivate : public QtWaylandServer::wl_data_source
|
||||
{
|
||||
public:
|
||||
DataSourceInterfacePrivate(DataSourceInterface *_q, ::wl_resource *resource);
|
||||
|
||||
static DataSourceInterfacePrivate *get(DataSourceInterface *dataSource);
|
||||
|
||||
DataSourceInterface *q;
|
||||
QStringList mimeTypes;
|
||||
DataDeviceManagerInterface::DnDActions supportedDnDActions = DataDeviceManagerInterface::DnDAction::None;
|
||||
DataDeviceManagerInterface::DnDAction selectedDndAction = DataDeviceManagerInterface::DnDAction::None;
|
||||
bool isAccepted = false;
|
||||
bool dropPerformed = false;
|
||||
bool isCanceled = false;
|
||||
XdgToplevelDragV1Interface *xdgToplevelDrag = nullptr;
|
||||
|
||||
protected:
|
||||
void data_source_destroy_resource(Resource *resource) override;
|
||||
void data_source_offer(Resource *resource, const QString &mime_type) override;
|
||||
void data_source_destroy(Resource *resource) override;
|
||||
void data_source_set_actions(Resource *resource, uint32_t dnd_actions) override;
|
||||
|
||||
private:
|
||||
void offer(const QString &mimeType);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "display.h"
|
||||
|
||||
#include "config-kwin.h"
|
||||
|
||||
#include "clientconnection.h"
|
||||
#include "display_p.h"
|
||||
#include "linuxdmabufv1clientbuffer_p.h"
|
||||
#include "output.h"
|
||||
#include "shmclientbuffer_p.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QRect>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
DisplayPrivate *DisplayPrivate::get(Display *display)
|
||||
{
|
||||
return display->d.get();
|
||||
}
|
||||
|
||||
DisplayPrivate::DisplayPrivate(Display *q)
|
||||
: q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void DisplayPrivate::registerSocketName(const QString &socketName)
|
||||
{
|
||||
socketNames.append(socketName);
|
||||
Q_EMIT q->socketNamesChanged();
|
||||
}
|
||||
|
||||
Display::Display(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new DisplayPrivate(this))
|
||||
{
|
||||
d->display = wl_display_create();
|
||||
d->loop = wl_display_get_event_loop(d->display);
|
||||
}
|
||||
|
||||
Display::~Display()
|
||||
{
|
||||
wl_display_destroy_clients(d->display);
|
||||
wl_display_destroy(d->display);
|
||||
}
|
||||
|
||||
bool Display::addSocketFileDescriptor(int fileDescriptor, const QString &name)
|
||||
{
|
||||
if (wl_display_add_socket_fd(d->display, fileDescriptor)) {
|
||||
qCWarning(KWIN_CORE, "Failed to add %d fd to display", fileDescriptor);
|
||||
return false;
|
||||
}
|
||||
if (!name.isEmpty()) {
|
||||
d->registerSocketName(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Display::addSocketName(const QString &name)
|
||||
{
|
||||
if (name.isEmpty()) {
|
||||
const char *socket = wl_display_add_socket_auto(d->display);
|
||||
if (!socket) {
|
||||
qCWarning(KWIN_CORE, "Failed to find a free display socket");
|
||||
return false;
|
||||
}
|
||||
d->registerSocketName(QString::fromUtf8(socket));
|
||||
} else {
|
||||
if (wl_display_add_socket(d->display, qPrintable(name))) {
|
||||
qCWarning(KWIN_CORE, "Failed to add %s socket to display", qPrintable(name));
|
||||
return false;
|
||||
}
|
||||
d->registerSocketName(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList Display::socketNames() const
|
||||
{
|
||||
return d->socketNames;
|
||||
}
|
||||
|
||||
bool Display::start()
|
||||
{
|
||||
if (d->running) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const int fileDescriptor = wl_event_loop_get_fd(d->loop);
|
||||
if (fileDescriptor == -1) {
|
||||
qCWarning(KWIN_CORE) << "Did not get the file descriptor for the event loop";
|
||||
return false;
|
||||
}
|
||||
|
||||
d->socketNotifier = new QSocketNotifier(fileDescriptor, QSocketNotifier::Read, this);
|
||||
connect(d->socketNotifier, &QSocketNotifier::activated, this, &Display::dispatchEvents);
|
||||
|
||||
QAbstractEventDispatcher *dispatcher = QCoreApplication::eventDispatcher();
|
||||
connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, &Display::flush);
|
||||
|
||||
d->running = true;
|
||||
Q_EMIT runningChanged(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Display::dispatchEvents()
|
||||
{
|
||||
if (wl_event_loop_dispatch(d->loop, 0) != 0) {
|
||||
qCWarning(KWIN_CORE) << "Error on dispatching Wayland event loop";
|
||||
}
|
||||
}
|
||||
|
||||
void Display::flush()
|
||||
{
|
||||
wl_display_flush_clients(d->display);
|
||||
}
|
||||
|
||||
void Display::createShm()
|
||||
{
|
||||
Q_ASSERT(d->display);
|
||||
new ShmClientBufferIntegration(this);
|
||||
}
|
||||
|
||||
quint32 Display::nextSerial()
|
||||
{
|
||||
return wl_display_next_serial(d->display);
|
||||
}
|
||||
|
||||
quint32 Display::serial()
|
||||
{
|
||||
return wl_display_get_serial(d->display);
|
||||
}
|
||||
|
||||
bool Display::isRunning() const
|
||||
{
|
||||
return d->running;
|
||||
}
|
||||
|
||||
Display::operator wl_display *()
|
||||
{
|
||||
return d->display;
|
||||
}
|
||||
|
||||
Display::operator wl_display *() const
|
||||
{
|
||||
return d->display;
|
||||
}
|
||||
|
||||
QList<OutputInterface *> Display::outputs() const
|
||||
{
|
||||
return d->outputs;
|
||||
}
|
||||
|
||||
QList<OutputDeviceV2Interface *> Display::outputDevices() const
|
||||
{
|
||||
return d->outputdevicesV2;
|
||||
}
|
||||
|
||||
QList<OutputInterface *> Display::outputsIntersecting(const QRect &rect) const
|
||||
{
|
||||
QList<OutputInterface *> outputs;
|
||||
for (auto *output : std::as_const(d->outputs)) {
|
||||
if (output->handle()->geometry().intersects(rect)) {
|
||||
outputs << output;
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
OutputInterface *Display::largestIntersectingOutput(const QRect &rect) const
|
||||
{
|
||||
OutputInterface *returnOutput = nullptr;
|
||||
uint64_t biggestArea = 0;
|
||||
for (auto *output : std::as_const(d->outputs)) {
|
||||
const QRect intersect = output->handle()->geometry().intersected(rect);
|
||||
const uint64_t area = intersect.width() * intersect.height();
|
||||
if (area > biggestArea) {
|
||||
biggestArea = area;
|
||||
returnOutput = output;
|
||||
}
|
||||
}
|
||||
return returnOutput;
|
||||
}
|
||||
|
||||
QList<SeatInterface *> Display::seats() const
|
||||
{
|
||||
return d->seats;
|
||||
}
|
||||
|
||||
ClientConnection *Display::getConnection(wl_client *client)
|
||||
{
|
||||
// TODO: Use wl_client_set_user_data() when we start requiring libwayland-server that has it, and remove client lists here and in ClientConnection.
|
||||
Q_ASSERT(client);
|
||||
auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), [client](ClientConnection *c) {
|
||||
return c->client() == client;
|
||||
});
|
||||
if (it != d->clients.constEnd()) {
|
||||
return *it;
|
||||
}
|
||||
// no ConnectionData yet, create it
|
||||
auto c = new ClientConnection(client, this);
|
||||
d->clients << c;
|
||||
connect(c, &ClientConnection::disconnected, this, [this](ClientConnection *c) {
|
||||
Q_EMIT clientDisconnected(c);
|
||||
});
|
||||
connect(c, &ClientConnection::destroyed, this, [this, c]() {
|
||||
d->clients.removeOne(c);
|
||||
});
|
||||
Q_EMIT clientConnected(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
ClientConnection *Display::createClient(int fd)
|
||||
{
|
||||
Q_ASSERT(fd != -1);
|
||||
Q_ASSERT(d->display);
|
||||
wl_client *c = wl_client_create(d->display, fd);
|
||||
if (!c) {
|
||||
return nullptr;
|
||||
}
|
||||
return getConnection(c);
|
||||
}
|
||||
|
||||
GraphicsBuffer *Display::bufferForResource(wl_resource *resource)
|
||||
{
|
||||
if (auto buffer = LinuxDmaBufV1ClientBuffer::get(resource)) {
|
||||
return buffer;
|
||||
} else if (auto buffer = ShmClientBuffer::get(resource)) {
|
||||
return buffer;
|
||||
} else {
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "Failed to find matching GraphicsBuffer for wl_resource");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Display::setDefaultMaxBufferSize(size_t max)
|
||||
{
|
||||
#if HAVE_WL_DISPLAY_SET_DEFAULT_MAX_BUFFER_SIZE
|
||||
wl_display_set_default_max_buffer_size(d->display, max);
|
||||
#endif
|
||||
}
|
||||
|
||||
SecurityContext::SecurityContext(Display *display, FileDescriptor &&listenFd, FileDescriptor &&closeFd, const QString &appId)
|
||||
: QObject(display)
|
||||
, m_display(display)
|
||||
, m_listenFd(std::move(listenFd))
|
||||
, m_closeFd(std::move(closeFd))
|
||||
, m_appId(appId)
|
||||
{
|
||||
qCDebug(KWIN_CORE) << "Adding listen fd for" << appId;
|
||||
|
||||
auto closeSocketWatcher = new QSocketNotifier(m_closeFd.get(), QSocketNotifier::Read, this);
|
||||
connect(closeSocketWatcher, &QSocketNotifier::activated, this, &SecurityContext::onCloseFdActivated);
|
||||
|
||||
if (m_closeFd.isClosed()) {
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
auto listenFdListener = new QSocketNotifier(m_listenFd.get(), QSocketNotifier::Read, this);
|
||||
connect(listenFdListener, &QSocketNotifier::activated, this, &SecurityContext::onListenFdActivated);
|
||||
}
|
||||
|
||||
SecurityContext::~SecurityContext()
|
||||
{
|
||||
qCDebug(KWIN_CORE) << "Removing listen fd for " << m_appId;
|
||||
}
|
||||
|
||||
void SecurityContext::onListenFdActivated(QSocketDescriptor socketDescriptor)
|
||||
{
|
||||
const int clientFd = accept4(socketDescriptor, nullptr, nullptr, SOCK_CLOEXEC);
|
||||
if (clientFd < 0) {
|
||||
qCWarning(KWIN_CORE) << "Failed to accept client from security listen FD:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
auto client = m_display->createClient(clientFd);
|
||||
if (!client) {
|
||||
close(clientFd);
|
||||
return;
|
||||
}
|
||||
|
||||
client->setSecurityContextAppId(m_appId);
|
||||
}
|
||||
|
||||
void SecurityContext::onCloseFdActivated()
|
||||
{
|
||||
if (m_closeFd.isClosed()) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_display.cpp"
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
struct wl_client;
|
||||
struct wl_display;
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class ClientConnection;
|
||||
class DisplayPrivate;
|
||||
class OutputInterface;
|
||||
class OutputDeviceV2Interface;
|
||||
class SeatInterface;
|
||||
class GraphicsBuffer;
|
||||
|
||||
/**
|
||||
* @brief Class holding the Wayland server display loop.
|
||||
*
|
||||
* @todo Improve documentation
|
||||
*/
|
||||
class KWIN_EXPORT Display : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged)
|
||||
public:
|
||||
explicit Display(QObject *parent = nullptr);
|
||||
virtual ~Display();
|
||||
|
||||
/**
|
||||
* Adds a socket with the given @p fileDescriptor to the Wayland display. This function
|
||||
* returns @c true if the socket has been added successfully; otherwise returns @c false.
|
||||
*
|
||||
* The compositor can call this function even after the display has been started.
|
||||
* @arg socketName can optionally be parsed to store the socket name represented by the given file-descriptor
|
||||
*
|
||||
* @see start()
|
||||
*/
|
||||
bool addSocketFileDescriptor(int fileDescriptor, const QString &socketName = QString());
|
||||
/**
|
||||
* Adds a UNIX socket with the specified @p name to the Wayland display. This function
|
||||
* returns @c true if the socket has been added successfully; otherwise returns @c false.
|
||||
*
|
||||
* If the specified socket name @p name is empty, the display will pick a free socket with
|
||||
* a filename "wayland-%d".
|
||||
*
|
||||
* The compositor can call this function even after the display has been started.
|
||||
*
|
||||
* @see start()
|
||||
*/
|
||||
bool addSocketName(const QString &name = QString());
|
||||
|
||||
/**
|
||||
* Returns the list of socket names that the display listens for client connections.
|
||||
*/
|
||||
QStringList socketNames() const;
|
||||
|
||||
quint32 serial();
|
||||
quint32 nextSerial();
|
||||
|
||||
/**
|
||||
* Start accepting client connections. If the display has started successfully, this
|
||||
* function returns @c true; otherwise @c false is returned.
|
||||
*/
|
||||
bool start();
|
||||
void dispatchEvents();
|
||||
|
||||
/**
|
||||
* Create a client for the given file descriptor.
|
||||
*
|
||||
* The client is created as if it connected through the normal server
|
||||
* socket. This method can be used to create a connection bypassing the
|
||||
* normal socket connection. It's recommended to use together with
|
||||
* socketpair and pass the other side of the socket to the client.
|
||||
*
|
||||
* @param fd The file descriptor for the socket to the client
|
||||
* @returns The new ClientConnection or @c null on failure.
|
||||
*/
|
||||
ClientConnection *createClient(int fd);
|
||||
|
||||
operator wl_display *();
|
||||
operator wl_display *() const;
|
||||
bool isRunning() const;
|
||||
|
||||
void createShm();
|
||||
/**
|
||||
* @returns All SeatInterface currently managed on the Display.
|
||||
*/
|
||||
QList<SeatInterface *> seats() const;
|
||||
QList<OutputDeviceV2Interface *> outputDevices() const;
|
||||
QList<OutputInterface *> outputs() const;
|
||||
QList<OutputInterface *> outputsIntersecting(const QRect &rect) const;
|
||||
OutputInterface *largestIntersectingOutput(const QRect &rect) const;
|
||||
|
||||
/**
|
||||
* Gets the ClientConnection for the given @p client.
|
||||
* If there is no ClientConnection yet for the given @p client, it will be created.
|
||||
* @param client The native client for which the ClientConnection is retrieved
|
||||
* @return The ClientConnection for the given native client
|
||||
*/
|
||||
ClientConnection *getConnection(wl_client *client);
|
||||
|
||||
/**
|
||||
* Returns the graphics buffer for the given @a resource, or @c null if there's no buffer.
|
||||
*/
|
||||
static GraphicsBuffer *bufferForResource(wl_resource *resource);
|
||||
|
||||
/**
|
||||
* Sets the default maximum size for connection buffers of new clients. The size is in bytes.
|
||||
* The minimum buffer size is 4096.
|
||||
*/
|
||||
void setDefaultMaxBufferSize(size_t max);
|
||||
|
||||
public Q_SLOTS:
|
||||
void flush();
|
||||
|
||||
Q_SIGNALS:
|
||||
void socketNamesChanged();
|
||||
void runningChanged(bool);
|
||||
void clientConnected(KWin::ClientConnection *);
|
||||
void clientDisconnected(KWin::ClientConnection *);
|
||||
|
||||
private:
|
||||
friend class DisplayPrivate;
|
||||
std::unique_ptr<DisplayPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2018 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "utils/filedescriptor.h"
|
||||
#include <QList>
|
||||
#include <QSocketNotifier>
|
||||
#include <QString>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ClientConnection;
|
||||
class Display;
|
||||
class OutputInterface;
|
||||
class OutputDeviceV2Interface;
|
||||
class SeatInterface;
|
||||
|
||||
class DisplayPrivate
|
||||
{
|
||||
public:
|
||||
static DisplayPrivate *get(Display *display);
|
||||
DisplayPrivate(Display *q);
|
||||
|
||||
void registerSocketName(const QString &socketName);
|
||||
|
||||
Display *q;
|
||||
QSocketNotifier *socketNotifier = nullptr;
|
||||
wl_display *display = nullptr;
|
||||
wl_event_loop *loop = nullptr;
|
||||
bool running = false;
|
||||
QList<OutputInterface *> outputs;
|
||||
QList<OutputDeviceV2Interface *> outputdevicesV2;
|
||||
QList<SeatInterface *> seats;
|
||||
QList<ClientConnection *> clients;
|
||||
QStringList socketNames;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The SecurityContext is a helper for the SecurityContextProtocol
|
||||
* It stays alive whilst closeFd remains open, listening for new connections on listenFd
|
||||
* Any new clients created via listenFd are tagged with the appId
|
||||
* It is parented to the display
|
||||
*/
|
||||
class SecurityContext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SecurityContext(Display *display, FileDescriptor &&listenFd, FileDescriptor &&closeFd, const QString &appId);
|
||||
~SecurityContext() override;
|
||||
|
||||
private:
|
||||
void onCloseFdActivated();
|
||||
void onListenFdActivated(QSocketDescriptor descriptor);
|
||||
Display *m_display;
|
||||
FileDescriptor m_listenFd;
|
||||
FileDescriptor m_closeFd;
|
||||
QString m_appId;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "dpms.h"
|
||||
#include "display.h"
|
||||
#include "output.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
#include <qwayland-server-dpms.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
class DpmsManagerInterfacePrivate : public QtWaylandServer::org_kde_kwin_dpms_manager
|
||||
{
|
||||
public:
|
||||
DpmsManagerInterfacePrivate(Display *d);
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_dpms_manager_get(Resource *resource, uint32_t id, wl_resource *output) override;
|
||||
};
|
||||
|
||||
class DpmsInterface : public QObject, QtWaylandServer::org_kde_kwin_dpms
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DpmsInterface(OutputInterface *output, wl_resource *resource);
|
||||
|
||||
void sendSupported();
|
||||
void sendMode();
|
||||
void sendDone();
|
||||
|
||||
QPointer<OutputInterface> m_output;
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_dpms_destroy_resource(Resource *resource) override;
|
||||
void org_kde_kwin_dpms_set(Resource *resource, uint32_t mode) override;
|
||||
void org_kde_kwin_dpms_release(Resource *resource) override;
|
||||
};
|
||||
|
||||
DpmsManagerInterfacePrivate::DpmsManagerInterfacePrivate(Display *display)
|
||||
: QtWaylandServer::org_kde_kwin_dpms_manager(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void DpmsManagerInterfacePrivate::org_kde_kwin_dpms_manager_get(Resource *resource, uint32_t id, wl_resource *output)
|
||||
{
|
||||
OutputInterface *o = OutputInterface::get(output);
|
||||
|
||||
wl_resource *dpms_resource = wl_resource_create(resource->client(), &org_kde_kwin_dpms_interface, resource->version(), id);
|
||||
if (!dpms_resource) {
|
||||
wl_client_post_no_memory(resource->client());
|
||||
return;
|
||||
}
|
||||
|
||||
new DpmsInterface(o, dpms_resource);
|
||||
}
|
||||
|
||||
DpmsManagerInterface::DpmsManagerInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new DpmsManagerInterfacePrivate(display))
|
||||
{
|
||||
}
|
||||
|
||||
DpmsManagerInterface::~DpmsManagerInterface() = default;
|
||||
|
||||
DpmsInterface::DpmsInterface(OutputInterface *output, wl_resource *resource)
|
||||
: QObject()
|
||||
, QtWaylandServer::org_kde_kwin_dpms(resource)
|
||||
, m_output(output)
|
||||
{
|
||||
if (!m_output || m_output->isRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendSupported();
|
||||
sendMode();
|
||||
sendDone();
|
||||
|
||||
connect(m_output->handle(), &Output::capabilitiesChanged, this, [this]() {
|
||||
sendSupported();
|
||||
sendDone();
|
||||
});
|
||||
connect(m_output->handle(), &Output::dpmsModeChanged, this, [this]() {
|
||||
sendMode();
|
||||
sendDone();
|
||||
});
|
||||
}
|
||||
|
||||
void DpmsInterface::org_kde_kwin_dpms_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DpmsInterface::org_kde_kwin_dpms_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void DpmsInterface::org_kde_kwin_dpms_set(Resource *resource, uint32_t mode)
|
||||
{
|
||||
if (!m_output || m_output->isRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Output::DpmsMode dpmsMode;
|
||||
switch (mode) {
|
||||
case ORG_KDE_KWIN_DPMS_MODE_ON:
|
||||
dpmsMode = Output::DpmsMode::On;
|
||||
break;
|
||||
case ORG_KDE_KWIN_DPMS_MODE_STANDBY:
|
||||
dpmsMode = Output::DpmsMode::Standby;
|
||||
break;
|
||||
case ORG_KDE_KWIN_DPMS_MODE_SUSPEND:
|
||||
dpmsMode = Output::DpmsMode::Suspend;
|
||||
break;
|
||||
case ORG_KDE_KWIN_DPMS_MODE_OFF:
|
||||
dpmsMode = Output::DpmsMode::Off;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
m_output->handle()->setDpmsMode(dpmsMode);
|
||||
}
|
||||
|
||||
void DpmsInterface::sendSupported()
|
||||
{
|
||||
if (!m_output || m_output->isRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_supported(m_output->handle()->capabilities() & Output::Capability::Dpms ? 1 : 0);
|
||||
}
|
||||
|
||||
void DpmsInterface::sendMode()
|
||||
{
|
||||
if (!m_output || m_output->isRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mode = m_output->handle()->dpmsMode();
|
||||
org_kde_kwin_dpms_mode wlMode;
|
||||
switch (mode) {
|
||||
case Output::DpmsMode::On:
|
||||
case Output::DpmsMode::AboutToTurnOff:
|
||||
wlMode = ORG_KDE_KWIN_DPMS_MODE_ON;
|
||||
break;
|
||||
case Output::DpmsMode::Standby:
|
||||
wlMode = ORG_KDE_KWIN_DPMS_MODE_STANDBY;
|
||||
break;
|
||||
case Output::DpmsMode::Suspend:
|
||||
wlMode = ORG_KDE_KWIN_DPMS_MODE_SUSPEND;
|
||||
break;
|
||||
case Output::DpmsMode::Off:
|
||||
wlMode = ORG_KDE_KWIN_DPMS_MODE_OFF;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
send_mode(wlMode);
|
||||
}
|
||||
|
||||
void DpmsInterface::sendDone()
|
||||
{
|
||||
send_done();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "dpms.moc"
|
||||
|
||||
#include "moc_dpms.cpp"
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class DpmsManagerInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Global for server side Display Power Management Signaling interface.
|
||||
*
|
||||
* A DpmsManagerInterface allows a client to query the DPMS state
|
||||
* on a given OutputInterface and request changes to it.
|
||||
* Server-side the interaction happens only via the OutputInterface,
|
||||
* for clients the Dpms class provides the API.
|
||||
* This global implements org_kde_kwin_dpms_manager.
|
||||
*
|
||||
* To create a DpmsManagerInterface use:
|
||||
* @code
|
||||
* auto manager = display->createDpmsManager();
|
||||
* manager->create();
|
||||
* @endcode
|
||||
*
|
||||
* To interact with Dpms use one needs to mark it as enabled and set the
|
||||
* proper mode on the OutputInterface.
|
||||
* @code
|
||||
* // We have our OutputInterface called output.
|
||||
* output->setDpmsSupported(true);
|
||||
* output->setDpmsMode(Output::DpmsMode::On);
|
||||
* @endcode
|
||||
*
|
||||
* To connect to Dpms change requests use:
|
||||
* @code
|
||||
* connect(output, &Output::DpmsModeRequested,
|
||||
* [] (Output::DpmsMode requestedMode) { qDebug() << "Mode change requested"; });
|
||||
* @endcode
|
||||
*
|
||||
* @see Display
|
||||
* @see OutputInterface
|
||||
*/
|
||||
class KWIN_EXPORT DpmsManagerInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DpmsManagerInterface(Display *display, QObject *parent = nullptr);
|
||||
~DpmsManagerInterface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<DpmsManagerInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "drmclientbuffer.h"
|
||||
#include "display.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
#include "qwayland-server-drm.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr int s_version = 2;
|
||||
|
||||
class DrmClientBufferIntegrationPrivate : public QtWaylandServer::wl_drm
|
||||
{
|
||||
public:
|
||||
explicit DrmClientBufferIntegrationPrivate(Display *display);
|
||||
|
||||
QString nodeName;
|
||||
|
||||
protected:
|
||||
void drm_bind_resource(Resource *resource) override;
|
||||
void drm_authenticate(Resource *resource, uint32_t id) override;
|
||||
void drm_create_buffer(Resource *resource,
|
||||
uint32_t id,
|
||||
uint32_t name,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t stride,
|
||||
uint32_t format) override;
|
||||
void drm_create_planar_buffer(Resource *resource,
|
||||
uint32_t id,
|
||||
uint32_t name,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t format,
|
||||
int32_t offset0,
|
||||
int32_t stride0,
|
||||
int32_t offset1,
|
||||
int32_t stride1,
|
||||
int32_t offset2,
|
||||
int32_t stride2) override;
|
||||
void drm_create_prime_buffer(Resource *resource,
|
||||
uint32_t id,
|
||||
int32_t name,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t format,
|
||||
int32_t offset0,
|
||||
int32_t stride0,
|
||||
int32_t offset1,
|
||||
int32_t stride1,
|
||||
int32_t offset2,
|
||||
int32_t stride2) override;
|
||||
};
|
||||
|
||||
DrmClientBufferIntegrationPrivate::DrmClientBufferIntegrationPrivate(Display *display)
|
||||
: QtWaylandServer::wl_drm(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void DrmClientBufferIntegrationPrivate::drm_bind_resource(Resource *resource)
|
||||
{
|
||||
send_device(resource->handle, nodeName);
|
||||
send_capabilities(resource->handle, capability_prime);
|
||||
}
|
||||
|
||||
void DrmClientBufferIntegrationPrivate::drm_authenticate(Resource *resource, uint32_t id)
|
||||
{
|
||||
send_authenticated(resource->handle);
|
||||
}
|
||||
|
||||
void DrmClientBufferIntegrationPrivate::drm_create_buffer(Resource *resource,
|
||||
uint32_t id,
|
||||
uint32_t name,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t stride,
|
||||
uint32_t format)
|
||||
{
|
||||
wl_resource_post_error(resource->handle, 0, "wl_drm.create_buffer is not implemented");
|
||||
}
|
||||
|
||||
void DrmClientBufferIntegrationPrivate::drm_create_planar_buffer(Resource *resource,
|
||||
uint32_t id,
|
||||
uint32_t name,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t format,
|
||||
int32_t offset0,
|
||||
int32_t stride0,
|
||||
int32_t offset1,
|
||||
int32_t stride1,
|
||||
int32_t offset2,
|
||||
int32_t stride2)
|
||||
{
|
||||
wl_resource_post_error(resource->handle, 0, "wl_drm.create_planar_buffer is not implemented");
|
||||
}
|
||||
|
||||
void DrmClientBufferIntegrationPrivate::drm_create_prime_buffer(Resource *resource,
|
||||
uint32_t id,
|
||||
int32_t name,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t format,
|
||||
int32_t offset0,
|
||||
int32_t stride0,
|
||||
int32_t offset1,
|
||||
int32_t stride1,
|
||||
int32_t offset2,
|
||||
int32_t stride2)
|
||||
{
|
||||
close(name);
|
||||
wl_resource_post_error(resource->handle, 0, "wl_drm.create_prime_buffer is not implemented");
|
||||
}
|
||||
|
||||
DrmClientBufferIntegration::DrmClientBufferIntegration(Display *display)
|
||||
: QObject(display)
|
||||
, d(std::make_unique<DrmClientBufferIntegrationPrivate>(display))
|
||||
{
|
||||
}
|
||||
|
||||
DrmClientBufferIntegration::~DrmClientBufferIntegration()
|
||||
{
|
||||
}
|
||||
|
||||
void DrmClientBufferIntegration::setDevice(const QString &node)
|
||||
{
|
||||
d->nodeName = node;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_drmclientbuffer.cpp"
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class DrmClientBufferIntegrationPrivate;
|
||||
|
||||
/**
|
||||
* The DrmClientBufferIntegration provides a stub implementation for the wl_drm
|
||||
* protocol.
|
||||
*
|
||||
* It provides the minimum amount of information to Xwayland so it can run. No
|
||||
* GraphicsBuffers are provided by the DrmClientBufferIntegration. Xwayland is
|
||||
* expected to provide us linux dmabuf client buffers instead.
|
||||
*
|
||||
* Once the wl_drm protocol is no longer mandatory in Xwayland, this stub can be
|
||||
* dropped.
|
||||
*/
|
||||
class KWIN_EXPORT DrmClientBufferIntegration : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DrmClientBufferIntegration(Display *display);
|
||||
~DrmClientBufferIntegration() override;
|
||||
|
||||
void setDevice(const QString &node);
|
||||
|
||||
private:
|
||||
std::unique_ptr<DrmClientBufferIntegrationPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021-2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "drmlease_v1.h"
|
||||
#include "display.h"
|
||||
#include "drmlease_v1_p.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/resource.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
DrmLeaseManagerV1::DrmLeaseManagerV1(DrmBackend *backend, Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_backend(backend)
|
||||
, m_display(display)
|
||||
{
|
||||
const auto &gpus = m_backend->gpus();
|
||||
for (const auto &gpu : gpus) {
|
||||
addGpu(gpu.get());
|
||||
}
|
||||
connect(m_backend, &DrmBackend::gpuAdded, this, &DrmLeaseManagerV1::addGpu);
|
||||
connect(m_backend, &DrmBackend::gpuRemoved, this, &DrmLeaseManagerV1::removeGpu);
|
||||
connect(m_backend, &DrmBackend::outputsQueried, this, &DrmLeaseManagerV1::handleOutputsQueried);
|
||||
}
|
||||
|
||||
DrmLeaseManagerV1::~DrmLeaseManagerV1()
|
||||
{
|
||||
for (const auto device : m_leaseDevices) {
|
||||
device->remove();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseManagerV1::addGpu(DrmGpu *gpu)
|
||||
{
|
||||
m_leaseDevices[gpu] = new DrmLeaseDeviceV1Interface(m_display, gpu);
|
||||
}
|
||||
|
||||
void DrmLeaseManagerV1::removeGpu(DrmGpu *gpu)
|
||||
{
|
||||
if (auto device = m_leaseDevices.take(gpu)) {
|
||||
device->remove();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseManagerV1::handleOutputsQueried()
|
||||
{
|
||||
for (const auto device : m_leaseDevices) {
|
||||
device->done();
|
||||
device->setDrmMaster(device->gpu()->isActive());
|
||||
}
|
||||
}
|
||||
|
||||
DrmLeaseDeviceV1Interface::DrmLeaseDeviceV1Interface(Display *display, DrmGpu *gpu)
|
||||
: QtWaylandServer::wp_drm_lease_device_v1(*display, s_version)
|
||||
, m_gpu(gpu)
|
||||
{
|
||||
const auto outputs = gpu->drmOutputs();
|
||||
for (const auto output : outputs) {
|
||||
addOutput(output);
|
||||
}
|
||||
connect(gpu, &DrmGpu::outputAdded, this, &DrmLeaseDeviceV1Interface::addOutput);
|
||||
connect(gpu, &DrmGpu::outputRemoved, this, &DrmLeaseDeviceV1Interface::removeOutput);
|
||||
connect(gpu, &DrmGpu::activeChanged, this, &DrmLeaseDeviceV1Interface::setDrmMaster);
|
||||
}
|
||||
|
||||
DrmLeaseDeviceV1Interface::~DrmLeaseDeviceV1Interface()
|
||||
{
|
||||
while (!m_connectors.empty()) {
|
||||
removeOutput(m_connectors.begin()->first);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::addOutput(DrmAbstractOutput *output)
|
||||
{
|
||||
DrmOutput *drmOutput = qobject_cast<DrmOutput *>(output);
|
||||
if (!drmOutput || !drmOutput->isNonDesktop()) {
|
||||
return;
|
||||
}
|
||||
m_connectors[drmOutput] = std::make_unique<DrmLeaseConnectorV1Interface>(this, drmOutput);
|
||||
|
||||
if (m_hasDrmMaster) {
|
||||
offerConnector(m_connectors[drmOutput].get());
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::removeOutput(DrmAbstractOutput *output)
|
||||
{
|
||||
const auto it = m_connectors.find(output);
|
||||
if (it != m_connectors.end()) {
|
||||
DrmLeaseConnectorV1Interface *connector = it->second.get();
|
||||
connector->withdraw();
|
||||
for (const auto &lease : std::as_const(m_leases)) {
|
||||
if (lease->connectors().contains(connector)) {
|
||||
lease->connectors().removeOne(connector);
|
||||
lease->revoke();
|
||||
}
|
||||
}
|
||||
for (const auto &leaseRequest : std::as_const(m_leaseRequests)) {
|
||||
if (leaseRequest->connectors().contains(connector)) {
|
||||
leaseRequest->invalidate();
|
||||
}
|
||||
}
|
||||
m_connectors.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::setDrmMaster(bool hasDrmMaster)
|
||||
{
|
||||
if (hasDrmMaster == m_hasDrmMaster) {
|
||||
return;
|
||||
}
|
||||
if (hasDrmMaster) {
|
||||
// send pending drm fds
|
||||
while (!m_pendingFds.isEmpty()) {
|
||||
FileDescriptor fd = m_gpu->createNonMasterFd();
|
||||
send_drm_fd(m_pendingFds.dequeue(), fd.get());
|
||||
}
|
||||
// offer all connectors again
|
||||
for (const auto &[output, connector] : m_connectors) {
|
||||
offerConnector(connector.get());
|
||||
}
|
||||
} else {
|
||||
// withdraw all connectors
|
||||
for (const auto &[output, connector] : m_connectors) {
|
||||
connector->withdraw();
|
||||
}
|
||||
// and revoke all leases
|
||||
for (const auto &lease : std::as_const(m_leases)) {
|
||||
lease->revoke();
|
||||
}
|
||||
}
|
||||
m_hasDrmMaster = hasDrmMaster;
|
||||
done();
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::done()
|
||||
{
|
||||
const auto resources = resourceMap();
|
||||
for (const auto resource : resources) {
|
||||
send_done(resource->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::remove()
|
||||
{
|
||||
for (const auto &lease : std::as_const(m_leases)) {
|
||||
lease->deny();
|
||||
}
|
||||
for (const auto &[output, connector] : m_connectors) {
|
||||
connector->withdraw();
|
||||
}
|
||||
for (const auto &request : std::as_const(m_leaseRequests)) {
|
||||
request->invalidate();
|
||||
}
|
||||
done();
|
||||
globalRemove();
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::addLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
|
||||
{
|
||||
m_leaseRequests.push_back(leaseRequest);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::removeLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
|
||||
{
|
||||
m_leaseRequests.removeOne(leaseRequest);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::addLease(DrmLeaseV1Interface *lease)
|
||||
{
|
||||
m_leases.push_back(lease);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::removeLease(DrmLeaseV1Interface *lease)
|
||||
{
|
||||
m_leases.removeOne(lease);
|
||||
}
|
||||
|
||||
bool DrmLeaseDeviceV1Interface::hasDrmMaster() const
|
||||
{
|
||||
return m_hasDrmMaster;
|
||||
}
|
||||
|
||||
DrmGpu *DrmLeaseDeviceV1Interface::gpu() const
|
||||
{
|
||||
return m_gpu;
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::offerConnector(DrmLeaseConnectorV1Interface *connector)
|
||||
{
|
||||
for (const auto &resource : resourceMap()) {
|
||||
auto connectorResource = connector->add(resource->client(), 0, resource->version());
|
||||
send_connector(resource->handle, connectorResource->handle);
|
||||
connector->send(connectorResource->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_create_lease_request(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *requestResource = wl_resource_create(resource->client(), &wp_drm_lease_request_v1_interface,
|
||||
resource->version(), id);
|
||||
if (!requestResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
m_leaseRequests << new DrmLeaseRequestV1Interface(this, requestResource);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_release(Resource *resource)
|
||||
{
|
||||
send_released(resource->handle);
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_bind_resource(Resource *resource)
|
||||
{
|
||||
if (isGlobalRemoved()) {
|
||||
return;
|
||||
}
|
||||
if (!m_hasDrmMaster) {
|
||||
m_pendingFds << resource->handle;
|
||||
return;
|
||||
}
|
||||
FileDescriptor fd = m_gpu->createNonMasterFd();
|
||||
send_drm_fd(resource->handle, fd.get());
|
||||
for (const auto &[output, connector] : m_connectors) {
|
||||
if (!connector->withdrawn()) {
|
||||
auto connectorResource = connector->add(resource->client(), 0, s_version);
|
||||
send_connector(resource->handle, connectorResource->handle);
|
||||
connector->send(connectorResource->handle);
|
||||
}
|
||||
}
|
||||
send_done(resource->handle);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
m_pendingFds.removeOne(resource->handle);
|
||||
}
|
||||
|
||||
void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_destroy_global()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
DrmLeaseConnectorV1Interface::DrmLeaseConnectorV1Interface(DrmLeaseDeviceV1Interface *leaseDevice, DrmOutput *output)
|
||||
: wp_drm_lease_connector_v1()
|
||||
, m_device(leaseDevice)
|
||||
, m_output(output)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t DrmLeaseConnectorV1Interface::id() const
|
||||
{
|
||||
return m_output->connector()->id();
|
||||
}
|
||||
|
||||
DrmLeaseDeviceV1Interface *DrmLeaseConnectorV1Interface::device() const
|
||||
{
|
||||
return m_device;
|
||||
}
|
||||
|
||||
DrmOutput *DrmLeaseConnectorV1Interface::output() const
|
||||
{
|
||||
return m_output;
|
||||
}
|
||||
|
||||
bool DrmLeaseConnectorV1Interface::withdrawn() const
|
||||
{
|
||||
return m_withdrawn;
|
||||
}
|
||||
|
||||
void DrmLeaseConnectorV1Interface::send(wl_resource *resource)
|
||||
{
|
||||
m_withdrawn = false;
|
||||
send_connector_id(resource, m_output->connector()->id());
|
||||
send_name(resource, m_output->name());
|
||||
send_description(resource, m_output->description());
|
||||
send_done(resource);
|
||||
}
|
||||
|
||||
void DrmLeaseConnectorV1Interface::withdraw()
|
||||
{
|
||||
if (!m_withdrawn) {
|
||||
m_withdrawn = true;
|
||||
for (const auto &resource : resourceMap()) {
|
||||
send_withdrawn(resource->handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseConnectorV1Interface::wp_drm_lease_connector_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
DrmLeaseRequestV1Interface::DrmLeaseRequestV1Interface(DrmLeaseDeviceV1Interface *device, wl_resource *resource)
|
||||
: wp_drm_lease_request_v1(resource)
|
||||
, m_device(device)
|
||||
{
|
||||
}
|
||||
|
||||
DrmLeaseRequestV1Interface::~DrmLeaseRequestV1Interface()
|
||||
{
|
||||
m_device->removeLeaseRequest(this);
|
||||
}
|
||||
|
||||
QList<DrmLeaseConnectorV1Interface *> DrmLeaseRequestV1Interface::connectors() const
|
||||
{
|
||||
return m_connectors;
|
||||
}
|
||||
|
||||
void DrmLeaseRequestV1Interface::invalidate()
|
||||
{
|
||||
m_connectors.clear();
|
||||
m_invalid = true;
|
||||
}
|
||||
|
||||
void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_request_connector(Resource *resource, struct ::wl_resource *connector_handle)
|
||||
{
|
||||
if (auto connector = resource_cast<DrmLeaseConnectorV1Interface *>(connector_handle)) {
|
||||
if (connector->device() != m_device) {
|
||||
wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE, "Requested connector from invalid lease device");
|
||||
} else if (connector->withdrawn()) {
|
||||
qCWarning(KWIN_CORE) << "DrmLease: withdrawn connector requested";
|
||||
invalidate();
|
||||
} else if (m_connectors.contains(connector)) {
|
||||
wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Requested connector twice");
|
||||
} else if (!m_invalid) {
|
||||
m_connectors << connector;
|
||||
}
|
||||
} else {
|
||||
qCWarning(KWIN_CORE, "DrmLease: Invalid connector requested");
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_submit(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *leaseResource = wl_resource_create(resource->client(), &wp_drm_lease_v1_interface, s_version, id);
|
||||
if (!leaseResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
DrmLeaseV1Interface *lease = new DrmLeaseV1Interface(m_device, m_connectors, leaseResource);
|
||||
m_device->addLease(lease);
|
||||
if (!m_device->hasDrmMaster()) {
|
||||
qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request without drm master";
|
||||
lease->deny();
|
||||
} else if (m_invalid) {
|
||||
qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request with a withdrawn connector";
|
||||
lease->deny();
|
||||
} else if (m_connectors.isEmpty()) {
|
||||
wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "Requested lease without connectors");
|
||||
} else {
|
||||
QList<DrmOutput *> outputs;
|
||||
for (const auto &connector : m_connectors) {
|
||||
outputs.push_back(connector->output());
|
||||
}
|
||||
auto drmLease = m_device->gpu()->leaseOutputs(outputs);
|
||||
if (drmLease) {
|
||||
lease->grant(std::move(drmLease));
|
||||
} else {
|
||||
lease->deny();
|
||||
}
|
||||
}
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
DrmLeaseV1Interface::DrmLeaseV1Interface(DrmLeaseDeviceV1Interface *device, const QList<DrmLeaseConnectorV1Interface *> &connectors, wl_resource *resource)
|
||||
: wp_drm_lease_v1(resource)
|
||||
, m_device(device)
|
||||
, m_connectors(connectors)
|
||||
{
|
||||
}
|
||||
|
||||
DrmLeaseV1Interface::~DrmLeaseV1Interface()
|
||||
{
|
||||
if (m_lease) {
|
||||
revoke();
|
||||
} else {
|
||||
deny();
|
||||
}
|
||||
m_device->removeLease(this);
|
||||
}
|
||||
|
||||
void DrmLeaseV1Interface::grant(std::unique_ptr<DrmLease> &&lease)
|
||||
{
|
||||
FileDescriptor tmp = std::move(lease->fd());
|
||||
send_lease_fd(tmp.get());
|
||||
m_lease = std::move(lease);
|
||||
connect(m_lease.get(), &DrmLease::revokeRequested, this, &DrmLeaseV1Interface::revoke);
|
||||
for (const auto &connector : std::as_const(m_connectors)) {
|
||||
connector->withdraw();
|
||||
}
|
||||
m_device->done();
|
||||
}
|
||||
|
||||
void DrmLeaseV1Interface::deny()
|
||||
{
|
||||
Q_ASSERT(!m_lease);
|
||||
if (!m_finished) {
|
||||
m_finished = true;
|
||||
send_finished();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseV1Interface::revoke()
|
||||
{
|
||||
Q_ASSERT(m_lease);
|
||||
if (!m_finished) {
|
||||
m_finished = true;
|
||||
send_finished();
|
||||
}
|
||||
m_lease.reset();
|
||||
// check if we should offer connectors again
|
||||
if (m_device->hasDrmMaster()) {
|
||||
for (const auto &connector : std::as_const(m_connectors)) {
|
||||
m_device->offerConnector(connector);
|
||||
}
|
||||
m_device->done();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmLeaseV1Interface::wp_drm_lease_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void DrmLeaseV1Interface::wp_drm_lease_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
QList<DrmLeaseConnectorV1Interface *> DrmLeaseV1Interface::connectors() const
|
||||
{
|
||||
return m_connectors;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_drmlease_v1.cpp"
|
||||
#include "moc_drmlease_v1_p.cpp"
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021-2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmBackend;
|
||||
class DrmGpu;
|
||||
class DrmLeaseDeviceV1Interface;
|
||||
class DrmLeaseConnectorV1Interface;
|
||||
class Display;
|
||||
|
||||
class DrmLeaseManagerV1 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DrmLeaseManagerV1(DrmBackend *backend, Display *display, QObject *parent = nullptr);
|
||||
~DrmLeaseManagerV1();
|
||||
|
||||
private:
|
||||
void addGpu(DrmGpu *gpu);
|
||||
void removeGpu(DrmGpu *gpu);
|
||||
void handleOutputsQueried();
|
||||
|
||||
DrmBackend *const m_backend;
|
||||
Display *const m_display;
|
||||
QHash<DrmGpu *, DrmLeaseDeviceV1Interface *> m_leaseDevices;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2021-2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <qwayland-server-drm-lease-v1.h>
|
||||
|
||||
#include "backends/drm/drm_backend.h"
|
||||
#include "backends/drm/drm_connector.h"
|
||||
#include "backends/drm/drm_gpu.h"
|
||||
#include "backends/drm/drm_output.h"
|
||||
#include "utils/filedescriptor.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QQueue>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class DrmLeaseConnectorV1Interface;
|
||||
class DrmLeaseRequestV1Interface;
|
||||
class DrmLeaseV1Interface;
|
||||
|
||||
class DrmLeaseDeviceV1Interface : public QObject, public QtWaylandServer::wp_drm_lease_device_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DrmLeaseDeviceV1Interface(Display *display, DrmGpu *gpu);
|
||||
~DrmLeaseDeviceV1Interface();
|
||||
|
||||
void addOutput(DrmAbstractOutput *output);
|
||||
void removeOutput(DrmAbstractOutput *output);
|
||||
void setDrmMaster(bool hasDrmMaster);
|
||||
void done();
|
||||
void remove();
|
||||
void addLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest);
|
||||
void removeLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest);
|
||||
void addLease(DrmLeaseV1Interface *lease);
|
||||
void removeLease(DrmLeaseV1Interface *lease);
|
||||
void offerConnector(DrmLeaseConnectorV1Interface *connector);
|
||||
|
||||
bool hasDrmMaster() const;
|
||||
DrmGpu *gpu() const;
|
||||
|
||||
private:
|
||||
void wp_drm_lease_device_v1_create_lease_request(Resource *resource, uint32_t id) override;
|
||||
void wp_drm_lease_device_v1_release(Resource *resource) override;
|
||||
void wp_drm_lease_device_v1_bind_resource(Resource *resource) override;
|
||||
void wp_drm_lease_device_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_drm_lease_device_v1_destroy_global() override;
|
||||
|
||||
DrmGpu *const m_gpu;
|
||||
bool m_hasDrmMaster = true;
|
||||
std::map<DrmAbstractOutput *, std::unique_ptr<DrmLeaseConnectorV1Interface>> m_connectors;
|
||||
QQueue<wl_resource *> m_pendingFds;
|
||||
QList<DrmLeaseRequestV1Interface *> m_leaseRequests;
|
||||
QList<DrmLeaseV1Interface *> m_leases;
|
||||
};
|
||||
|
||||
class DrmLeaseConnectorV1Interface : public QObject, public QtWaylandServer::wp_drm_lease_connector_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DrmLeaseConnectorV1Interface(DrmLeaseDeviceV1Interface *leaseDevice, DrmOutput *output);
|
||||
|
||||
uint32_t id() const;
|
||||
void send(wl_resource *resource);
|
||||
void withdraw();
|
||||
|
||||
DrmLeaseDeviceV1Interface *device() const;
|
||||
DrmOutput *output() const;
|
||||
bool withdrawn() const;
|
||||
|
||||
private:
|
||||
void wp_drm_lease_connector_v1_destroy(Resource *resource) override;
|
||||
|
||||
QPointer<DrmLeaseDeviceV1Interface> m_device;
|
||||
bool m_withdrawn = false;
|
||||
DrmOutput *const m_output;
|
||||
};
|
||||
|
||||
class DrmLeaseRequestV1Interface : public QtWaylandServer::wp_drm_lease_request_v1
|
||||
{
|
||||
public:
|
||||
DrmLeaseRequestV1Interface(DrmLeaseDeviceV1Interface *device, wl_resource *resource);
|
||||
~DrmLeaseRequestV1Interface();
|
||||
|
||||
QList<DrmLeaseConnectorV1Interface *> connectors() const;
|
||||
void invalidate();
|
||||
|
||||
protected:
|
||||
void wp_drm_lease_request_v1_request_connector(Resource *resource, struct ::wl_resource *connector) override;
|
||||
void wp_drm_lease_request_v1_submit(Resource *resource, uint32_t id) override;
|
||||
void wp_drm_lease_request_v1_destroy_resource(Resource *resource) override;
|
||||
|
||||
DrmLeaseDeviceV1Interface *const m_device;
|
||||
QList<DrmLeaseConnectorV1Interface *> m_connectors;
|
||||
bool m_invalid = false;
|
||||
};
|
||||
|
||||
class DrmLeaseV1Interface : public QObject, private QtWaylandServer::wp_drm_lease_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DrmLeaseV1Interface(DrmLeaseDeviceV1Interface *device, const QList<DrmLeaseConnectorV1Interface *> &connectors, wl_resource *resource);
|
||||
~DrmLeaseV1Interface();
|
||||
|
||||
void grant(std::unique_ptr<DrmLease> &&lease);
|
||||
void deny();
|
||||
void revoke();
|
||||
|
||||
QList<DrmLeaseConnectorV1Interface *> connectors() const;
|
||||
|
||||
private:
|
||||
DrmLeaseDeviceV1Interface *m_device;
|
||||
QList<DrmLeaseConnectorV1Interface *> m_connectors;
|
||||
std::unique_ptr<DrmLease> m_lease;
|
||||
bool m_finished = false;
|
||||
|
||||
void wp_drm_lease_v1_destroy(Resource *resource) override;
|
||||
void wp_drm_lease_v1_destroy_resource(Resource *resource) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "externalbrightness_v1.h"
|
||||
#include "core/output.h"
|
||||
#include "display.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_version = 2;
|
||||
|
||||
ExternalBrightnessV1::ExternalBrightnessV1(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWaylandServer::kde_external_brightness_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void ExternalBrightnessV1::kde_external_brightness_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ExternalBrightnessV1::kde_external_brightness_v1_create_brightness_control(Resource *resource, uint32_t id)
|
||||
{
|
||||
new ExternalBrightnessDeviceV1(this, resource->client(), id, resource->version());
|
||||
}
|
||||
|
||||
void ExternalBrightnessV1::addDevice(ExternalBrightnessDeviceV1 *device)
|
||||
{
|
||||
m_devices.push_back(device);
|
||||
Q_EMIT devicesChanged();
|
||||
}
|
||||
|
||||
void ExternalBrightnessV1::removeDevice(ExternalBrightnessDeviceV1 *device)
|
||||
{
|
||||
m_devices.removeOne(device);
|
||||
Q_EMIT devicesChanged();
|
||||
}
|
||||
|
||||
QList<BrightnessDevice *> ExternalBrightnessV1::devices() const
|
||||
{
|
||||
return m_devices;
|
||||
}
|
||||
|
||||
ExternalBrightnessDeviceV1::ExternalBrightnessDeviceV1(ExternalBrightnessV1 *global, wl_client *client, uint32_t id, uint32_t version)
|
||||
: QtWaylandServer::kde_external_brightness_device_v1(client, id, version)
|
||||
, m_global(global)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalBrightnessDeviceV1::~ExternalBrightnessDeviceV1()
|
||||
{
|
||||
if (m_global) {
|
||||
m_global->removeDevice(this);
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::setBrightness(double brightness)
|
||||
{
|
||||
m_observedBrightness.reset();
|
||||
|
||||
const uint32_t minBrightness = m_internal ? 1 : 0; // some laptop screens turn off at brightness 0
|
||||
const uint32_t val = std::round(std::lerp(minBrightness, m_maxBrightness, std::clamp(brightness, 0.0, 1.0)));
|
||||
send_requested_brightness(val);
|
||||
}
|
||||
|
||||
std::optional<double> ExternalBrightnessDeviceV1::observedBrightness() const
|
||||
{
|
||||
std::optional<double> fractional = std::nullopt;
|
||||
if (m_observedBrightness.has_value()) {
|
||||
const uint32_t minBrightness = m_internal ? 1 : 0; // some laptop screens turn off at brightness 0
|
||||
fractional = std::clamp((*m_observedBrightness - minBrightness) / static_cast<double>(m_maxBrightness - minBrightness), 0.0, 1.0);
|
||||
}
|
||||
return fractional;
|
||||
}
|
||||
|
||||
bool ExternalBrightnessDeviceV1::isInternal() const
|
||||
{
|
||||
return m_internal;
|
||||
}
|
||||
|
||||
QByteArray ExternalBrightnessDeviceV1::edidBeginning() const
|
||||
{
|
||||
return m_edidBeginning;
|
||||
}
|
||||
|
||||
int ExternalBrightnessDeviceV1::brightnessSteps() const
|
||||
{
|
||||
return m_maxBrightness - (m_internal ? 1 : 0);
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_set_internal(Resource *resource, uint32_t internal)
|
||||
{
|
||||
m_internal = internal == 1;
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_set_edid(Resource *resource, const QString &edid)
|
||||
{
|
||||
m_edidBeginning = QByteArray::fromBase64(edid.toUtf8());
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_set_max_brightness(Resource *resource, uint32_t value)
|
||||
{
|
||||
m_maxBrightness = value;
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_set_observed_brightness(Resource *resource, uint32_t value)
|
||||
{
|
||||
m_observedBrightness = value;
|
||||
}
|
||||
|
||||
void ExternalBrightnessDeviceV1::kde_external_brightness_device_v1_commit(Resource *resource)
|
||||
{
|
||||
if (!m_done) {
|
||||
m_done = true;
|
||||
if (m_global) {
|
||||
m_global->addDevice(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/brightnessdevice.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <qwayland-server-kde-external-brightness-v1.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class ExternalBrightnessDeviceV1;
|
||||
class Output;
|
||||
|
||||
class ExternalBrightnessV1 : public QObject, private QtWaylandServer::kde_external_brightness_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ExternalBrightnessV1(Display *display, QObject *parent);
|
||||
|
||||
QList<BrightnessDevice *> devices() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void devicesChanged();
|
||||
|
||||
private:
|
||||
void kde_external_brightness_v1_destroy(Resource *resource) override;
|
||||
void kde_external_brightness_v1_create_brightness_control(Resource *resource, uint32_t id) override;
|
||||
|
||||
void addDevice(ExternalBrightnessDeviceV1 *device);
|
||||
void removeDevice(ExternalBrightnessDeviceV1 *device);
|
||||
|
||||
friend class ExternalBrightnessDeviceV1;
|
||||
QList<BrightnessDevice *> m_devices;
|
||||
};
|
||||
|
||||
class ExternalBrightnessDeviceV1 : private QtWaylandServer::kde_external_brightness_device_v1, public BrightnessDevice
|
||||
{
|
||||
public:
|
||||
explicit ExternalBrightnessDeviceV1(ExternalBrightnessV1 *global, wl_client *client, uint32_t id, uint32_t version);
|
||||
~ExternalBrightnessDeviceV1() override;
|
||||
|
||||
void setBrightness(double brightness) override;
|
||||
|
||||
std::optional<double> observedBrightness() const override;
|
||||
bool isInternal() const override;
|
||||
QByteArray edidBeginning() const override;
|
||||
int brightnessSteps() const override;
|
||||
|
||||
private:
|
||||
void kde_external_brightness_device_v1_destroy_resource(Resource *resource) override;
|
||||
void kde_external_brightness_device_v1_destroy(Resource *resource) override;
|
||||
void kde_external_brightness_device_v1_set_internal(Resource *resource, uint32_t internal) override;
|
||||
void kde_external_brightness_device_v1_set_edid(Resource *resource, const QString &string) override;
|
||||
void kde_external_brightness_device_v1_set_max_brightness(Resource *resource, uint32_t value) override;
|
||||
void kde_external_brightness_device_v1_set_observed_brightness(Resource *resource, uint32_t value) override;
|
||||
void kde_external_brightness_device_v1_commit(Resource *resource) override;
|
||||
|
||||
QPointer<ExternalBrightnessV1> m_global;
|
||||
QByteArray m_edidBeginning;
|
||||
std::optional<uint32_t> m_observedBrightness;
|
||||
uint32_t m_maxBrightness = 1;
|
||||
bool m_internal = false;
|
||||
bool m_done = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "filtered_display.h"
|
||||
#include "display.h"
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class FilteredDisplayPrivate
|
||||
{
|
||||
public:
|
||||
FilteredDisplayPrivate(FilteredDisplay *_q);
|
||||
FilteredDisplay *q;
|
||||
static bool globalFilterCallback(const wl_client *client, const wl_global *global, void *data)
|
||||
{
|
||||
auto t = static_cast<FilteredDisplayPrivate *>(data);
|
||||
auto clientConnection = t->q->getConnection(const_cast<wl_client *>(client));
|
||||
auto interface = wl_global_get_interface(global);
|
||||
auto name = QByteArray::fromRawData(interface->name, strlen(interface->name));
|
||||
return t->q->allowInterface(clientConnection, name);
|
||||
};
|
||||
};
|
||||
|
||||
FilteredDisplayPrivate::FilteredDisplayPrivate(FilteredDisplay *_q)
|
||||
: q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
FilteredDisplay::FilteredDisplay(QObject *parent)
|
||||
: Display(parent)
|
||||
, d(new FilteredDisplayPrivate(this))
|
||||
{
|
||||
connect(this, &Display::runningChanged, [this](bool running) {
|
||||
if (!running) {
|
||||
return;
|
||||
}
|
||||
wl_display_set_global_filter(*this, FilteredDisplayPrivate::globalFilterCallback, d.get());
|
||||
});
|
||||
}
|
||||
|
||||
FilteredDisplay::~FilteredDisplay()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_filtered_display.cpp"
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "display.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class FilteredDisplayPrivate;
|
||||
|
||||
/**
|
||||
* Server Implementation that allows one to restrict which globals are available to which clients
|
||||
*
|
||||
* Users of this class must implement the virtual @method allowInterface method.
|
||||
*/
|
||||
class KWIN_EXPORT FilteredDisplay : public Display
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FilteredDisplay(QObject *parent);
|
||||
~FilteredDisplay() override;
|
||||
|
||||
/**
|
||||
* Return whether the @arg client can see the interface with the given @arg interfaceName
|
||||
*
|
||||
* When false will not see these globals for a given interface in the registry,
|
||||
* and any manual attempts to bind will fail
|
||||
*
|
||||
* @return true if the client should be able to access the global with the following interfaceName
|
||||
*/
|
||||
virtual bool allowInterface(ClientConnection *client, const QByteArray &interfaceName) = 0;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FilteredDisplayPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Joaquim Monteiro <joaquim.monteiro@protonmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "fixes.h"
|
||||
|
||||
#include "config-kwin.h"
|
||||
#if HAVE_WL_FIXES
|
||||
|
||||
#include "display.h"
|
||||
#include "qwayland-server-wayland.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static constexpr int s_version = 1;
|
||||
|
||||
class FixesInterfacePrivate : public QtWaylandServer::wl_fixes
|
||||
{
|
||||
public:
|
||||
FixesInterfacePrivate(Display *display);
|
||||
|
||||
protected:
|
||||
void fixes_destroy(Resource *resource) override;
|
||||
void fixes_destroy_registry(Resource *resource, struct ::wl_resource *registry) override;
|
||||
};
|
||||
|
||||
FixesInterfacePrivate::FixesInterfacePrivate(Display *display)
|
||||
: QtWaylandServer::wl_fixes(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void FixesInterfacePrivate::fixes_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void FixesInterfacePrivate::fixes_destroy_registry(Resource *resource, struct ::wl_resource *registry)
|
||||
{
|
||||
wl_resource_destroy(registry);
|
||||
}
|
||||
|
||||
FixesInterface::FixesInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d{std::make_unique<FixesInterfacePrivate>(display)}
|
||||
{
|
||||
}
|
||||
|
||||
FixesInterface::~FixesInterface()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#endif // HAVE_WL_FIXES
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2024 Joaquim Monteiro <joaquim.monteiro@protonmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config-kwin.h"
|
||||
#if HAVE_WL_FIXES
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class FixesInterfacePrivate;
|
||||
|
||||
class KWIN_EXPORT FixesInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FixesInterface(Display *display, QObject *parent = nullptr);
|
||||
~FixesInterface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FixesInterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#endif // HAVE_WL_FIXES
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "fractionalscale_v1.h"
|
||||
|
||||
#include "display.h"
|
||||
#include "fractionalscale_v1_p.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
static const int s_version = 1;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class FractionalScaleManagerV1InterfacePrivate : public QtWaylandServer::wp_fractional_scale_manager_v1
|
||||
{
|
||||
protected:
|
||||
void wp_fractional_scale_manager_v1_destroy(Resource *resource) override;
|
||||
void wp_fractional_scale_manager_v1_get_fractional_scale(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
};
|
||||
|
||||
void FractionalScaleManagerV1InterfacePrivate::wp_fractional_scale_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void FractionalScaleManagerV1InterfacePrivate::wp_fractional_scale_manager_v1_get_fractional_scale(Resource *resource, uint32_t id, struct ::wl_resource *surface_resource)
|
||||
{
|
||||
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
|
||||
|
||||
FractionalScaleV1Interface *scaleIface = FractionalScaleV1Interface::get(surface);
|
||||
if (scaleIface) {
|
||||
wl_resource_post_error(resource->handle, error_fractional_scale_exists, "the specified surface already has a fractional scale");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *surfaceScalerResource = wl_resource_create(resource->client(), &wp_fractional_scale_v1_interface, resource->version(), id);
|
||||
|
||||
new FractionalScaleV1Interface(surface, surfaceScalerResource);
|
||||
}
|
||||
|
||||
FractionalScaleV1Interface::FractionalScaleV1Interface(SurfaceInterface *surface, wl_resource *resource)
|
||||
: QtWaylandServer::wp_fractional_scale_v1(resource)
|
||||
, surface(surface)
|
||||
{
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
|
||||
surfacePrivate->fractionalScaleExtension = this;
|
||||
if (surfacePrivate->preferredBufferScale.has_value()) {
|
||||
setPreferredScale(surfacePrivate->preferredBufferScale.value());
|
||||
}
|
||||
}
|
||||
|
||||
FractionalScaleV1Interface::~FractionalScaleV1Interface()
|
||||
{
|
||||
if (surface) {
|
||||
SurfaceInterfacePrivate *surfacePrivate = SurfaceInterfacePrivate::get(surface);
|
||||
surfacePrivate->fractionalScaleExtension = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FractionalScaleV1Interface *FractionalScaleV1Interface::get(SurfaceInterface *surface)
|
||||
{
|
||||
return SurfaceInterfacePrivate::get(surface)->fractionalScaleExtension;
|
||||
}
|
||||
|
||||
void FractionalScaleV1Interface::setPreferredScale(qreal scale)
|
||||
{
|
||||
send_preferred_scale(std::round(scale * 120));
|
||||
}
|
||||
|
||||
void FractionalScaleV1Interface::wp_fractional_scale_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void FractionalScaleV1Interface::wp_fractional_scale_v1_destroy_resource(Resource *)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
FractionalScaleManagerV1Interface::FractionalScaleManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new FractionalScaleManagerV1InterfacePrivate)
|
||||
{
|
||||
d->init(*display, s_version);
|
||||
}
|
||||
|
||||
FractionalScaleManagerV1Interface::~FractionalScaleManagerV1Interface()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_fractionalscale_v1.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class FractionalScaleManagerV1InterfacePrivate;
|
||||
|
||||
class KWIN_EXPORT FractionalScaleManagerV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FractionalScaleManagerV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~FractionalScaleManagerV1Interface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<FractionalScaleManagerV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "qwayland-server-fractional-scale-v1.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class SurfaceInterface;
|
||||
|
||||
class FractionalScaleV1Interface : protected QtWaylandServer::wp_fractional_scale_v1
|
||||
{
|
||||
public:
|
||||
FractionalScaleV1Interface(SurfaceInterface *surface, wl_resource *resource);
|
||||
~FractionalScaleV1Interface() override;
|
||||
|
||||
static FractionalScaleV1Interface *get(SurfaceInterface *surface);
|
||||
|
||||
void setPreferredScale(qreal scale);
|
||||
QPointer<SurfaceInterface> surface;
|
||||
|
||||
protected:
|
||||
void wp_fractional_scale_v1_destroy(Resource *resource) override;
|
||||
void wp_fractional_scale_v1_destroy_resource(Resource *resource) override;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "frog_colormanagement_v1.h"
|
||||
#include "display.h"
|
||||
#include "surface.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_version = 1;
|
||||
|
||||
FrogColorManagementV1::FrogColorManagementV1(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, QtWaylandServer::frog_color_management_factory_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
FrogColorManagementV1::~FrogColorManagementV1()
|
||||
{
|
||||
}
|
||||
|
||||
void FrogColorManagementV1::frog_color_management_factory_v1_get_color_managed_surface(Resource *resource, wl_resource *surface, uint32_t callback)
|
||||
{
|
||||
SurfaceInterface *surf = SurfaceInterface::get(surface);
|
||||
SurfaceInterfacePrivate::get(surf)->frogColorManagement = new FrogColorManagementSurfaceV1(surf, resource->client(), callback);
|
||||
}
|
||||
|
||||
void FrogColorManagementV1::frog_color_management_factory_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
FrogColorManagementSurfaceV1::FrogColorManagementSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id)
|
||||
: QtWaylandServer::frog_color_managed_surface(client, id, s_version)
|
||||
, m_surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
FrogColorManagementSurfaceV1::~FrogColorManagementSurfaceV1()
|
||||
{
|
||||
if (m_surface) {
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->colorDescription = ColorDescription::sRGB;
|
||||
priv->pending->colorDescriptionIsSet = true;
|
||||
priv->frogColorManagement = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static QtWaylandServer::frog_color_managed_surface::transfer_function kwinToFrogTransferFunction(TransferFunction tf)
|
||||
{
|
||||
switch (tf.type) {
|
||||
case TransferFunction::sRGB:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_srgb;
|
||||
case TransferFunction::gamma22:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_gamma_22;
|
||||
case TransferFunction::PerceptualQuantizer:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_st2084_pq;
|
||||
case TransferFunction::linear:
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_scrgb_linear;
|
||||
}
|
||||
return QtWaylandServer::frog_color_managed_surface::transfer_function_undefined;
|
||||
}
|
||||
|
||||
uint16_t encodePrimary(float primary)
|
||||
{
|
||||
return uint16_t(std::clamp<float>(std::round(primary / 0.00002), 0, 0xC350));
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::setPreferredColorDescription(const ColorDescription &colorDescription)
|
||||
{
|
||||
const auto color = colorDescription.masteringColorimetry().value_or(colorDescription.containerColorimetry());
|
||||
const xyY red = color.red().toxyY();
|
||||
const xyY green = color.green().toxyY();
|
||||
const xyY blue = color.blue().toxyY();
|
||||
const xyY white = color.white().toxyY();
|
||||
send_preferred_metadata(kwinToFrogTransferFunction(colorDescription.transferFunction()),
|
||||
encodePrimary(red.x), encodePrimary(red.y),
|
||||
encodePrimary(green.x), encodePrimary(green.y),
|
||||
encodePrimary(blue.x), encodePrimary(blue.y),
|
||||
encodePrimary(white.x), encodePrimary(white.y),
|
||||
std::round(colorDescription.maxHdrLuminance().value_or(0)),
|
||||
std::round(colorDescription.minLuminance() / 0.0001),
|
||||
std::round(colorDescription.maxAverageLuminance().value_or(0)));
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::frog_color_managed_surface_set_known_transfer_function(Resource *resource, uint32_t transfer_function)
|
||||
{
|
||||
switch (transfer_function) {
|
||||
case transfer_function_undefined:
|
||||
case transfer_function_srgb:
|
||||
case transfer_function_gamma_22:
|
||||
m_transferFunction = TransferFunction(TransferFunction::gamma22);
|
||||
break;
|
||||
case transfer_function_st2084_pq:
|
||||
m_transferFunction = TransferFunction(TransferFunction::PerceptualQuantizer);
|
||||
break;
|
||||
case transfer_function_scrgb_linear:
|
||||
m_transferFunction = TransferFunction(TransferFunction::linear, 0.0, 80.0);
|
||||
break;
|
||||
}
|
||||
updateColorDescription();
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::frog_color_managed_surface_set_known_container_color_volume(Resource *resource, uint32_t primaries)
|
||||
{
|
||||
switch (primaries) {
|
||||
case primaries_undefined:
|
||||
case primaries_rec709:
|
||||
m_containerColorimetry = NamedColorimetry::BT709;
|
||||
break;
|
||||
case primaries_rec2020:
|
||||
m_containerColorimetry = NamedColorimetry::BT2020;
|
||||
break;
|
||||
}
|
||||
updateColorDescription();
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::frog_color_managed_surface_set_render_intent(Resource *resource, uint32_t render_intent)
|
||||
{
|
||||
// there's only perceptual right now, so this can be ignored
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::frog_color_managed_surface_set_hdr_metadata(Resource *resource,
|
||||
uint32_t mastering_display_primary_red_x, uint32_t mastering_display_primary_red_y,
|
||||
uint32_t mastering_display_primary_green_x, uint32_t mastering_display_primary_green_y,
|
||||
uint32_t mastering_display_primary_blue_x, uint32_t mastering_display_primary_blue_y,
|
||||
uint32_t mastering_white_point_x, uint32_t mastering_white_point_y,
|
||||
uint32_t max_display_mastering_luminance, uint32_t min_display_mastering_luminance,
|
||||
uint32_t max_cll, uint32_t max_fall)
|
||||
{
|
||||
// some applications provide truly nonsensical luminance values, like 10 million nits.
|
||||
// this is just a basic sanity check to not make use of that and instead assume HGIG values
|
||||
// which are fine for most HDR games and videos
|
||||
const bool hasSensibleLuminances = max_display_mastering_luminance <= 10'000 && max_cll <= 10'000 && max_fall <= 10'000;
|
||||
if (hasSensibleLuminances) {
|
||||
// max_display_mastering_luminance and max_cll more or less have the same meaning in practice
|
||||
// it seems that max_cll, if set, is more accurate, so prefer it
|
||||
if (max_cll != 0) {
|
||||
m_maxPeakBrightness = max_cll;
|
||||
} else if (max_display_mastering_luminance != 0) {
|
||||
m_maxPeakBrightness = max_display_mastering_luminance;
|
||||
}
|
||||
if (max_fall != 0 && (!m_maxPeakBrightness || max_fall <= *m_maxPeakBrightness)) {
|
||||
m_maxAverageLuminance = max_fall;
|
||||
}
|
||||
const double minLuminance = min_display_mastering_luminance / 10'000.0;
|
||||
if ((!m_maxPeakBrightness && !m_maxAverageLuminance)
|
||||
|| (m_maxPeakBrightness && minLuminance < *m_maxPeakBrightness)
|
||||
|| (m_maxAverageLuminance && minLuminance < *m_maxAverageLuminance)) {
|
||||
m_minMasteringLuminance = minLuminance;
|
||||
}
|
||||
} else {
|
||||
m_minMasteringLuminance = m_transferFunction.minLuminance;
|
||||
m_maxAverageLuminance = 600;
|
||||
m_maxPeakBrightness = 1'000;
|
||||
}
|
||||
if (mastering_display_primary_red_x > 0 && mastering_display_primary_red_y > 0 && mastering_display_primary_green_x > 0 && mastering_display_primary_green_y > 0 && mastering_display_primary_blue_x > 0 && mastering_display_primary_blue_y > 0 && mastering_white_point_x > 0 && mastering_white_point_y > 0) {
|
||||
m_masteringColorimetry = Colorimetry{
|
||||
xy{mastering_display_primary_red_x / 10'000.0, mastering_display_primary_red_y / 10'000.0},
|
||||
xy{mastering_display_primary_green_x / 10'000.0, mastering_display_primary_green_y / 10'000.0},
|
||||
xy{mastering_display_primary_blue_x / 10'000.0, mastering_display_primary_blue_y / 10'000.0},
|
||||
xy{mastering_white_point_x / 10'000.0, mastering_white_point_y / 10'000.0},
|
||||
};
|
||||
}
|
||||
updateColorDescription();
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::frog_color_managed_surface_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::frog_color_managed_surface_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void FrogColorManagementSurfaceV1::updateColorDescription()
|
||||
{
|
||||
if (m_surface) {
|
||||
SurfaceInterfacePrivate *priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
double referenceLuminance = TransferFunction::defaultReferenceLuminanceFor(m_transferFunction.type);
|
||||
if (m_transferFunction.type == TransferFunction::linear) {
|
||||
// we know we're dealing with Windows scRGB here, which works
|
||||
// quite badly when the more correct reference of 80 nits is used
|
||||
referenceLuminance = 203;
|
||||
}
|
||||
priv->pending->colorDescription = ColorDescription{
|
||||
m_containerColorimetry,
|
||||
m_transferFunction,
|
||||
referenceLuminance,
|
||||
m_minMasteringLuminance.value_or(m_transferFunction.minLuminance),
|
||||
m_maxAverageLuminance,
|
||||
m_maxPeakBrightness,
|
||||
m_masteringColorimetry,
|
||||
Colorimetry::fromName(NamedColorimetry::BT709),
|
||||
};
|
||||
priv->pending->colorDescriptionIsSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_frog_colormanagement_v1.cpp"
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/colorspace.h"
|
||||
#include "wayland/qwayland-server-frog-color-management-v1.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
|
||||
class FrogColorManagementV1 : public QObject, private QtWaylandServer::frog_color_management_factory_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FrogColorManagementV1(Display *display, QObject *parent);
|
||||
~FrogColorManagementV1() override;
|
||||
|
||||
private:
|
||||
void frog_color_management_factory_v1_get_color_managed_surface(Resource *resource, wl_resource *surface, uint32_t callback) override;
|
||||
void frog_color_management_factory_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
class FrogColorManagementSurfaceV1 : public QObject, private QtWaylandServer::frog_color_managed_surface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FrogColorManagementSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id);
|
||||
~FrogColorManagementSurfaceV1() override;
|
||||
|
||||
void setPreferredColorDescription(const ColorDescription &colorDescription);
|
||||
|
||||
private:
|
||||
void frog_color_managed_surface_set_known_transfer_function(Resource *resource, uint32_t transfer_function) override;
|
||||
void frog_color_managed_surface_set_known_container_color_volume(Resource *resource, uint32_t primaries) override;
|
||||
void frog_color_managed_surface_set_render_intent(Resource *resource, uint32_t render_intent) override;
|
||||
void frog_color_managed_surface_set_hdr_metadata(Resource *resource, uint32_t mastering_display_primary_red_x, uint32_t mastering_display_primary_red_y,
|
||||
uint32_t mastering_display_primary_green_x, uint32_t mastering_display_primary_green_y,
|
||||
uint32_t mastering_display_primary_blue_x, uint32_t mastering_display_primary_blue_y,
|
||||
uint32_t mastering_white_point_x, uint32_t mastering_white_point_y,
|
||||
uint32_t max_display_mastering_luminance, uint32_t min_display_mastering_luminance,
|
||||
uint32_t max_cll, uint32_t max_fall) override;
|
||||
void frog_color_managed_surface_destroy(Resource *resource) override;
|
||||
void frog_color_managed_surface_destroy_resource(Resource *resource) override;
|
||||
void updateColorDescription();
|
||||
|
||||
const QPointer<SurfaceInterface> m_surface;
|
||||
TransferFunction m_transferFunction{TransferFunction::sRGB};
|
||||
NamedColorimetry m_containerColorimetry = NamedColorimetry::BT709;
|
||||
std::optional<Colorimetry> m_masteringColorimetry;
|
||||
std::optional<double> m_minMasteringLuminance;
|
||||
std::optional<double> m_maxAverageLuminance;
|
||||
std::optional<double> m_maxPeakBrightness;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "display.h"
|
||||
#include "idle_p.h"
|
||||
#include "seat.h"
|
||||
|
||||
#include "idledetector.h"
|
||||
#include "input.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
IdleInterfacePrivate::IdleInterfacePrivate(Display *display)
|
||||
: QtWaylandServer::org_kde_kwin_idle(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void IdleInterfacePrivate::org_kde_kwin_idle_get_idle_timeout(Resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout)
|
||||
{
|
||||
SeatInterface *s = SeatInterface::get(seat);
|
||||
Q_ASSERT(s);
|
||||
|
||||
wl_resource *idleTimoutResource = wl_resource_create(resource->client(), &org_kde_kwin_idle_timeout_interface, resource->version(), id);
|
||||
if (!idleTimoutResource) {
|
||||
wl_client_post_no_memory(resource->client());
|
||||
return;
|
||||
}
|
||||
|
||||
new IdleTimeoutInterface(std::chrono::milliseconds(timeout), idleTimoutResource);
|
||||
}
|
||||
|
||||
IdleInterface::IdleInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new IdleInterfacePrivate(display))
|
||||
{
|
||||
}
|
||||
|
||||
IdleInterface::~IdleInterface() = default;
|
||||
|
||||
IdleTimeoutInterface::IdleTimeoutInterface(std::chrono::milliseconds timeout, wl_resource *resource)
|
||||
: QtWaylandServer::org_kde_kwin_idle_timeout(resource)
|
||||
{
|
||||
auto detector = new IdleDetector(timeout, this);
|
||||
connect(detector, &IdleDetector::idle, this, [this]() {
|
||||
send_idle();
|
||||
});
|
||||
connect(detector, &IdleDetector::resumed, this, [this]() {
|
||||
send_resumed();
|
||||
});
|
||||
}
|
||||
|
||||
void IdleTimeoutInterface::org_kde_kwin_idle_timeout_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void IdleTimeoutInterface::org_kde_kwin_idle_timeout_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void IdleTimeoutInterface::org_kde_kwin_idle_timeout_simulate_user_activity(Resource *resource)
|
||||
{
|
||||
input()->simulateUserActivity();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_idle.cpp"
|
||||
#include "moc_idle_p.cpp"
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class IdleInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Global representing the org_kde_kwin_idle interface.
|
||||
*
|
||||
* The IdleInterface allows to register callbacks which are invoked if there has
|
||||
* not been any user activity (no input) for a specified time span on a seat.
|
||||
*
|
||||
* A client can bind an idle timeout for a SeatInterface and through that register
|
||||
* an idle timeout. The complete interaction is handled internally, thus the API
|
||||
* user only needs to create the IdleInterface in order to provide this feature.
|
||||
*
|
||||
* This interface is useful for clients as it allows them to perform power management,
|
||||
* chat applications might want to set to away after no user input for some time, etc.
|
||||
*
|
||||
* Of course this exposes the global input usage to all clients. Normally clients don't
|
||||
* know whether the input devices are used, only if their surfaces have focus. With this
|
||||
* interface it is possible to notice that there are input events. A server should consider
|
||||
* this to decide whether it wants to provide this feature!
|
||||
*/
|
||||
class KWIN_EXPORT IdleInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IdleInterface(Display *display, QObject *parent = nullptr);
|
||||
~IdleInterface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<IdleInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "idle.h"
|
||||
|
||||
#include <qwayland-server-idle.h>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class SeatInterface;
|
||||
class IdleTimeoutInterface;
|
||||
class IdleTimeoutInterface;
|
||||
|
||||
class IdleInterfacePrivate : public QtWaylandServer::org_kde_kwin_idle
|
||||
{
|
||||
public:
|
||||
IdleInterfacePrivate(Display *display);
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_idle_get_idle_timeout(Resource *resource, uint32_t id, wl_resource *seat, uint32_t timeout) override;
|
||||
};
|
||||
|
||||
class IdleTimeoutInterface : public QObject, public QtWaylandServer::org_kde_kwin_idle_timeout
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IdleTimeoutInterface(std::chrono::milliseconds timeout, wl_resource *resource);
|
||||
|
||||
protected:
|
||||
void org_kde_kwin_idle_timeout_destroy_resource(Resource *resource) override;
|
||||
void org_kde_kwin_idle_timeout_release(Resource *resource) override;
|
||||
void org_kde_kwin_idle_timeout_simulate_user_activity(Resource *resource) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "display.h"
|
||||
#include "idleinhibit_v1_p.h"
|
||||
#include "surface_p.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
IdleInhibitManagerV1InterfacePrivate::IdleInhibitManagerV1InterfacePrivate(IdleInhibitManagerV1Interface *_q, Display *display)
|
||||
: QtWaylandServer::zwp_idle_inhibit_manager_v1(*display, s_version)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
void IdleInhibitManagerV1InterfacePrivate::zwp_idle_inhibit_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void IdleInhibitManagerV1InterfacePrivate::zwp_idle_inhibit_manager_v1_create_inhibitor(Resource *resource, uint32_t id, wl_resource *wlsurface)
|
||||
{
|
||||
const auto surface = SurfaceInterface::get(wlsurface);
|
||||
const auto inhibitor = new IdleInhibitorV1Interface(resource->client(), id, resource->version(), surface);
|
||||
SurfaceInterfacePrivate::get(surface)->installIdleInhibitor(inhibitor);
|
||||
}
|
||||
|
||||
IdleInhibitManagerV1Interface::IdleInhibitManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new IdleInhibitManagerV1InterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
IdleInhibitManagerV1Interface::~IdleInhibitManagerV1Interface() = default;
|
||||
|
||||
IdleInhibitorV1Interface::IdleInhibitorV1Interface(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface)
|
||||
: QtWaylandServer::zwp_idle_inhibitor_v1(client, id, version)
|
||||
, m_surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
IdleInhibitorV1Interface::~IdleInhibitorV1Interface()
|
||||
{
|
||||
if (m_surface) {
|
||||
SurfaceInterfacePrivate::get(m_surface)->removeIdleInhibitor(this);
|
||||
}
|
||||
}
|
||||
|
||||
void IdleInhibitorV1Interface::zwp_idle_inhibitor_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void IdleInhibitorV1Interface::zwp_idle_inhibitor_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_idleinhibit_v1.cpp"
|
||||
#include "moc_idleinhibit_v1_p.cpp"
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class IdleInhibitManagerV1InterfacePrivate;
|
||||
|
||||
/**
|
||||
* The IdleInhibitorManagerInterface is used by clients to inhibit idle on a
|
||||
* SurfaceInterface. Whether a SurfaceInterface inhibits idle is exposes through
|
||||
* @link{SurfaceInterface::inhibitsIdle}.
|
||||
*/
|
||||
class KWIN_EXPORT IdleInhibitManagerV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IdleInhibitManagerV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~IdleInhibitManagerV1Interface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<IdleInhibitManagerV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "idleinhibit_v1.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <qwayland-server-idle-inhibit-unstable-v1.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class SurfaceInterface;
|
||||
|
||||
class IdleInhibitManagerV1InterfacePrivate : public QtWaylandServer::zwp_idle_inhibit_manager_v1
|
||||
{
|
||||
public:
|
||||
IdleInhibitManagerV1InterfacePrivate(IdleInhibitManagerV1Interface *_q, Display *display);
|
||||
|
||||
IdleInhibitManagerV1Interface *q;
|
||||
|
||||
protected:
|
||||
void zwp_idle_inhibit_manager_v1_destroy(Resource *resource) override;
|
||||
void zwp_idle_inhibit_manager_v1_create_inhibitor(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
};
|
||||
|
||||
class IdleInhibitorV1Interface : QtWaylandServer::zwp_idle_inhibitor_v1
|
||||
{
|
||||
public:
|
||||
explicit IdleInhibitorV1Interface(wl_client *client, uint32_t id, uint32_t version, SurfaceInterface *surface);
|
||||
~IdleInhibitorV1Interface() override;
|
||||
|
||||
protected:
|
||||
void zwp_idle_inhibitor_v1_destroy_resource(Resource *resource) override;
|
||||
void zwp_idle_inhibitor_v1_destroy(Resource *resource) override;
|
||||
|
||||
const QPointer<SurfaceInterface> m_surface;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "idlenotify_v1.h"
|
||||
#include "display.h"
|
||||
|
||||
#include "idledetector.h"
|
||||
|
||||
#include "qwayland-server-ext-idle-notify-v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
class IdleNotifyV1InterfacePrivate : public QtWaylandServer::ext_idle_notifier_v1
|
||||
{
|
||||
public:
|
||||
IdleNotifyV1InterfacePrivate(Display *display);
|
||||
|
||||
protected:
|
||||
void ext_idle_notifier_v1_destroy(Resource *resource) override;
|
||||
void ext_idle_notifier_v1_get_idle_notification(Resource *resource, uint32_t id, uint32_t timeout, struct ::wl_resource *seat) override;
|
||||
};
|
||||
|
||||
class IdleNotificationV1Interface : public QObject, public QtWaylandServer::ext_idle_notification_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
IdleNotificationV1Interface(wl_client *client, int version, uint32_t id, std::chrono::milliseconds timeout);
|
||||
|
||||
protected:
|
||||
void ext_idle_notification_v1_destroy_resource(Resource *resource) override;
|
||||
void ext_idle_notification_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
IdleNotifyV1InterfacePrivate::IdleNotifyV1InterfacePrivate(Display *display)
|
||||
: QtWaylandServer::ext_idle_notifier_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void IdleNotifyV1InterfacePrivate::ext_idle_notifier_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void IdleNotifyV1InterfacePrivate::ext_idle_notifier_v1_get_idle_notification(Resource *resource, uint32_t id, uint32_t timeout, struct ::wl_resource *seat)
|
||||
{
|
||||
new IdleNotificationV1Interface(resource->client(), resource->version(), id, std::chrono::milliseconds(timeout));
|
||||
}
|
||||
|
||||
IdleNotificationV1Interface::IdleNotificationV1Interface(wl_client *client, int version, uint32_t id, std::chrono::milliseconds timeout)
|
||||
: QtWaylandServer::ext_idle_notification_v1(client, id, version)
|
||||
{
|
||||
auto detector = new IdleDetector(timeout, this);
|
||||
connect(detector, &IdleDetector::idle, this, [this]() {
|
||||
send_idled();
|
||||
});
|
||||
connect(detector, &IdleDetector::resumed, this, [this]() {
|
||||
send_resumed();
|
||||
});
|
||||
}
|
||||
|
||||
void IdleNotificationV1Interface::ext_idle_notification_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void IdleNotificationV1Interface::ext_idle_notification_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
IdleNotifyV1Interface::IdleNotifyV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new IdleNotifyV1InterfacePrivate(display))
|
||||
{
|
||||
}
|
||||
|
||||
IdleNotifyV1Interface::~IdleNotifyV1Interface()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "idlenotify_v1.moc"
|
||||
|
||||
#include "moc_idlenotify_v1.cpp"
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class IdleNotifyV1InterfacePrivate;
|
||||
|
||||
class KWIN_EXPORT IdleNotifyV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IdleNotifyV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~IdleNotifyV1Interface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<IdleNotifyV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "inputmethod_v1.h"
|
||||
#include "display.h"
|
||||
#include "keyboard.h"
|
||||
#include "keyboard_p.h"
|
||||
#include "output.h"
|
||||
#include "seat.h"
|
||||
#include "surface.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/ramfile.h"
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "qwayland-server-input-method-unstable-v1.h"
|
||||
#include "qwayland-server-text-input-unstable-v1.h"
|
||||
#include "qwayland-server-wayland.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static int s_version = 1;
|
||||
|
||||
class InputKeyboardV1InterfacePrivate : public QtWaylandServer::wl_keyboard
|
||||
{
|
||||
public:
|
||||
InputKeyboardV1InterfacePrivate()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
InputMethodGrabV1::InputMethodGrabV1(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new InputKeyboardV1InterfacePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
InputMethodGrabV1::~InputMethodGrabV1()
|
||||
{
|
||||
}
|
||||
|
||||
void InputMethodGrabV1::sendKeymap(const QByteArray &keymap)
|
||||
{
|
||||
RamFile keymapFile("kwin-xkb-input-method-grab-keymap", keymap.constData(), keymap.size() + 1); // include QByteArray null terminator
|
||||
|
||||
const auto resources = d->resourceMap();
|
||||
for (auto r : resources) {
|
||||
d->send_keymap(r->handle, QtWaylandServer::wl_keyboard::keymap_format::keymap_format_xkb_v1, keymapFile.fd(), keymapFile.size());
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodGrabV1::sendKey(quint32 serial, quint32 timestamp, quint32 key, KeyboardKeyState state)
|
||||
{
|
||||
quint32 waylandState;
|
||||
switch (state) {
|
||||
case KeyboardKeyState::Pressed:
|
||||
waylandState = WL_KEYBOARD_KEY_STATE_PRESSED;
|
||||
break;
|
||||
case KeyboardKeyState::Released:
|
||||
waylandState = WL_KEYBOARD_KEY_STATE_RELEASED;
|
||||
break;
|
||||
case KeyboardKeyState::Repeated:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
const auto resources = d->resourceMap();
|
||||
for (auto r : resources) {
|
||||
d->send_key(r->handle, serial, timestamp, key, waylandState);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodGrabV1::sendModifiers(quint32 serial, quint32 depressed, quint32 latched, quint32 locked, quint32 group)
|
||||
{
|
||||
const auto resources = d->resourceMap();
|
||||
for (auto r : resources) {
|
||||
d->send_modifiers(r->handle, serial, depressed, latched, locked, group);
|
||||
}
|
||||
}
|
||||
|
||||
class InputMethodContextV1InterfacePrivate : public QtWaylandServer::zwp_input_method_context_v1
|
||||
{
|
||||
public:
|
||||
InputMethodContextV1InterfacePrivate(InputMethodContextV1Interface *q)
|
||||
: zwp_input_method_context_v1()
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
~InputMethodContextV1InterfacePrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void zwp_input_method_context_v1_commit_string(Resource *, uint32_t serial, const QString &text) override
|
||||
{
|
||||
Q_EMIT q->commitString(serial, text);
|
||||
}
|
||||
void zwp_input_method_context_v1_preedit_string(Resource *, uint32_t serial, const QString &text, const QString &commit) override
|
||||
{
|
||||
Q_EMIT q->preeditString(serial, text, commit);
|
||||
}
|
||||
|
||||
void zwp_input_method_context_v1_preedit_styling(Resource *, uint32_t index, uint32_t length, uint32_t style) override
|
||||
{
|
||||
Q_EMIT q->preeditStyling(index, length, style);
|
||||
}
|
||||
void zwp_input_method_context_v1_preedit_cursor(Resource *, int32_t index) override
|
||||
{
|
||||
Q_EMIT q->preeditCursor(index);
|
||||
}
|
||||
void zwp_input_method_context_v1_delete_surrounding_text(Resource *, int32_t index, uint32_t length) override
|
||||
{
|
||||
Q_EMIT q->deleteSurroundingText(index, length);
|
||||
}
|
||||
void zwp_input_method_context_v1_cursor_position(Resource *, int32_t index, int32_t anchor) override
|
||||
{
|
||||
Q_EMIT q->cursorPosition(index, anchor);
|
||||
}
|
||||
void zwp_input_method_context_v1_modifiers_map(Resource *, wl_array *map) override
|
||||
{
|
||||
const auto mods = QByteArray(static_cast<const char *>(map->data), map->size);
|
||||
|
||||
Q_EMIT q->modifiersMap(mods);
|
||||
}
|
||||
void zwp_input_method_context_v1_keysym(Resource *, uint32_t serial, uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers) override
|
||||
{
|
||||
Q_EMIT q->keysym(serial, time, sym, state == WL_KEYBOARD_KEY_STATE_PRESSED, modifiers);
|
||||
}
|
||||
void zwp_input_method_context_v1_grab_keyboard(Resource *resource, uint32_t id) override
|
||||
{
|
||||
m_keyboardGrab.reset(new InputMethodGrabV1(q));
|
||||
m_keyboardGrab->d->add(resource->client(), id, 1);
|
||||
Q_EMIT q->keyboardGrabRequested(m_keyboardGrab.get());
|
||||
}
|
||||
void zwp_input_method_context_v1_key(Resource *, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) override
|
||||
{
|
||||
Q_EMIT q->key(serial, time, key, state == WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||
}
|
||||
void zwp_input_method_context_v1_modifiers(Resource *,
|
||||
uint32_t serial,
|
||||
uint32_t mods_depressed,
|
||||
uint32_t mods_latched,
|
||||
uint32_t mods_locked,
|
||||
uint32_t group) override
|
||||
{
|
||||
Q_EMIT q->modifiers(serial, mods_depressed, mods_latched, mods_locked, group);
|
||||
}
|
||||
void zwp_input_method_context_v1_language(Resource *, uint32_t serial, const QString &language) override
|
||||
{
|
||||
Q_EMIT q->language(serial, language);
|
||||
}
|
||||
void zwp_input_method_context_v1_text_direction(Resource *, uint32_t serial, uint32_t direction) override
|
||||
{
|
||||
Qt::LayoutDirection qtDirection;
|
||||
switch (direction) {
|
||||
case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_LTR:
|
||||
qtDirection = Qt::LeftToRight;
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_RTL:
|
||||
qtDirection = Qt::RightToLeft;
|
||||
break;
|
||||
case ZWP_TEXT_INPUT_V1_TEXT_DIRECTION_AUTO:
|
||||
qtDirection = Qt::LayoutDirectionAuto;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
Q_EMIT q->textDirection(serial, qtDirection);
|
||||
}
|
||||
|
||||
void zwp_input_method_context_v1_destroy(Resource *resource) override
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
InputMethodContextV1Interface *const q;
|
||||
std::unique_ptr<InputMethodGrabV1> m_keyboardGrab;
|
||||
};
|
||||
|
||||
InputMethodContextV1Interface::InputMethodContextV1Interface(InputMethodV1Interface *parent)
|
||||
: QObject(parent)
|
||||
, d(new InputMethodContextV1InterfacePrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
InputMethodContextV1Interface::~InputMethodContextV1Interface() = default;
|
||||
|
||||
void InputMethodContextV1Interface::sendCommitState(uint32_t serial)
|
||||
{
|
||||
for (auto r : d->resourceMap()) {
|
||||
d->send_commit_state(r->handle, serial);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodContextV1Interface::sendContentType(TextInputContentHints hint, TextInputContentPurpose purpose)
|
||||
{
|
||||
quint32 contentHint = QtWaylandServer::zwp_text_input_v1::content_hint_none;
|
||||
quint32 contentPurpose;
|
||||
|
||||
if (hint.testFlag(TextInputContentHint::AutoCapitalization)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_auto_capitalization;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::AutoCorrection)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_auto_correction;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::AutoCompletion)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_auto_completion;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::LowerCase)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_lowercase;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::UpperCase)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_uppercase;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::TitleCase)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_titlecase;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::HiddenText)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_hidden_text;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::SensitiveData)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_lowercase;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::Latin)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_latin;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::MultiLine)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_multiline;
|
||||
}
|
||||
if (hint.testFlag(TextInputContentHint::None)) {
|
||||
contentHint |= QtWaylandServer::zwp_text_input_v1::content_hint_none;
|
||||
}
|
||||
|
||||
switch (purpose) {
|
||||
case TextInputContentPurpose::Alpha:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_alpha;
|
||||
break;
|
||||
case TextInputContentPurpose::Digits:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_digits;
|
||||
break;
|
||||
case TextInputContentPurpose::Number:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_number;
|
||||
break;
|
||||
case TextInputContentPurpose::Phone:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_phone;
|
||||
break;
|
||||
case TextInputContentPurpose::Url:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_url;
|
||||
break;
|
||||
case TextInputContentPurpose::Email:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_email;
|
||||
break;
|
||||
case TextInputContentPurpose::Name:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_name;
|
||||
break;
|
||||
case TextInputContentPurpose::Password:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_password;
|
||||
break;
|
||||
case TextInputContentPurpose::Date:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_date;
|
||||
break;
|
||||
case TextInputContentPurpose::Time:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_time;
|
||||
break;
|
||||
case TextInputContentPurpose::DateTime:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_datetime;
|
||||
break;
|
||||
case TextInputContentPurpose::Terminal:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_terminal;
|
||||
break;
|
||||
case TextInputContentPurpose::Normal:
|
||||
default:
|
||||
contentPurpose = QtWaylandServer::zwp_text_input_v1::content_purpose_normal;
|
||||
}
|
||||
|
||||
for (auto r : d->resourceMap()) {
|
||||
d->send_content_type(r->handle, contentHint, contentPurpose);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodContextV1Interface::sendInvokeAction(uint32_t button, uint32_t index)
|
||||
{
|
||||
for (auto r : d->resourceMap()) {
|
||||
d->send_invoke_action(r->handle, button, index);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodContextV1Interface::sendPreferredLanguage(const QString &language)
|
||||
{
|
||||
for (auto r : d->resourceMap()) {
|
||||
d->send_preferred_language(r->handle, language);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodContextV1Interface::sendReset()
|
||||
{
|
||||
for (auto r : d->resourceMap()) {
|
||||
d->send_reset(r->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodContextV1Interface::sendSurroundingText(const QString &text, uint32_t cursor, uint32_t anchor)
|
||||
{
|
||||
for (auto r : d->resourceMap()) {
|
||||
d->send_surrounding_text(r->handle, text, cursor, anchor);
|
||||
}
|
||||
}
|
||||
|
||||
InputMethodGrabV1 *InputMethodContextV1Interface::keyboardGrab() const
|
||||
{
|
||||
return d->m_keyboardGrab.get();
|
||||
}
|
||||
|
||||
class InputPanelSurfaceV1InterfacePrivate : public QtWaylandServer::zwp_input_panel_surface_v1
|
||||
{
|
||||
friend class InputPanelSurfaceV1Interface;
|
||||
|
||||
public:
|
||||
InputPanelSurfaceV1InterfacePrivate(SurfaceInterface *surface, quint32 id, InputPanelSurfaceV1Interface *q)
|
||||
: zwp_input_panel_surface_v1()
|
||||
, q(q)
|
||||
, surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
void zwp_input_panel_surface_v1_set_overlay_panel(Resource *) override
|
||||
{
|
||||
Q_EMIT q->overlayPanel();
|
||||
}
|
||||
|
||||
void zwp_input_panel_surface_v1_set_toplevel(Resource *, struct ::wl_resource *output, uint32_t position) override
|
||||
{
|
||||
Q_EMIT q->topLevel(OutputInterface::get(output), InputPanelSurfaceV1Interface::Position(position));
|
||||
}
|
||||
|
||||
void zwp_input_panel_surface_v1_destroy_resource(Resource *) override
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
InputPanelSurfaceV1Interface *const q;
|
||||
QPointer<SurfaceInterface> surface;
|
||||
};
|
||||
|
||||
InputPanelSurfaceV1Interface::InputPanelSurfaceV1Interface(SurfaceInterface *surface, quint32 id, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new InputPanelSurfaceV1InterfacePrivate(surface, id, this))
|
||||
{
|
||||
}
|
||||
|
||||
InputPanelSurfaceV1Interface::~InputPanelSurfaceV1Interface()
|
||||
{
|
||||
Q_EMIT aboutToBeDestroyed();
|
||||
}
|
||||
|
||||
SurfaceRole *InputPanelSurfaceV1Interface::role()
|
||||
{
|
||||
static SurfaceRole role(QByteArrayLiteral("input_panel_surface_v1"));
|
||||
return &role;
|
||||
}
|
||||
|
||||
class InputPanelV1InterfacePrivate : public QtWaylandServer::zwp_input_panel_v1
|
||||
{
|
||||
public:
|
||||
InputPanelV1InterfacePrivate(InputPanelV1Interface *q, Display *d)
|
||||
: zwp_input_panel_v1(*d, s_version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void zwp_input_panel_v1_get_input_panel_surface(Resource *resource, uint32_t id, struct ::wl_resource *surfaceResource) override
|
||||
{
|
||||
auto surface = SurfaceInterface::get(surfaceResource);
|
||||
|
||||
if (const SurfaceRole *role = surface->role()) {
|
||||
if (role != InputPanelSurfaceV1Interface::role()) {
|
||||
wl_resource_post_error(resource->handle, 0, "the surface already has a role assigned %s", role->name().constData());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
surface->setRole(InputPanelSurfaceV1Interface::role());
|
||||
}
|
||||
|
||||
auto interface = new InputPanelSurfaceV1Interface(surface, id, nullptr);
|
||||
interface->d->init(resource->client(), id, resource->version());
|
||||
|
||||
Q_EMIT q->inputPanelSurfaceAdded(interface);
|
||||
}
|
||||
|
||||
InputPanelV1Interface *const q;
|
||||
};
|
||||
|
||||
InputPanelV1Interface::InputPanelV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new InputPanelV1InterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
InputPanelV1Interface::~InputPanelV1Interface() = default;
|
||||
|
||||
SurfaceInterface *InputPanelSurfaceV1Interface::surface() const
|
||||
{
|
||||
return d->surface;
|
||||
}
|
||||
|
||||
class InputMethodV1InterfacePrivate : public QtWaylandServer::zwp_input_method_v1
|
||||
{
|
||||
public:
|
||||
InputMethodV1InterfacePrivate(Display *d, InputMethodV1Interface *q)
|
||||
: zwp_input_method_v1(*d, s_version)
|
||||
, q(q)
|
||||
, m_display(d)
|
||||
{
|
||||
}
|
||||
|
||||
void zwp_input_method_v1_bind_resource(Resource *resource) override
|
||||
{
|
||||
if (!m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto addedResource = m_context->d->add(resource->client(), resource->version());
|
||||
send_activate(resource->handle, addedResource->handle);
|
||||
}
|
||||
|
||||
std::unique_ptr<InputMethodContextV1Interface> m_context;
|
||||
InputMethodV1Interface *const q;
|
||||
Display *const m_display;
|
||||
};
|
||||
|
||||
InputMethodV1Interface::InputMethodV1Interface(Display *d, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new InputMethodV1InterfacePrivate(d, this))
|
||||
{
|
||||
}
|
||||
|
||||
InputMethodV1Interface::~InputMethodV1Interface() = default;
|
||||
|
||||
void InputMethodV1Interface::sendActivate()
|
||||
{
|
||||
if (d->m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_context.reset(new InputMethodContextV1Interface(this));
|
||||
|
||||
for (auto resource : d->resourceMap()) {
|
||||
auto connection = d->m_context->d->add(resource->client(), resource->version());
|
||||
d->send_activate(resource->handle, connection->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodV1Interface::sendDeactivate()
|
||||
{
|
||||
if (!d->m_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto resource : d->resourceMap()) {
|
||||
auto connection = d->m_context->d->resourceMap().value(resource->client());
|
||||
if (connection) {
|
||||
d->send_deactivate(resource->handle, connection->handle);
|
||||
}
|
||||
}
|
||||
d->m_context.reset();
|
||||
}
|
||||
|
||||
InputMethodContextV1Interface *InputMethodV1Interface::context() const
|
||||
{
|
||||
return d->m_context.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_inputmethod_v1.cpp"
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/inputdevice.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
#include "textinput.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class OutputInterface;
|
||||
class SurfaceInterface;
|
||||
class SurfaceRole;
|
||||
class Display;
|
||||
class KeyboardInterface;
|
||||
class InputPanelSurfaceV1Interface;
|
||||
class InputMethodContextV1Interface;
|
||||
|
||||
class InputMethodV1InterfacePrivate;
|
||||
class InputMethodContextV1InterfacePrivate;
|
||||
class InputPanelV1InterfacePrivate;
|
||||
class InputPanelSurfaceV1InterfacePrivate;
|
||||
class InputMethodGrabV1;
|
||||
class InputKeyboardV1InterfacePrivate;
|
||||
|
||||
// This file's classes implment input_method_unstable_v1
|
||||
|
||||
/**
|
||||
* Implements zwp_input_method_v1 and allows to activate and deactivate a context
|
||||
*
|
||||
* When we activate, an @class InputMethodContextV1Interface becomes available
|
||||
*/
|
||||
class KWIN_EXPORT InputMethodV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InputMethodV1Interface(Display *d, QObject *parent);
|
||||
~InputMethodV1Interface() override;
|
||||
|
||||
/**
|
||||
* Activates the input method.
|
||||
*/
|
||||
void sendActivate();
|
||||
|
||||
/**
|
||||
* Deactivates the input method, probably because we're not on some area
|
||||
* where we can write text.
|
||||
*/
|
||||
void sendDeactivate();
|
||||
|
||||
InputMethodContextV1Interface *context() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<InputMethodV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements zwp_input_method_context_v1, allows to describe the client's input state
|
||||
*/
|
||||
class KWIN_EXPORT InputMethodContextV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~InputMethodContextV1Interface() override;
|
||||
|
||||
void sendSurroundingText(const QString &text, quint32 cursor, quint32 anchor);
|
||||
void sendReset();
|
||||
void sendContentType(KWin::TextInputContentHints hint, KWin::TextInputContentPurpose purpose);
|
||||
void sendInvokeAction(quint32 button, quint32 index);
|
||||
void sendCommitState(quint32 serial);
|
||||
void sendPreferredLanguage(const QString &language);
|
||||
|
||||
InputMethodGrabV1 *keyboardGrab() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void commitString(quint32 serial, const QString &text);
|
||||
void preeditString(quint32 serial, const QString &text, const QString &commit);
|
||||
void preeditStyling(quint32 index, quint32 length, quint32 style);
|
||||
void preeditCursor(qint32 index);
|
||||
void deleteSurroundingText(qint32 index, quint32 length);
|
||||
void cursorPosition(qint32 index, qint32 anchor);
|
||||
void keysym(quint32 serial, quint32 time, quint32 sym, bool pressed, quint32 modifiers);
|
||||
void key(quint32 serial, quint32 time, quint32 key, bool pressed);
|
||||
void modifiers(quint32 serial, quint32 mods_depressed, quint32 mods_latched, quint32 mods_locked, quint32 group);
|
||||
void language(quint32 serial, const QString &language);
|
||||
void textDirection(quint32 serial, Qt::LayoutDirection direction);
|
||||
void keyboardGrabRequested(InputMethodGrabV1 *keyboardGrab);
|
||||
void modifiersMap(const QByteArray &map);
|
||||
|
||||
private:
|
||||
friend class InputMethodV1Interface;
|
||||
friend class InputMethodV1InterfacePrivate;
|
||||
InputMethodContextV1Interface(InputMethodV1Interface *parent);
|
||||
std::unique_ptr<InputMethodContextV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements zwp_input_panel_v1, tells us about the InputPanelSurfaceV1Interface that we might get
|
||||
*/
|
||||
class KWIN_EXPORT InputPanelV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
InputPanelV1Interface(Display *display, QObject *parent);
|
||||
~InputPanelV1Interface() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void inputPanelSurfaceAdded(InputPanelSurfaceV1Interface *surface);
|
||||
|
||||
private:
|
||||
std::unique_ptr<InputPanelV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements zwp_input_panel_surface_v1, it corresponds to each element shown so it can be placed.
|
||||
*/
|
||||
class KWIN_EXPORT InputPanelSurfaceV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~InputPanelSurfaceV1Interface() override;
|
||||
|
||||
static SurfaceRole *role();
|
||||
|
||||
enum Position {
|
||||
CenterBottom = 0,
|
||||
};
|
||||
Q_ENUM(Position)
|
||||
|
||||
SurfaceInterface *surface() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void topLevel(OutputInterface *output, Position position);
|
||||
void overlayPanel();
|
||||
void aboutToBeDestroyed();
|
||||
|
||||
private:
|
||||
InputPanelSurfaceV1Interface(SurfaceInterface *surface, quint32 id, QObject *parent);
|
||||
friend class InputPanelV1InterfacePrivate;
|
||||
std::unique_ptr<InputPanelSurfaceV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a wl_keyboard tailored for zwp_input_method_v1 use-cases
|
||||
*/
|
||||
class KWIN_EXPORT InputMethodGrabV1 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~InputMethodGrabV1() override;
|
||||
|
||||
void sendKeymap(const QByteArray &content);
|
||||
void sendKey(quint32 serial, quint32 timestamp, quint32 key, KeyboardKeyState state);
|
||||
void sendModifiers(quint32 serial, quint32 depressed, quint32 latched, quint32 locked, quint32 group);
|
||||
|
||||
private:
|
||||
InputMethodGrabV1(QObject *parent);
|
||||
friend class InputPanelV1InterfacePrivate;
|
||||
friend class InputMethodContextV1InterfacePrivate;
|
||||
std::unique_ptr<InputKeyboardV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::InputMethodV1Interface *)
|
||||
Q_DECLARE_METATYPE(KWin::InputMethodGrabV1 *)
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "clientconnection.h"
|
||||
#include "display.h"
|
||||
#include "keyboard_p.h"
|
||||
#include "seat.h"
|
||||
#include "seat_p.h"
|
||||
#include "surface.h"
|
||||
#include "utils/common.h"
|
||||
// Qt
|
||||
#include <QList>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
KeyboardInterfacePrivate::KeyboardInterfacePrivate(SeatInterface *s)
|
||||
: seat(s)
|
||||
{
|
||||
}
|
||||
|
||||
void KeyboardInterfacePrivate::keyboard_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void KeyboardInterfacePrivate::keyboard_bind_resource(Resource *resource)
|
||||
{
|
||||
const ClientConnection *focusedClient = focusedSurface ? focusedSurface->client() : nullptr;
|
||||
|
||||
if (resource->version() >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) {
|
||||
send_repeat_info(resource->handle, keyRepeat.charactersPerSecond, keyRepeat.delay);
|
||||
}
|
||||
if (!keymap.isNull()) {
|
||||
sendKeymap(resource);
|
||||
}
|
||||
|
||||
if (focusedClient && focusedClient->client() == resource->client()) {
|
||||
const QByteArray keysData = QByteArray::fromRawData(reinterpret_cast<const char *>(pressedKeys.data()), sizeof(quint32) * pressedKeys.count());
|
||||
const quint32 serial = seat->display()->nextSerial();
|
||||
|
||||
send_enter(resource->handle, serial, focusedSurface->resource(), keysData);
|
||||
send_modifiers(resource->handle, serial, modifiers.depressed, modifiers.latched, modifiers.locked, modifiers.group);
|
||||
}
|
||||
}
|
||||
|
||||
QList<KeyboardInterfacePrivate::Resource *> KeyboardInterfacePrivate::keyboardsForClient(ClientConnection *client) const
|
||||
{
|
||||
return resourceMap().values(client->client());
|
||||
}
|
||||
|
||||
void KeyboardInterfacePrivate::sendLeave(SurfaceInterface *surface, quint32 serial)
|
||||
{
|
||||
const QList<Resource *> keyboards = keyboardsForClient(surface->client());
|
||||
for (Resource *keyboardResource : keyboards) {
|
||||
send_leave(keyboardResource->handle, serial, surface->resource());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterfacePrivate::sendEnter(SurfaceInterface *surface, quint32 serial)
|
||||
{
|
||||
QByteArray data = QByteArray::fromRawData(reinterpret_cast<const char *>(pressedKeys.constData()), sizeof(quint32) * pressedKeys.size());
|
||||
|
||||
const QList<Resource *> keyboards = keyboardsForClient(surface->client());
|
||||
for (Resource *keyboardResource : keyboards) {
|
||||
send_enter(keyboardResource->handle, serial, surface->resource(), data);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterfacePrivate::sendKeymap(Resource *resource)
|
||||
{
|
||||
// From version 7 on, keymaps must be mapped privately, so that
|
||||
// we can seal the fd and reuse it between clients.
|
||||
if (resource->version() >= 7 && sharedKeymapFile.effectiveFlags().testFlag(RamFile::Flag::SealWrite)) {
|
||||
send_keymap(resource->handle, keymap_format::keymap_format_xkb_v1, sharedKeymapFile.fd(), sharedKeymapFile.size());
|
||||
// otherwise give each client its own unsealed copy.
|
||||
} else {
|
||||
RamFile keymapFile("kwin-xkb-keymap", keymap.constData(), keymap.size() + 1); // Include QByteArray null-terminator.
|
||||
send_keymap(resource->handle, keymap_format::keymap_format_xkb_v1, keymapFile.fd(), keymapFile.size());
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterface::setKeymap(const QByteArray &content)
|
||||
{
|
||||
if (content.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->keymap = content;
|
||||
// +1 to include QByteArray null terminator.
|
||||
d->sharedKeymapFile = RamFile("kwin-xkb-keymap-shared", content.constData(), content.size() + 1, RamFile::Flag::SealWrite);
|
||||
|
||||
const auto keyboardResources = d->resourceMap();
|
||||
for (KeyboardInterfacePrivate::Resource *resource : keyboardResources) {
|
||||
d->sendKeymap(resource);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterfacePrivate::sendModifiers(SurfaceInterface *surface, quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial)
|
||||
{
|
||||
const QList<Resource *> keyboards = keyboardsForClient(surface->client());
|
||||
for (Resource *keyboardResource : keyboards) {
|
||||
send_modifiers(keyboardResource->handle, serial, depressed, latched, locked, group);
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyboardInterfacePrivate::updateKey(quint32 key, KeyboardKeyState state)
|
||||
{
|
||||
switch (state) {
|
||||
case KeyboardKeyState::Pressed:
|
||||
if (pressedKeys.contains(key)) {
|
||||
return false;
|
||||
}
|
||||
pressedKeys.append(key);
|
||||
return true;
|
||||
case KeyboardKeyState::Released:
|
||||
return pressedKeys.removeOne(key);
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardInterface::KeyboardInterface(SeatInterface *seat)
|
||||
: d(new KeyboardInterfacePrivate(seat))
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardInterface::~KeyboardInterface() = default;
|
||||
|
||||
void KeyboardInterfacePrivate::sendModifiers(SurfaceInterface *surface)
|
||||
{
|
||||
sendModifiers(surface, modifiers.depressed, modifiers.latched, modifiers.locked, modifiers.group, modifiers.serial);
|
||||
}
|
||||
|
||||
void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface, const QList<quint32> &keys, quint32 serial)
|
||||
{
|
||||
if (d->focusedSurface == surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->focusedSurface) {
|
||||
d->sendLeave(d->focusedSurface, serial);
|
||||
disconnect(d->destroyConnection);
|
||||
}
|
||||
|
||||
d->focusedSurface = surface;
|
||||
d->pressedKeys = keys;
|
||||
|
||||
if (!d->focusedSurface) {
|
||||
return;
|
||||
}
|
||||
d->destroyConnection = connect(d->focusedSurface, &SurfaceInterface::aboutToBeDestroyed, this, [this] {
|
||||
d->sendLeave(d->focusedSurface, d->seat->display()->nextSerial());
|
||||
d->focusedSurface = nullptr;
|
||||
});
|
||||
|
||||
d->sendEnter(d->focusedSurface, serial);
|
||||
d->sendModifiers(d->focusedSurface);
|
||||
}
|
||||
|
||||
void KeyboardInterface::setModifierFocusSurface(SurfaceInterface *surface)
|
||||
{
|
||||
if (d->modifierFocusSurface == surface) {
|
||||
return;
|
||||
}
|
||||
d->modifierFocusSurface = surface;
|
||||
if (d->modifierFocusSurface && d->focusedSurface != d->modifierFocusSurface) {
|
||||
d->modifiers.serial = d->seat->display()->nextSerial();
|
||||
d->sendModifiers(d->modifierFocusSurface);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendKey(quint32 key, KeyboardKeyState state, ClientConnection *client)
|
||||
{
|
||||
quint32 waylandState;
|
||||
switch (state) {
|
||||
case KeyboardKeyState::Pressed:
|
||||
waylandState = WL_KEYBOARD_KEY_STATE_PRESSED;
|
||||
break;
|
||||
case KeyboardKeyState::Released:
|
||||
waylandState = WL_KEYBOARD_KEY_STATE_RELEASED;
|
||||
break;
|
||||
case KeyboardKeyState::Repeated:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
const QList<KeyboardInterfacePrivate::Resource *> keyboards = d->keyboardsForClient(client);
|
||||
const quint32 serial = d->seat->display()->nextSerial();
|
||||
for (KeyboardInterfacePrivate::Resource *keyboardResource : keyboards) {
|
||||
d->send_key(keyboardResource->handle, serial, d->seat->timestamp().count(), key, waylandState);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendKey(quint32 key, KeyboardKeyState state)
|
||||
{
|
||||
if (!d->focusedSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->updateKey(key, state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendKey(key, state, d->focusedSurface->client());
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group)
|
||||
{
|
||||
bool changed = false;
|
||||
if (d->modifiers.depressed != depressed) {
|
||||
d->modifiers.depressed = depressed;
|
||||
changed = true;
|
||||
}
|
||||
if (d->modifiers.latched != latched) {
|
||||
d->modifiers.latched = latched;
|
||||
changed = true;
|
||||
}
|
||||
if (d->modifiers.locked != locked) {
|
||||
d->modifiers.locked = locked;
|
||||
changed = true;
|
||||
}
|
||||
if (d->modifiers.group != group) {
|
||||
d->modifiers.group = group;
|
||||
changed = true;
|
||||
}
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->focusedSurface) {
|
||||
d->modifiers.serial = d->seat->display()->nextSerial();
|
||||
d->sendModifiers(d->focusedSurface, depressed, latched, locked, group, d->modifiers.serial);
|
||||
}
|
||||
if (d->modifierFocusSurface && d->focusedSurface != d->modifierFocusSurface) {
|
||||
d->modifiers.serial = d->seat->display()->nextSerial();
|
||||
d->sendModifiers(d->modifierFocusSurface, depressed, latched, locked, group, d->modifiers.serial);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, ClientConnection *client)
|
||||
{
|
||||
const QList<KeyboardInterfacePrivate::Resource *> keyboards = d->keyboardsForClient(client);
|
||||
const quint32 serial = d->seat->display()->nextSerial();
|
||||
for (KeyboardInterfacePrivate::Resource *keyboardResource : keyboards) {
|
||||
d->send_modifiers(keyboardResource->handle, serial, depressed, latched, locked, group);
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterface::setRepeatInfo(qint32 charactersPerSecond, qint32 delay)
|
||||
{
|
||||
d->keyRepeat.charactersPerSecond = std::max(charactersPerSecond, 0);
|
||||
d->keyRepeat.delay = std::max(delay, 0);
|
||||
const auto keyboards = d->resourceMap();
|
||||
for (KeyboardInterfacePrivate::Resource *keyboardResource : keyboards) {
|
||||
if (keyboardResource->version() >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) {
|
||||
d->send_repeat_info(keyboardResource->handle, d->keyRepeat.charactersPerSecond, d->keyRepeat.delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceInterface *KeyboardInterface::focusedSurface() const
|
||||
{
|
||||
return d->focusedSurface;
|
||||
}
|
||||
|
||||
qint32 KeyboardInterface::keyRepeatDelay() const
|
||||
{
|
||||
return d->keyRepeat.delay;
|
||||
}
|
||||
|
||||
qint32 KeyboardInterface::keyRepeatRate() const
|
||||
{
|
||||
return d->keyRepeat.charactersPerSecond;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_keyboard.cpp"
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/inputdevice.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ClientConnection;
|
||||
class SeatInterface;
|
||||
class SurfaceInterface;
|
||||
class KeyboardInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Resource for the wl_keyboard interface.
|
||||
*/
|
||||
class KWIN_EXPORT KeyboardInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~KeyboardInterface() override;
|
||||
|
||||
/**
|
||||
* @returns the focused SurfaceInterface on this keyboard resource, if any.
|
||||
*/
|
||||
SurfaceInterface *focusedSurface() const;
|
||||
|
||||
/**
|
||||
* @returns The key repeat in character per second
|
||||
*/
|
||||
qint32 keyRepeatRate() const;
|
||||
/**
|
||||
* @returns The delay on key press before starting repeating keys
|
||||
*/
|
||||
qint32 keyRepeatDelay() const;
|
||||
void setKeymap(const QByteArray &content);
|
||||
|
||||
/**
|
||||
* Sets the key repeat information to be forwarded to all bound keyboards.
|
||||
*
|
||||
* To disable key repeat set a @p charactersPerSecond of @c 0.
|
||||
*
|
||||
* Requires wl_seat version 4.
|
||||
*
|
||||
* @param charactersPerSecond The characters per second rate, value of @c 0 disables key repeating
|
||||
* @param delay The delay on key press before starting repeating keys
|
||||
*/
|
||||
void setRepeatInfo(qint32 charactersPerSecond, qint32 delay);
|
||||
|
||||
void sendKey(quint32 key, KeyboardKeyState state);
|
||||
void sendKey(quint32 key, KeyboardKeyState state, ClientConnection *client);
|
||||
void sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group);
|
||||
void sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, ClientConnection *client);
|
||||
|
||||
private:
|
||||
void setFocusedSurface(SurfaceInterface *surface, const QList<quint32> &keys, quint32 serial);
|
||||
void setModifierFocusSurface(SurfaceInterface *surface);
|
||||
friend class SeatInterface;
|
||||
friend class SeatInterfacePrivate;
|
||||
friend class KeyboardInterfacePrivate;
|
||||
explicit KeyboardInterface(SeatInterface *seat);
|
||||
|
||||
std::unique_ptr<KeyboardInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::KeyboardInterface *)
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "utils/ramfile.h"
|
||||
|
||||
#include <qwayland-server-wayland.h>
|
||||
|
||||
#include <QHash>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ClientConnection;
|
||||
|
||||
class KeyboardInterfacePrivate : public QtWaylandServer::wl_keyboard
|
||||
{
|
||||
public:
|
||||
KeyboardInterfacePrivate(SeatInterface *s);
|
||||
|
||||
void sendKeymap(Resource *resource);
|
||||
void sendModifiers(SurfaceInterface *surface);
|
||||
void sendModifiers(SurfaceInterface *surface, quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial);
|
||||
|
||||
QList<Resource *> keyboardsForClient(ClientConnection *client) const;
|
||||
void sendLeave(SurfaceInterface *surface, quint32 serial);
|
||||
void sendEnter(SurfaceInterface *surface, quint32 serial);
|
||||
|
||||
static KeyboardInterfacePrivate *get(KeyboardInterface *keyboard)
|
||||
{
|
||||
return keyboard->d.get();
|
||||
}
|
||||
|
||||
SeatInterface *seat;
|
||||
SurfaceInterface *focusedSurface = nullptr;
|
||||
QList<quint32> pressedKeys;
|
||||
QMetaObject::Connection destroyConnection;
|
||||
QPointer<SurfaceInterface> modifierFocusSurface;
|
||||
QByteArray keymap;
|
||||
RamFile sharedKeymapFile;
|
||||
|
||||
struct
|
||||
{
|
||||
qint32 charactersPerSecond = 0;
|
||||
qint32 delay = 0;
|
||||
} keyRepeat;
|
||||
|
||||
struct Modifiers
|
||||
{
|
||||
quint32 depressed = 0;
|
||||
quint32 latched = 0;
|
||||
quint32 locked = 0;
|
||||
quint32 group = 0;
|
||||
quint32 serial = 0;
|
||||
};
|
||||
Modifiers modifiers;
|
||||
|
||||
bool updateKey(quint32 key, KeyboardKeyState state);
|
||||
|
||||
protected:
|
||||
void keyboard_release(Resource *resource) override;
|
||||
void keyboard_bind_resource(Resource *resource) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "keyboard_shortcuts_inhibit_v1.h"
|
||||
#include "display.h"
|
||||
#include "seat.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include <qwayland-server-keyboard-shortcuts-inhibit-unstable-v1.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
static const int s_version = 1;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class KeyboardShortcutsInhibitorV1InterfacePrivate : public QtWaylandServer::zwp_keyboard_shortcuts_inhibitor_v1
|
||||
{
|
||||
public:
|
||||
KeyboardShortcutsInhibitorV1InterfacePrivate(SurfaceInterface *surface,
|
||||
SeatInterface *seat,
|
||||
KeyboardShortcutsInhibitManagerV1Interface *manager,
|
||||
KeyboardShortcutsInhibitorV1Interface *q,
|
||||
wl_resource *resource);
|
||||
|
||||
KeyboardShortcutsInhibitorV1Interface *q;
|
||||
QPointer<KeyboardShortcutsInhibitManagerV1Interface> m_manager;
|
||||
SurfaceInterface *const m_surface;
|
||||
SeatInterface *const m_seat;
|
||||
bool m_active;
|
||||
|
||||
protected:
|
||||
void zwp_keyboard_shortcuts_inhibitor_v1_destroy_resource(Resource *resource) override;
|
||||
|
||||
void zwp_keyboard_shortcuts_inhibitor_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
class KeyboardShortcutsInhibitManagerV1InterfacePrivate : public QtWaylandServer::zwp_keyboard_shortcuts_inhibit_manager_v1
|
||||
{
|
||||
public:
|
||||
KeyboardShortcutsInhibitManagerV1InterfacePrivate(Display *display, KeyboardShortcutsInhibitManagerV1Interface *q);
|
||||
|
||||
void zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(Resource *resource,
|
||||
uint32_t id,
|
||||
struct ::wl_resource *surface_resource,
|
||||
struct ::wl_resource *seat_resource) override;
|
||||
|
||||
KeyboardShortcutsInhibitorV1Interface *findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const;
|
||||
|
||||
void removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat);
|
||||
|
||||
KeyboardShortcutsInhibitManagerV1Interface *q;
|
||||
Display *const m_display;
|
||||
QHash<QPair<SurfaceInterface *, SeatInterface *>, KeyboardShortcutsInhibitorV1Interface *> m_inhibitors;
|
||||
|
||||
protected:
|
||||
void zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
KeyboardShortcutsInhibitorV1InterfacePrivate::KeyboardShortcutsInhibitorV1InterfacePrivate(SurfaceInterface *surface,
|
||||
SeatInterface *seat,
|
||||
KeyboardShortcutsInhibitManagerV1Interface *manager,
|
||||
KeyboardShortcutsInhibitorV1Interface *q,
|
||||
wl_resource *resource)
|
||||
: zwp_keyboard_shortcuts_inhibitor_v1(resource)
|
||||
, q(q)
|
||||
, m_manager(manager)
|
||||
, m_surface(surface)
|
||||
, m_seat(seat)
|
||||
, m_active(false)
|
||||
{
|
||||
}
|
||||
|
||||
void KeyboardShortcutsInhibitorV1InterfacePrivate::zwp_keyboard_shortcuts_inhibitor_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void KeyboardShortcutsInhibitorV1InterfacePrivate::zwp_keyboard_shortcuts_inhibitor_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
// Ensure manager don't track anymore this inhibitor
|
||||
if (m_manager) {
|
||||
m_manager->removeInhibitor(m_surface, m_seat);
|
||||
}
|
||||
delete q;
|
||||
}
|
||||
|
||||
KeyboardShortcutsInhibitorV1Interface::KeyboardShortcutsInhibitorV1Interface(SurfaceInterface *surface,
|
||||
SeatInterface *seat,
|
||||
KeyboardShortcutsInhibitManagerV1Interface *manager,
|
||||
wl_resource *resource)
|
||||
: QObject(nullptr)
|
||||
, d(new KeyboardShortcutsInhibitorV1InterfacePrivate(surface, seat, manager, this, resource))
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardShortcutsInhibitorV1Interface::~KeyboardShortcutsInhibitorV1Interface() = default;
|
||||
|
||||
void KeyboardShortcutsInhibitorV1Interface::setActive(bool active)
|
||||
{
|
||||
if (d->m_active == active) {
|
||||
return;
|
||||
}
|
||||
d->m_active = active;
|
||||
if (active) {
|
||||
d->send_active();
|
||||
} else {
|
||||
d->send_inactive();
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyboardShortcutsInhibitorV1Interface::isActive() const
|
||||
{
|
||||
return d->m_active;
|
||||
}
|
||||
|
||||
SeatInterface *KeyboardShortcutsInhibitorV1Interface::seat() const
|
||||
{
|
||||
return d->m_seat;
|
||||
}
|
||||
|
||||
SurfaceInterface *KeyboardShortcutsInhibitorV1Interface::surface() const
|
||||
{
|
||||
return d->m_surface;
|
||||
}
|
||||
|
||||
KeyboardShortcutsInhibitManagerV1InterfacePrivate::KeyboardShortcutsInhibitManagerV1InterfacePrivate(Display *display,
|
||||
KeyboardShortcutsInhibitManagerV1Interface *q)
|
||||
: zwp_keyboard_shortcuts_inhibit_manager_v1(*display, s_version)
|
||||
, q(q)
|
||||
, m_display(display)
|
||||
{
|
||||
}
|
||||
|
||||
void KeyboardShortcutsInhibitManagerV1InterfacePrivate::zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(Resource *resource,
|
||||
uint32_t id,
|
||||
wl_resource *surface_resource,
|
||||
wl_resource *seat_resource)
|
||||
{
|
||||
SeatInterface *seat = SeatInterface::get(seat_resource);
|
||||
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
|
||||
if (m_inhibitors.contains({surface, seat})) {
|
||||
wl_resource_post_error(resource->handle, error::error_already_inhibited, "the shortcuts are already inhibited for this surface and seat");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *inhibitorResource = wl_resource_create(resource->client(), &zwp_keyboard_shortcuts_inhibitor_v1_interface, resource->version(), id);
|
||||
auto inhibitor = new KeyboardShortcutsInhibitorV1Interface(surface, seat, q, inhibitorResource);
|
||||
m_inhibitors[{surface, seat}] = inhibitor;
|
||||
Q_EMIT q->inhibitorCreated(inhibitor);
|
||||
inhibitor->setActive(true);
|
||||
}
|
||||
|
||||
KeyboardShortcutsInhibitorV1Interface *KeyboardShortcutsInhibitManagerV1InterfacePrivate::findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const
|
||||
{
|
||||
return m_inhibitors.value({surface, seat}, nullptr);
|
||||
}
|
||||
|
||||
void KeyboardShortcutsInhibitManagerV1InterfacePrivate::zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void KeyboardShortcutsInhibitManagerV1InterfacePrivate::removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat)
|
||||
{
|
||||
m_inhibitors.remove({surface, seat});
|
||||
}
|
||||
|
||||
KeyboardShortcutsInhibitManagerV1Interface::KeyboardShortcutsInhibitManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new KeyboardShortcutsInhibitManagerV1InterfacePrivate(display, this))
|
||||
{
|
||||
}
|
||||
|
||||
KeyboardShortcutsInhibitManagerV1Interface::~KeyboardShortcutsInhibitManagerV1Interface() = default;
|
||||
|
||||
KeyboardShortcutsInhibitorV1Interface *KeyboardShortcutsInhibitManagerV1Interface::findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const
|
||||
{
|
||||
return d->findInhibitor(surface, seat);
|
||||
}
|
||||
|
||||
void KeyboardShortcutsInhibitManagerV1Interface::removeInhibitor(SurfaceInterface *surface, SeatInterface *seat)
|
||||
{
|
||||
d->removeInhibitor(surface, seat);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_keyboard_shortcuts_inhibit_v1.cpp"
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "seat.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
/**
|
||||
* This is an implementation of wayland-protocols/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml
|
||||
*
|
||||
* This class is just the means to get a @class KeyboardShortcutsInhibitorV1Interface, which is
|
||||
* the class that will have all of the information we need.
|
||||
*/
|
||||
|
||||
class KeyboardShortcutsInhibitManagerV1Interface;
|
||||
class KeyboardShortcutsInhibitorV1InterfacePrivate;
|
||||
class KeyboardShortcutsInhibitManagerV1InterfacePrivate;
|
||||
|
||||
class KWIN_EXPORT KeyboardShortcutsInhibitorV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~KeyboardShortcutsInhibitorV1Interface() override;
|
||||
|
||||
SurfaceInterface *surface() const;
|
||||
SeatInterface *seat() const;
|
||||
void setActive(bool active);
|
||||
bool isActive() const;
|
||||
|
||||
private:
|
||||
friend class KeyboardShortcutsInhibitManagerV1InterfacePrivate;
|
||||
explicit KeyboardShortcutsInhibitorV1Interface(SurfaceInterface *surface,
|
||||
SeatInterface *seat,
|
||||
KeyboardShortcutsInhibitManagerV1Interface *manager,
|
||||
wl_resource *resource);
|
||||
std::unique_ptr<KeyboardShortcutsInhibitorV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* The KeyboardShortcutsInhibitManagerV1Interface allows clients to inhibit global shortcuts.
|
||||
*
|
||||
* KeyboardShortcutsInhibitManagerV1Interface correponds to the wayland interface zwp_keyboard_shortcuts_inhibit_manager_v1.
|
||||
*/
|
||||
class KWIN_EXPORT KeyboardShortcutsInhibitManagerV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KeyboardShortcutsInhibitManagerV1Interface(Display *d, QObject *parent = nullptr);
|
||||
~KeyboardShortcutsInhibitManagerV1Interface() override;
|
||||
|
||||
/**
|
||||
* return shortucts inhibitor associated with surface and seat, if no shortcut are associated, return nullptr
|
||||
*/
|
||||
KeyboardShortcutsInhibitorV1Interface *findInhibitor(SurfaceInterface *surface, SeatInterface *seat) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when a keyboard shortcuts inhibitor @a inhibitor is created.
|
||||
*/
|
||||
void inhibitorCreated(KeyboardShortcutsInhibitorV1Interface *inhibitor);
|
||||
|
||||
private:
|
||||
friend class KeyboardShortcutsInhibitorV1InterfacePrivate;
|
||||
void removeInhibitor(SurfaceInterface *const surface, SeatInterface *const seat);
|
||||
std::unique_ptr<KeyboardShortcutsInhibitManagerV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "keystate.h"
|
||||
#include "display.h"
|
||||
|
||||
#include "keyboard_input.h"
|
||||
#include "xkb.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <qwayland-server-keystate.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const quint32 s_version = 5;
|
||||
|
||||
class KeyStateInterfacePrivate : public QtWaylandServer::org_kde_kwin_keystate
|
||||
{
|
||||
public:
|
||||
KeyStateInterfacePrivate(Display *d)
|
||||
: QtWaylandServer::org_kde_kwin_keystate(*d, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void org_kde_kwin_keystate_fetchStates(Resource *resource) override
|
||||
{
|
||||
const LEDs leds = input()->keyboard()->xkb()->leds();
|
||||
|
||||
// Scroll is a virtual modifier and xkbcommon doesn't (yet) support querying those
|
||||
// See https://github.com/xkbcommon/libxkbcommon/pull/512
|
||||
send_stateChanged(resource->handle, key_scrolllock, leds & LED::ScrollLock ? state_locked : state_unlocked);
|
||||
|
||||
auto sendModifier = [this, resource](key k, Xkb::Modifier mod) {
|
||||
if (input()->keyboard()->xkb()->lockedModifiers().testFlag(mod)) {
|
||||
send_stateChanged(resource->handle, k, state_locked);
|
||||
} else if (input()->keyboard()->xkb()->latchedModifiers().testFlag(mod)) {
|
||||
send_stateChanged(resource->handle, k, state_latched);
|
||||
} else if (input()->keyboard()->xkb()->depressedModifiers().testFlag(mod) && resource->version() >= ORG_KDE_KWIN_KEYSTATE_STATE_PRESSED_SINCE_VERSION) {
|
||||
send_stateChanged(resource->handle, k, state_pressed);
|
||||
} else {
|
||||
send_stateChanged(resource->handle, k, state_unlocked);
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr int modifierSinceVersion = ORG_KDE_KWIN_KEYSTATE_KEY_ALT_SINCE_VERSION;
|
||||
|
||||
if (resource->version() >= modifierSinceVersion) {
|
||||
sendModifier(key_alt, Xkb::Mod1);
|
||||
sendModifier(key_shift, Xkb::Shift);
|
||||
sendModifier(key_control, Xkb::Control);
|
||||
sendModifier(key_meta, Xkb::Mod4);
|
||||
sendModifier(key_altgr, Xkb::Mod5);
|
||||
}
|
||||
|
||||
sendModifier(key_capslock, Xkb::Lock);
|
||||
sendModifier(key_numlock, Xkb::Num);
|
||||
}
|
||||
};
|
||||
|
||||
KeyStateInterface::KeyStateInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new KeyStateInterfacePrivate(display))
|
||||
{
|
||||
connect(input()->keyboard(), &KeyboardInputRedirection::ledsChanged, this, [this]() {
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->org_kde_kwin_keystate_fetchStates(resource);
|
||||
}
|
||||
});
|
||||
|
||||
connect(input()->keyboard()->xkb(), &Xkb::modifierStateChanged, this, [this]() {
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->org_kde_kwin_keystate_fetchStates(resource);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KeyStateInterface::~KeyStateInterface() = default;
|
||||
|
||||
}
|
||||
|
||||
#include "moc_keystate.cpp"
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class KeyStateInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Exposes key states to wayland clients
|
||||
*/
|
||||
class KWIN_EXPORT KeyStateInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KeyStateInterface(Display *display, QObject *parent = nullptr);
|
||||
~KeyStateInterface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KeyStateInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,541 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "layershell_v1.h"
|
||||
#include "display.h"
|
||||
#include "output.h"
|
||||
#include "surface.h"
|
||||
#include "utils/common.h"
|
||||
#include "xdgshell_p.h"
|
||||
|
||||
#include <QPointer>
|
||||
#include <QQueue>
|
||||
|
||||
#include "qwayland-server-wlr-layer-shell-unstable-v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const int s_version = 5;
|
||||
|
||||
class LayerShellV1InterfacePrivate : public QtWaylandServer::zwlr_layer_shell_v1
|
||||
{
|
||||
public:
|
||||
LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display);
|
||||
|
||||
LayerShellV1Interface *q;
|
||||
Display *display;
|
||||
|
||||
protected:
|
||||
void zwlr_layer_shell_v1_get_layer_surface(Resource *resource,
|
||||
uint32_t id,
|
||||
struct ::wl_resource *surface_resource,
|
||||
struct ::wl_resource *output_resource,
|
||||
uint32_t layer,
|
||||
const QString &scope) override;
|
||||
void zwlr_layer_shell_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
struct LayerSurfaceV1Commit
|
||||
{
|
||||
std::optional<LayerSurfaceV1Interface::Layer> layer;
|
||||
std::optional<Qt::Edges> anchor;
|
||||
std::optional<QMargins> margins;
|
||||
std::optional<QSize> desiredSize;
|
||||
std::optional<int> exclusiveZone;
|
||||
std::optional<Qt::Edge> exclusiveEdge;
|
||||
std::optional<quint32> acknowledgedConfigure;
|
||||
std::optional<bool> acceptsFocus;
|
||||
};
|
||||
|
||||
struct LayerSurfaceV1State
|
||||
{
|
||||
QQueue<quint32> serials;
|
||||
LayerSurfaceV1Interface::Layer layer = LayerSurfaceV1Interface::BottomLayer;
|
||||
Qt::Edges anchor;
|
||||
QMargins margins;
|
||||
QSize desiredSize = QSize(0, 0);
|
||||
int exclusiveZone = 0;
|
||||
Qt::Edge exclusiveEdge = Qt::Edge();
|
||||
bool acceptsFocus = false;
|
||||
bool configured = false;
|
||||
bool closed = false;
|
||||
bool committed = false;
|
||||
bool firstBufferAttached = false;
|
||||
};
|
||||
|
||||
class LayerSurfaceV1InterfacePrivate : public SurfaceExtension<LayerSurfaceV1Commit>, public QtWaylandServer::zwlr_layer_surface_v1
|
||||
{
|
||||
public:
|
||||
LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface);
|
||||
|
||||
void apply(LayerSurfaceV1Commit *commit) override;
|
||||
|
||||
LayerSurfaceV1Interface *q;
|
||||
LayerShellV1Interface *shell;
|
||||
QPointer<SurfaceInterface> surface;
|
||||
QPointer<OutputInterface> output;
|
||||
QString scope;
|
||||
LayerSurfaceV1State state;
|
||||
|
||||
protected:
|
||||
void zwlr_layer_surface_v1_destroy_resource(Resource *resource) override;
|
||||
void zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height) override;
|
||||
void zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor) override;
|
||||
void zwlr_layer_surface_v1_set_exclusive_edge(Resource *resource, uint32_t edge) override;
|
||||
void zwlr_layer_surface_v1_set_exclusive_zone(Resource *resource, int32_t zone) override;
|
||||
void zwlr_layer_surface_v1_set_margin(Resource *resource, int32_t top, int32_t right, int32_t bottom, int32_t left) override;
|
||||
void zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity) override;
|
||||
void zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup) override;
|
||||
void zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial) override;
|
||||
void zwlr_layer_surface_v1_destroy(Resource *resource) override;
|
||||
void zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer) override;
|
||||
};
|
||||
|
||||
LayerShellV1InterfacePrivate::LayerShellV1InterfacePrivate(LayerShellV1Interface *q, Display *display)
|
||||
: QtWaylandServer::zwlr_layer_shell_v1(*display, s_version)
|
||||
, q(q)
|
||||
, display(display)
|
||||
{
|
||||
}
|
||||
|
||||
void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_get_layer_surface(Resource *resource,
|
||||
uint32_t id,
|
||||
wl_resource *surface_resource,
|
||||
wl_resource *output_resource,
|
||||
uint32_t layer,
|
||||
const QString &scope)
|
||||
{
|
||||
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
|
||||
OutputInterface *output = OutputInterface::get(output_resource);
|
||||
|
||||
if (surface->buffer()) {
|
||||
wl_resource_post_error(resource->handle, error_already_constructed, "the wl_surface already has a buffer attached");
|
||||
return;
|
||||
}
|
||||
|
||||
if (layer > layer_overlay) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_layer, "invalid layer %d", layer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const SurfaceRole *role = surface->role()) {
|
||||
if (role != LayerSurfaceV1Interface::role()) {
|
||||
wl_resource_post_error(resource->handle, error_role,
|
||||
"the wl_surface already has a role assigned %s", role->name().constData());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
surface->setRole(LayerSurfaceV1Interface::role());
|
||||
}
|
||||
|
||||
wl_resource *layerSurfaceResource = wl_resource_create(resource->client(), &zwlr_layer_surface_v1_interface, resource->version(), id);
|
||||
if (!layerSurfaceResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
|
||||
auto layerSurface = new LayerSurfaceV1Interface(q, surface, output, LayerSurfaceV1Interface::Layer(layer), scope, layerSurfaceResource);
|
||||
Q_EMIT q->surfaceCreated(layerSurface);
|
||||
}
|
||||
|
||||
void LayerShellV1InterfacePrivate::zwlr_layer_shell_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
LayerShellV1Interface::LayerShellV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new LayerShellV1InterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
LayerShellV1Interface::~LayerShellV1Interface()
|
||||
{
|
||||
}
|
||||
|
||||
Display *LayerShellV1Interface::display() const
|
||||
{
|
||||
return d->display;
|
||||
}
|
||||
|
||||
LayerSurfaceV1InterfacePrivate::LayerSurfaceV1InterfacePrivate(LayerSurfaceV1Interface *q, SurfaceInterface *surface)
|
||||
: SurfaceExtension(surface)
|
||||
, q(q)
|
||||
, surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
Q_EMIT q->aboutToBeDestroyed();
|
||||
delete q;
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_size(Resource *resource, uint32_t width, uint32_t height)
|
||||
{
|
||||
pending.desiredSize = QSize(width, height);
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_anchor(Resource *resource, uint32_t anchor)
|
||||
{
|
||||
const uint32_t anchorMask = anchor_top | anchor_left | anchor_right | anchor_bottom;
|
||||
if (anchor > anchorMask) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_anchor, "invalid anchor %d", anchor);
|
||||
return;
|
||||
}
|
||||
|
||||
pending.anchor = Qt::Edges();
|
||||
|
||||
if (anchor & anchor_top) {
|
||||
*pending.anchor |= Qt::TopEdge;
|
||||
}
|
||||
|
||||
if (anchor & anchor_right) {
|
||||
*pending.anchor |= Qt::RightEdge;
|
||||
}
|
||||
|
||||
if (anchor & anchor_bottom) {
|
||||
*pending.anchor |= Qt::BottomEdge;
|
||||
}
|
||||
|
||||
if (anchor & anchor_left) {
|
||||
*pending.anchor |= Qt::LeftEdge;
|
||||
}
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_exclusive_edge(Resource *resource, uint32_t edge)
|
||||
{
|
||||
if (!edge) {
|
||||
pending.exclusiveEdge = Qt::Edge();
|
||||
} else if (edge == anchor_top) {
|
||||
pending.exclusiveEdge = Qt::TopEdge;
|
||||
} else if (edge == anchor_right) {
|
||||
pending.exclusiveEdge = Qt::RightEdge;
|
||||
} else if (edge == anchor_bottom) {
|
||||
pending.exclusiveEdge = Qt::BottomEdge;
|
||||
} else if (edge == anchor_left) {
|
||||
pending.exclusiveEdge = Qt::LeftEdge;
|
||||
} else {
|
||||
wl_resource_post_error(resource->handle, error_invalid_exclusive_edge, "Invalid exclusive edge: %d", edge);
|
||||
}
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_exclusive_zone(Resource *, int32_t zone)
|
||||
{
|
||||
pending.exclusiveZone = zone;
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_margin(Resource *, int32_t top, int32_t right, int32_t bottom, int32_t left)
|
||||
{
|
||||
pending.margins = QMargins(left, top, right, bottom);
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_keyboard_interactivity(Resource *resource, uint32_t keyboard_interactivity)
|
||||
{
|
||||
pending.acceptsFocus = keyboard_interactivity;
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_get_popup(Resource *resource, struct ::wl_resource *popup_resource)
|
||||
{
|
||||
XdgPopupInterface *popup = XdgPopupInterface::get(popup_resource);
|
||||
XdgPopupInterfacePrivate *popupPrivate = XdgPopupInterfacePrivate::get(popup);
|
||||
|
||||
if (popup->isConfigured()) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_surface_state, "xdg_popup surface is already configured");
|
||||
return;
|
||||
}
|
||||
|
||||
popupPrivate->parentSurface = surface;
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_ack_configure(Resource *resource, uint32_t serial)
|
||||
{
|
||||
if (!state.serials.contains(serial)) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_surface_state, "invalid configure serial %d", serial);
|
||||
return;
|
||||
}
|
||||
while (!state.serials.isEmpty()) {
|
||||
const quint32 head = state.serials.takeFirst();
|
||||
if (head == serial) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!state.closed) {
|
||||
pending.acknowledgedConfigure = serial;
|
||||
}
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::zwlr_layer_surface_v1_set_layer(Resource *resource, uint32_t layer)
|
||||
{
|
||||
if (Q_UNLIKELY(layer > LayerShellV1InterfacePrivate::layer_overlay)) {
|
||||
wl_resource_post_error(resource->handle, LayerShellV1InterfacePrivate::error_invalid_layer, "invalid layer %d", layer);
|
||||
return;
|
||||
}
|
||||
pending.layer = LayerSurfaceV1Interface::Layer(layer);
|
||||
}
|
||||
|
||||
void LayerSurfaceV1InterfacePrivate::apply(LayerSurfaceV1Commit *commit)
|
||||
{
|
||||
if (state.closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (commit->acknowledgedConfigure.has_value()) {
|
||||
Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value());
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(surface->isMapped() && !state.configured)) {
|
||||
wl_resource_post_error(resource()->handle,
|
||||
error_invalid_surface_state,
|
||||
"a buffer has been attached to a layer surface prior "
|
||||
"to the first layer_surface.configure event");
|
||||
return;
|
||||
}
|
||||
|
||||
if (commit->desiredSize && commit->desiredSize->width() == 0) {
|
||||
const Qt::Edges anchor = commit->anchor.value_or(state.anchor);
|
||||
if (!(anchor & Qt::LeftEdge) || !(anchor & Qt::RightEdge)) {
|
||||
wl_resource_post_error(resource()->handle,
|
||||
error_invalid_size,
|
||||
"the layer surface has a width of 0 but its anchor "
|
||||
"doesn't include the left and the right screen edge");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (commit->desiredSize && commit->desiredSize->height() == 0) {
|
||||
const Qt::Edges anchor = commit->anchor.value_or(state.anchor);
|
||||
if (!(anchor & Qt::TopEdge) || !(anchor & Qt::BottomEdge)) {
|
||||
wl_resource_post_error(resource()->handle,
|
||||
error_invalid_size,
|
||||
"the layer surface has a height of 0 but its anchor "
|
||||
"doesn't include the top and the bottom screen edge");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (commit->exclusiveEdge.has_value() || commit->anchor.has_value()) {
|
||||
const quint32 exclusiveEdge = commit->exclusiveEdge.value_or(state.exclusiveEdge);
|
||||
const quint32 anchor = commit->anchor.value_or(state.anchor);
|
||||
if (exclusiveEdge && !(exclusiveEdge & anchor)) {
|
||||
wl_resource_post_error(resource()->handle, error_invalid_exclusive_edge, "Exclusive edge is not of the anchors");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// detect reset
|
||||
if (!surface->isMapped() && state.firstBufferAttached) {
|
||||
state = LayerSurfaceV1State();
|
||||
pending = LayerSurfaceV1Commit();
|
||||
stashed.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const LayerSurfaceV1State previous = state;
|
||||
|
||||
state.committed = true; // Must set the committed state before emitting any signals.
|
||||
if (surface->isMapped()) {
|
||||
state.firstBufferAttached = true;
|
||||
}
|
||||
|
||||
if (commit->layer.has_value()) {
|
||||
state.layer = commit->layer.value();
|
||||
}
|
||||
if (commit->anchor.has_value()) {
|
||||
state.anchor = commit->anchor.value();
|
||||
}
|
||||
if (commit->margins.has_value()) {
|
||||
state.margins = commit->margins.value();
|
||||
}
|
||||
if (commit->desiredSize.has_value()) {
|
||||
state.desiredSize = commit->desiredSize.value();
|
||||
}
|
||||
if (commit->exclusiveZone.has_value()) {
|
||||
state.exclusiveZone = commit->exclusiveZone.value();
|
||||
}
|
||||
if (commit->exclusiveEdge.has_value()) {
|
||||
state.exclusiveEdge = commit->exclusiveEdge.value();
|
||||
}
|
||||
|
||||
if (commit->acceptsFocus.has_value()) {
|
||||
state.acceptsFocus = commit->acceptsFocus.value();
|
||||
}
|
||||
|
||||
if (previous.acceptsFocus != state.acceptsFocus) {
|
||||
Q_EMIT q->acceptsFocusChanged();
|
||||
}
|
||||
if (previous.layer != state.layer) {
|
||||
Q_EMIT q->layerChanged();
|
||||
}
|
||||
if (previous.anchor != state.anchor) {
|
||||
Q_EMIT q->anchorChanged();
|
||||
}
|
||||
if (previous.desiredSize != state.desiredSize) {
|
||||
Q_EMIT q->desiredSizeChanged();
|
||||
}
|
||||
if (previous.exclusiveZone != state.exclusiveZone) {
|
||||
Q_EMIT q->exclusiveZoneChanged();
|
||||
}
|
||||
if (previous.margins != state.margins) {
|
||||
Q_EMIT q->marginsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
LayerSurfaceV1Interface::LayerSurfaceV1Interface(LayerShellV1Interface *shell,
|
||||
SurfaceInterface *surface,
|
||||
OutputInterface *output,
|
||||
Layer layer,
|
||||
const QString &scope,
|
||||
wl_resource *resource)
|
||||
: d(new LayerSurfaceV1InterfacePrivate(this, surface))
|
||||
{
|
||||
d->state.layer = layer;
|
||||
|
||||
d->shell = shell;
|
||||
d->output = output;
|
||||
d->scope = scope;
|
||||
|
||||
d->init(resource);
|
||||
}
|
||||
|
||||
LayerSurfaceV1Interface::~LayerSurfaceV1Interface()
|
||||
{
|
||||
}
|
||||
|
||||
SurfaceRole *LayerSurfaceV1Interface::role()
|
||||
{
|
||||
static SurfaceRole role(QByteArrayLiteral("layer_surface_v1"));
|
||||
return &role;
|
||||
}
|
||||
|
||||
bool LayerSurfaceV1Interface::isCommitted() const
|
||||
{
|
||||
return d->state.committed;
|
||||
}
|
||||
|
||||
SurfaceInterface *LayerSurfaceV1Interface::surface() const
|
||||
{
|
||||
return d->surface;
|
||||
}
|
||||
|
||||
Qt::Edges LayerSurfaceV1Interface::anchor() const
|
||||
{
|
||||
return d->state.anchor;
|
||||
}
|
||||
|
||||
QSize LayerSurfaceV1Interface::desiredSize() const
|
||||
{
|
||||
return d->state.desiredSize;
|
||||
}
|
||||
|
||||
bool LayerSurfaceV1Interface::acceptsFocus() const
|
||||
{
|
||||
return d->state.acceptsFocus;
|
||||
}
|
||||
|
||||
LayerSurfaceV1Interface::Layer LayerSurfaceV1Interface::layer() const
|
||||
{
|
||||
return d->state.layer;
|
||||
}
|
||||
|
||||
QMargins LayerSurfaceV1Interface::margins() const
|
||||
{
|
||||
return d->state.margins;
|
||||
}
|
||||
|
||||
int LayerSurfaceV1Interface::leftMargin() const
|
||||
{
|
||||
return d->state.margins.left();
|
||||
}
|
||||
|
||||
int LayerSurfaceV1Interface::topMargin() const
|
||||
{
|
||||
return d->state.margins.top();
|
||||
}
|
||||
|
||||
int LayerSurfaceV1Interface::rightMargin() const
|
||||
{
|
||||
return d->state.margins.right();
|
||||
}
|
||||
|
||||
int LayerSurfaceV1Interface::bottomMargin() const
|
||||
{
|
||||
return d->state.margins.bottom();
|
||||
}
|
||||
|
||||
int LayerSurfaceV1Interface::exclusiveZone() const
|
||||
{
|
||||
return d->state.exclusiveZone;
|
||||
}
|
||||
|
||||
Qt::Edge LayerSurfaceV1Interface::exclusiveEdge() const
|
||||
{
|
||||
if (exclusiveZone() <= 0) {
|
||||
return Qt::Edge();
|
||||
}
|
||||
|
||||
if (d->state.exclusiveEdge) {
|
||||
return d->state.exclusiveEdge;
|
||||
}
|
||||
|
||||
if (anchor() == (Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge) || anchor() == Qt::TopEdge) {
|
||||
return Qt::TopEdge;
|
||||
}
|
||||
if (anchor() == (Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge) || anchor() == Qt::RightEdge) {
|
||||
return Qt::RightEdge;
|
||||
}
|
||||
if (anchor() == (Qt::RightEdge | Qt::BottomEdge | Qt::LeftEdge) || anchor() == Qt::BottomEdge) {
|
||||
return Qt::BottomEdge;
|
||||
}
|
||||
if (anchor() == (Qt::BottomEdge | Qt::LeftEdge | Qt::TopEdge) || anchor() == Qt::LeftEdge) {
|
||||
return Qt::LeftEdge;
|
||||
}
|
||||
return Qt::Edge();
|
||||
}
|
||||
|
||||
OutputInterface *LayerSurfaceV1Interface::output() const
|
||||
{
|
||||
return d->output;
|
||||
}
|
||||
|
||||
QString LayerSurfaceV1Interface::scope() const
|
||||
{
|
||||
return d->scope;
|
||||
}
|
||||
|
||||
quint32 LayerSurfaceV1Interface::sendConfigure(const QSize &size)
|
||||
{
|
||||
if (d->state.closed) {
|
||||
qCWarning(KWIN_CORE) << "Cannot configure a closed layer shell surface";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint32_t serial = d->shell->display()->nextSerial();
|
||||
d->state.serials << serial;
|
||||
|
||||
d->send_configure(serial, size.width(), size.height());
|
||||
d->state.configured = true;
|
||||
|
||||
return serial;
|
||||
}
|
||||
|
||||
void LayerSurfaceV1Interface::sendClosed()
|
||||
{
|
||||
if (!d->state.closed) {
|
||||
d->send_closed();
|
||||
d->state.closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_layershell_v1.cpp"
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#include <QMargins>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class LayerShellV1InterfacePrivate;
|
||||
class LayerSurfaceV1Interface;
|
||||
class LayerSurfaceV1InterfacePrivate;
|
||||
class OutputInterface;
|
||||
class SurfaceInterface;
|
||||
class SurfaceRole;
|
||||
|
||||
/**
|
||||
* The LayerShellV1Interface compositor extension allows to create desktop shell surfaces.
|
||||
*
|
||||
* The layer shell compositor extension provides a way to create surfaces that can serve as
|
||||
* building blocks for desktop environment elements such as panels, notifications, etc.
|
||||
*
|
||||
* The LayerShellV1Interface corresponds to the Wayland interface @c zwlr_layer_shell_v1.
|
||||
*/
|
||||
class KWIN_EXPORT LayerShellV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LayerShellV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~LayerShellV1Interface() override;
|
||||
|
||||
/**
|
||||
* Returns the Wayland display for the layer shell compositor extension.
|
||||
*/
|
||||
Display *display() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when a new layer surface @a surface has been created.
|
||||
*/
|
||||
void surfaceCreated(LayerSurfaceV1Interface *surface);
|
||||
|
||||
private:
|
||||
std::unique_ptr<LayerShellV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* The LayerSurfaceV1Interface class represents a desktop shell surface, e.g. panel, etc.
|
||||
*
|
||||
* The LayerSurfaceV1Interface corresponds to the Wayland interface @c zwlr_layer_surface_v1.
|
||||
*/
|
||||
class KWIN_EXPORT LayerSurfaceV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Layer {
|
||||
BackgroundLayer,
|
||||
BottomLayer,
|
||||
TopLayer,
|
||||
OverlayLayer,
|
||||
};
|
||||
|
||||
LayerSurfaceV1Interface(LayerShellV1Interface *shell,
|
||||
SurfaceInterface *surface,
|
||||
OutputInterface *output,
|
||||
Layer layer,
|
||||
const QString &scope,
|
||||
wl_resource *resource);
|
||||
~LayerSurfaceV1Interface() override;
|
||||
|
||||
static SurfaceRole *role();
|
||||
|
||||
/**
|
||||
* Returns @c true if the initial commit has been performed; otherwise returns @c false.
|
||||
*/
|
||||
bool isCommitted() const;
|
||||
|
||||
/**
|
||||
* Returns the underlying Wayland surface for this layer shell surface.
|
||||
*/
|
||||
SurfaceInterface *surface() const;
|
||||
|
||||
/**
|
||||
* Returns the anchor point relative to which the surface will be positioned. If no edges
|
||||
* have been specified, the center of the screen is assumed to be the anchor point.
|
||||
*/
|
||||
Qt::Edges anchor() const;
|
||||
|
||||
/**
|
||||
* Returns the desired size for this layer shell surface, in the surface-local coordinates.
|
||||
*/
|
||||
QSize desiredSize() const;
|
||||
|
||||
/**
|
||||
* Returns the stacking order layer where this layer surface has to be rendered.
|
||||
*/
|
||||
Layer layer() const;
|
||||
|
||||
/**
|
||||
* Returns @c true if the surface accepts keyboard input; otherwise returns @c false.
|
||||
*/
|
||||
bool acceptsFocus() const;
|
||||
|
||||
/**
|
||||
* Returns the margins object that indicates the distance between an anchor edge and
|
||||
* the corresponding surface edge.
|
||||
*/
|
||||
QMargins margins() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the left margin. This is equivalent to calling margins().left().
|
||||
*/
|
||||
int leftMargin() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the right margin. This is equivalent to calling margins().right().
|
||||
*/
|
||||
int rightMargin() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the top margin. This is equivalent to calling margins().top().
|
||||
*/
|
||||
int topMargin() const;
|
||||
|
||||
/**
|
||||
* Returns the value of the bottom margin. This is equivalent to calling margins().bottom().
|
||||
*/
|
||||
int bottomMargin() const;
|
||||
|
||||
/**
|
||||
* Returns the distance from the anchor edge that should not be occluded.
|
||||
*
|
||||
* An exlusive zone of 0 means that the layer surface has to be moved to avoid occluding
|
||||
* surfaces with a positive exclusion zone. If the exclusive zone is -1, the layer surface
|
||||
* indicates that it doesn't want to be moved to accomodate for other surfaces.
|
||||
*/
|
||||
int exclusiveZone() const;
|
||||
|
||||
/**
|
||||
* If the exclusive zone is positive, this function returns the corresponding exclusive
|
||||
* anchor edge, otherwise returns a Qt::Edge() value.
|
||||
*/
|
||||
Qt::Edge exclusiveEdge() const;
|
||||
|
||||
/**
|
||||
* Returns the output where the surface wants to be displayed. This function can return
|
||||
* @c null, in which case the compositor is free to choose the output where the surface
|
||||
* has to be placed.
|
||||
*/
|
||||
OutputInterface *output() const;
|
||||
|
||||
/**
|
||||
* Returns the scope of this layer surface. The scope defines the purpose of the surface.
|
||||
*/
|
||||
QString scope() const;
|
||||
|
||||
/**
|
||||
* Sends a configure event to the client. @a size contains the desired size in surface-local
|
||||
* coordinates. A size of zero means that the client is free to choose its own dimensions.
|
||||
*
|
||||
* @see configureAcknowledged()
|
||||
*/
|
||||
quint32 sendConfigure(const QSize &size);
|
||||
|
||||
/**
|
||||
* Sends a closed event to the client. The client should destroy the surface after receiving
|
||||
* this event. Further changes to the surface will be ignored.
|
||||
*/
|
||||
void sendClosed();
|
||||
|
||||
Q_SIGNALS:
|
||||
void aboutToBeDestroyed();
|
||||
void configureAcknowledged(quint32 serial);
|
||||
void acceptsFocusChanged();
|
||||
void layerChanged();
|
||||
void anchorChanged();
|
||||
void desiredSizeChanged();
|
||||
void exclusiveZoneChanged();
|
||||
void marginsChanged();
|
||||
|
||||
private:
|
||||
std::unique_ptr<LayerSurfaceV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "linux_drm_syncobj_v1.h"
|
||||
#include "core/drmdevice.h"
|
||||
#include "core/syncobjtimeline.h"
|
||||
#include "display.h"
|
||||
#include "linux_drm_syncobj_v1_p.h"
|
||||
#include "surface.h"
|
||||
#include "surface_p.h"
|
||||
#include "transaction.h"
|
||||
#include "utils/resource.h"
|
||||
|
||||
#include <xf86drm.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_version = 1;
|
||||
|
||||
LinuxDrmSyncObjV1Interface::LinuxDrmSyncObjV1Interface(Display *display, QObject *parent, DrmDevice *drmDevice)
|
||||
: QObject(parent)
|
||||
, QtWaylandServer::wp_linux_drm_syncobj_manager_v1(*display, s_version)
|
||||
, m_drmDevice(drmDevice)
|
||||
{
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_get_surface(Resource *resource, uint32_t id, wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *surf = SurfaceInterface::get(surface);
|
||||
SurfaceInterfacePrivate *priv = SurfaceInterfacePrivate::get(surf);
|
||||
if (priv->syncObjV1) {
|
||||
wl_resource_post_error(resource->handle, error_surface_exists, "surface already exists");
|
||||
return;
|
||||
}
|
||||
priv->syncObjV1 = new LinuxDrmSyncObjSurfaceV1(surf, resource->client(), id);
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_import_timeline(Resource *resource, uint32_t id, int32_t rawFd)
|
||||
{
|
||||
FileDescriptor fd(rawFd);
|
||||
if (isGlobalRemoved()) {
|
||||
// to not crash the client, create an inert timeline
|
||||
new LinuxDrmSyncObjTimelineV1(resource->client(), id, nullptr);
|
||||
return;
|
||||
}
|
||||
uint32_t handle = 0;
|
||||
if (drmSyncobjFDToHandle(m_drmDevice->fileDescriptor(), fd.get(), &handle) != 0) {
|
||||
wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Importing timeline failed");
|
||||
return;
|
||||
}
|
||||
new LinuxDrmSyncObjTimelineV1(resource->client(), id, std::make_unique<SyncTimeline>(m_drmDevice->fileDescriptor(), handle));
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjV1Interface::remove()
|
||||
{
|
||||
QtWaylandServer::wp_linux_drm_syncobj_manager_v1::globalRemove();
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjV1Interface::wp_linux_drm_syncobj_manager_v1_destroy_global()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
LinuxDrmSyncObjTimelineV1::LinuxDrmSyncObjTimelineV1(wl_client *client, uint32_t id, std::unique_ptr<SyncTimeline> &&timeline)
|
||||
: QtWaylandServer::wp_linux_drm_syncobj_timeline_v1(client, id, s_version)
|
||||
, m_timeline(std::move(timeline))
|
||||
{
|
||||
}
|
||||
|
||||
LinuxDrmSyncObjTimelineV1::~LinuxDrmSyncObjTimelineV1()
|
||||
{
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjTimelineV1::wp_linux_drm_syncobj_timeline_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjTimelineV1::wp_linux_drm_syncobj_timeline_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
std::shared_ptr<SyncTimeline> LinuxDrmSyncObjTimelineV1::timeline() const
|
||||
{
|
||||
return m_timeline;
|
||||
}
|
||||
|
||||
LinuxDrmSyncObjSurfaceV1::LinuxDrmSyncObjSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id)
|
||||
: QtWaylandServer::wp_linux_drm_syncobj_surface_v1(client, id, s_version)
|
||||
, m_surface(surface)
|
||||
{
|
||||
}
|
||||
|
||||
LinuxDrmSyncObjSurfaceV1::~LinuxDrmSyncObjSurfaceV1()
|
||||
{
|
||||
if (m_surface) {
|
||||
SurfaceInterfacePrivate::get(m_surface)->syncObjV1 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_set_acquire_point(Resource *resource, wl_resource *timeline_resource, uint32_t point_hi, uint32_t point_lo)
|
||||
{
|
||||
if (!m_surface) {
|
||||
wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface got destroyed already");
|
||||
return;
|
||||
}
|
||||
const auto timeline = resource_cast<LinuxDrmSyncObjTimelineV1 *>(timeline_resource);
|
||||
if (!timeline->timeline()) {
|
||||
// in the normal case this should never happen, but if it does,
|
||||
// there's nothing we can do about it without killing the client
|
||||
return;
|
||||
}
|
||||
const uint64_t point = (uint64_t(point_hi) << 32) | point_lo;
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
priv->pending->acquirePoint.timeline = timeline->timeline();
|
||||
priv->pending->acquirePoint.point = point;
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_set_release_point(Resource *resource, wl_resource *timeline_resource, uint32_t point_hi, uint32_t point_lo)
|
||||
{
|
||||
if (!m_surface) {
|
||||
wl_resource_post_error(resource->handle, WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface got destroyed already");
|
||||
return;
|
||||
}
|
||||
const auto timeline = resource_cast<LinuxDrmSyncObjTimelineV1 *>(timeline_resource);
|
||||
if (!timeline->timeline()) {
|
||||
// in the normal case this should never happen, but if it does,
|
||||
// there's nothing we can do about it without killing the client
|
||||
return;
|
||||
}
|
||||
const uint64_t point = (uint64_t(point_hi) << 32) | point_lo;
|
||||
SurfaceInterfacePrivate::get(m_surface)->pending->releasePoint = std::make_unique<SyncReleasePoint>(timeline->timeline(), point);
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void LinuxDrmSyncObjSurfaceV1::wp_linux_drm_syncobj_surface_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
bool LinuxDrmSyncObjSurfaceV1::maybeEmitProtocolErrors()
|
||||
{
|
||||
const auto priv = SurfaceInterfacePrivate::get(m_surface);
|
||||
if ((!priv->pending->bufferIsSet || !priv->pending->buffer) && !priv->pending->acquirePoint.timeline && !priv->pending->releasePoint) {
|
||||
return false;
|
||||
}
|
||||
if (!priv->pending->acquirePoint.timeline) {
|
||||
wl_resource_post_error(resource()->handle, error_no_acquire_point, "explicit sync is used, but no acquire point is set");
|
||||
return true;
|
||||
}
|
||||
if (!priv->pending->releasePoint) {
|
||||
wl_resource_post_error(resource()->handle, error_no_release_point, "explicit sync is used, but no release point is set");
|
||||
return true;
|
||||
}
|
||||
if (priv->pending->acquirePoint.timeline.get() == priv->pending->releasePoint->timeline()
|
||||
&& priv->pending->acquirePoint.point >= priv->pending->releasePoint->timelinePoint()) {
|
||||
wl_resource_post_error(resource()->handle, error_conflicting_points, "acquire and release points are on the same timeline and acquire >= release");
|
||||
return true;
|
||||
}
|
||||
if (!priv->pending->buffer) {
|
||||
wl_resource_post_error(resource()->handle, error_no_buffer, "explicit sync is used, but no buffer is attached");
|
||||
return true;
|
||||
}
|
||||
if (!priv->pending->buffer->dmabufAttributes()) {
|
||||
wl_resource_post_error(resource()->handle, error_unsupported_buffer, "only linux dmabuf buffers are allowed to use explicit sync");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
#include "qwayland-server-linux-drm-syncobj-v1.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
class RenderBackend;
|
||||
class SyncTimeline;
|
||||
class DrmDevice;
|
||||
|
||||
class KWIN_EXPORT LinuxDrmSyncObjV1Interface : public QObject, private QtWaylandServer::wp_linux_drm_syncobj_manager_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LinuxDrmSyncObjV1Interface(Display *display, QObject *parent, DrmDevice *drmDevice);
|
||||
|
||||
void remove();
|
||||
|
||||
private:
|
||||
void wp_linux_drm_syncobj_manager_v1_get_surface(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
void wp_linux_drm_syncobj_manager_v1_import_timeline(Resource *resource, uint32_t id, int32_t fd) override;
|
||||
void wp_linux_drm_syncobj_manager_v1_destroy(Resource *resource) override;
|
||||
void wp_linux_drm_syncobj_manager_v1_destroy_global() override;
|
||||
|
||||
DrmDevice *const m_drmDevice;
|
||||
};
|
||||
|
||||
class LinuxDrmSyncObjSurfaceV1 : private QtWaylandServer::wp_linux_drm_syncobj_surface_v1
|
||||
{
|
||||
public:
|
||||
explicit LinuxDrmSyncObjSurfaceV1(SurfaceInterface *surface, wl_client *client, uint32_t id);
|
||||
~LinuxDrmSyncObjSurfaceV1() override;
|
||||
|
||||
/**
|
||||
* checks for protocol errors that may need to be sent at commit time
|
||||
* @returns if any protocol errors were actually emitted
|
||||
*/
|
||||
bool maybeEmitProtocolErrors();
|
||||
|
||||
private:
|
||||
void wp_linux_drm_syncobj_surface_v1_set_acquire_point(Resource *resource, wl_resource *timeline, uint32_t point_hi, uint32_t point_lo) override;
|
||||
void wp_linux_drm_syncobj_surface_v1_set_release_point(Resource *resource, wl_resource *timeline, uint32_t point_hi, uint32_t point_lo) override;
|
||||
void wp_linux_drm_syncobj_surface_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_linux_drm_syncobj_surface_v1_destroy(Resource *resource) override;
|
||||
|
||||
const QPointer<SurfaceInterface> m_surface;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
#include "linux_drm_syncobj_v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class LinuxDrmSyncObjTimelineV1 : public QtWaylandServer::wp_linux_drm_syncobj_timeline_v1
|
||||
{
|
||||
public:
|
||||
explicit LinuxDrmSyncObjTimelineV1(wl_client *client, uint32_t id, std::unique_ptr<SyncTimeline> &&timeline);
|
||||
~LinuxDrmSyncObjTimelineV1() override;
|
||||
|
||||
/**
|
||||
* May return nullptr if the timeline resource is inert
|
||||
*/
|
||||
std::shared_ptr<SyncTimeline> timeline() const;
|
||||
|
||||
private:
|
||||
void wp_linux_drm_syncobj_timeline_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_linux_drm_syncobj_timeline_v1_destroy(Resource *resource) override;
|
||||
|
||||
const std::shared_ptr<SyncTimeline> m_timeline;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
Based on the libweston implementation,
|
||||
SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "linuxdmabufv1clientbuffer.h"
|
||||
#include "core/drmdevice.h"
|
||||
#include "core/renderbackend.h"
|
||||
#include "linuxdmabufv1clientbuffer_p.h"
|
||||
#include "surface_p.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const int s_version = 4;
|
||||
|
||||
LinuxDmaBufV1ClientBufferIntegrationPrivate::LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display)
|
||||
: QtWaylandServer::zwp_linux_dmabuf_v1(*display, s_version)
|
||||
, q(q)
|
||||
, defaultFeedback(new LinuxDmaBufV1Feedback(this))
|
||||
{
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_bind_resource(Resource *resource)
|
||||
{
|
||||
if (resource->version() < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
|
||||
for (auto it = supportedModifiers.constBegin(); it != supportedModifiers.constEnd(); ++it) {
|
||||
const uint32_t &format = it.key();
|
||||
const auto &modifiers = it.value();
|
||||
for (const uint64_t &modifier : std::as_const(modifiers)) {
|
||||
if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
|
||||
const uint32_t modifier_lo = modifier & 0xffffffff;
|
||||
const uint32_t modifier_hi = modifier >> 32;
|
||||
send_modifier(resource->handle, format, modifier_hi, modifier_lo);
|
||||
} else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) {
|
||||
send_format(resource->handle, format);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id)
|
||||
{
|
||||
LinuxDmaBufV1FeedbackPrivate::get(defaultFeedback.get())->add(resource->client(), id, resource->version());
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surfaceResource)
|
||||
{
|
||||
auto surface = SurfaceInterface::get(surfaceResource);
|
||||
if (!surface) {
|
||||
qCWarning(KWIN_CORE) << "requested surface feedback for nonexistant surface!";
|
||||
return;
|
||||
}
|
||||
auto surfacePrivate = SurfaceInterfacePrivate::get(surface);
|
||||
if (!surfacePrivate->dmabufFeedbackV1) {
|
||||
surfacePrivate->dmabufFeedbackV1.reset(new LinuxDmaBufV1Feedback(this));
|
||||
}
|
||||
LinuxDmaBufV1FeedbackPrivate::get(surfacePrivate->dmabufFeedbackV1.get())->add(resource->client(), id, resource->version());
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegrationPrivate::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id)
|
||||
{
|
||||
wl_resource *paramsResource = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface, resource->version(), params_id);
|
||||
if (!paramsResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
new LinuxDmaBufParamsV1(q, paramsResource);
|
||||
}
|
||||
|
||||
LinuxDmaBufParamsV1::LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource)
|
||||
: QtWaylandServer::zwp_linux_buffer_params_v1(resource)
|
||||
, m_integration(integration)
|
||||
{
|
||||
}
|
||||
|
||||
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_add(Resource *resource,
|
||||
int32_t fd,
|
||||
uint32_t plane_idx,
|
||||
uint32_t offset,
|
||||
uint32_t stride,
|
||||
uint32_t modifier_hi,
|
||||
uint32_t modifier_lo)
|
||||
{
|
||||
if (Q_UNLIKELY(m_isUsed)) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(plane_idx >= 4)) {
|
||||
wl_resource_post_error(resource->handle, error_plane_idx, "plane index %d is out of bounds", plane_idx);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(m_attrs.fd[plane_idx].isValid())) {
|
||||
wl_resource_post_error(resource->handle, error_plane_set, "the plane index %d was already set", plane_idx);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
m_attrs.fd[plane_idx] = FileDescriptor{fd};
|
||||
m_attrs.offset[plane_idx] = offset;
|
||||
m_attrs.pitch[plane_idx] = stride;
|
||||
m_attrs.modifier = (quint64(modifier_hi) << 32) | modifier_lo;
|
||||
m_attrs.planeCount++;
|
||||
}
|
||||
|
||||
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags)
|
||||
{
|
||||
if (Q_UNLIKELY(m_isUsed)) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(!test(resource, width, height))) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderBackend *renderBackend = m_integration->renderBackend();
|
||||
if (Q_UNLIKELY(!renderBackend)) {
|
||||
send_failed(resource->handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
send_failed(resource->handle);
|
||||
return;
|
||||
}
|
||||
|
||||
m_isUsed = true;
|
||||
|
||||
m_attrs.width = width;
|
||||
m_attrs.height = height;
|
||||
m_attrs.format = format;
|
||||
|
||||
auto clientBuffer = new LinuxDmaBufV1ClientBuffer(std::move(m_attrs));
|
||||
if (!renderBackend->testImportBuffer(clientBuffer)) {
|
||||
send_failed(resource->handle);
|
||||
delete clientBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0);
|
||||
if (!bufferResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
delete clientBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
clientBuffer->initialize(bufferResource);
|
||||
send_created(resource->handle, bufferResource);
|
||||
}
|
||||
|
||||
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *resource,
|
||||
uint32_t buffer_id,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
uint32_t format,
|
||||
uint32_t flags)
|
||||
{
|
||||
if (Q_UNLIKELY(m_isUsed)) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "the params object has already been used to create a wl_buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(!test(resource, width, height))) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderBackend *renderBackend = m_integration->renderBackend();
|
||||
if (Q_UNLIKELY(!renderBackend)) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "dma-buf flags are not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
m_isUsed = true;
|
||||
|
||||
m_attrs.width = width;
|
||||
m_attrs.height = height;
|
||||
m_attrs.format = format;
|
||||
|
||||
auto clientBuffer = new LinuxDmaBufV1ClientBuffer(std::move(m_attrs));
|
||||
if (!renderBackend->testImportBuffer(clientBuffer)) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
|
||||
delete clientBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id);
|
||||
if (!bufferResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
delete clientBuffer;
|
||||
return;
|
||||
}
|
||||
|
||||
clientBuffer->initialize(bufferResource);
|
||||
}
|
||||
|
||||
bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height)
|
||||
{
|
||||
if (Q_UNLIKELY(!m_attrs.planeCount)) {
|
||||
wl_resource_post_error(resource->handle, error_incomplete, "no planes have been specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for holes in the dmabuf set (e.g. [0, 1, 3]).
|
||||
for (int i = 0; i < m_attrs.planeCount; ++i) {
|
||||
if (!m_attrs.fd[i].isValid()) {
|
||||
wl_resource_post_error(resource->handle, error_incomplete, "no dmabuf has been added for plane %d", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(width == 0 || height == 0)) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_dimensions, "invalid width %d or height %d", width, height);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_attrs.planeCount; ++i) {
|
||||
// Check for overflows.
|
||||
if (Q_UNLIKELY(uint64_t(m_attrs.offset[i]) + m_attrs.pitch[i] > UINT32_MAX)) {
|
||||
wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(i == 0 && uint64_t(m_attrs.offset[i]) + uint64_t(m_attrs.pitch[i]) * height > UINT32_MAX)) {
|
||||
wl_resource_post_error(resource->handle, error_out_of_bounds, "size overflow for plane %d", i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't report an error as it might be caused by the kernel not supporting
|
||||
// seeking on dmabuf.
|
||||
const off_t size = lseek(m_attrs.fd[i].get(), 0, SEEK_END);
|
||||
if (size == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(m_attrs.offset[i] >= size)) {
|
||||
wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid offset %i for plane %d", m_attrs.offset[i], i);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(m_attrs.offset[i] + m_attrs.pitch[i] > size)) {
|
||||
wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid stride %i for plane %d", m_attrs.pitch[i], i);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only valid for first plane as other planes might be sub-sampled according to
|
||||
// fourcc format.
|
||||
if (Q_UNLIKELY(i == 0 && m_attrs.offset[i] + m_attrs.pitch[i] * height > size)) {
|
||||
wl_resource_post_error(resource->handle, error_out_of_bounds, "invalid buffer stride of height for plane %d", i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LinuxDmaBufV1ClientBufferIntegration::LinuxDmaBufV1ClientBufferIntegration(Display *display)
|
||||
: QObject(display)
|
||||
, d(new LinuxDmaBufV1ClientBufferIntegrationPrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
LinuxDmaBufV1ClientBufferIntegration::~LinuxDmaBufV1ClientBufferIntegration()
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const LinuxDmaBufV1Feedback::Tranche &t1, const LinuxDmaBufV1Feedback::Tranche &t2)
|
||||
{
|
||||
return t1.device == t2.device && t1.flags == t2.flags && t1.formatTable == t2.formatTable;
|
||||
}
|
||||
|
||||
RenderBackend *LinuxDmaBufV1ClientBufferIntegration::renderBackend() const
|
||||
{
|
||||
return d->renderBackend;
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegration::setRenderBackend(RenderBackend *renderBackend)
|
||||
{
|
||||
d->renderBackend = renderBackend;
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QList<LinuxDmaBufV1Feedback::Tranche> &tranches)
|
||||
{
|
||||
if (LinuxDmaBufV1FeedbackPrivate::get(d->defaultFeedback.get())->m_tranches != tranches) {
|
||||
QHash<uint32_t, QList<uint64_t>> set;
|
||||
for (const auto &tranche : tranches) {
|
||||
set.insert(tranche.formatTable);
|
||||
}
|
||||
d->supportedModifiers = set;
|
||||
d->mainDevice = tranches.first().device;
|
||||
d->table = std::make_unique<LinuxDmaBufV1FormatTable>(set);
|
||||
d->defaultFeedback->setTranches(tranches);
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBuffer::buffer_destroy_resource(wl_resource *resource)
|
||||
{
|
||||
if (LinuxDmaBufV1ClientBuffer *buffer = LinuxDmaBufV1ClientBuffer::get(resource)) {
|
||||
buffer->m_resource = nullptr;
|
||||
buffer->drop();
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBuffer::buffer_destroy(wl_client *client, wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource);
|
||||
}
|
||||
|
||||
const struct wl_buffer_interface LinuxDmaBufV1ClientBuffer::implementation = {
|
||||
.destroy = buffer_destroy,
|
||||
};
|
||||
|
||||
LinuxDmaBufV1ClientBuffer::LinuxDmaBufV1ClientBuffer(DmaBufAttributes &&attrs)
|
||||
{
|
||||
m_attrs = std::move(attrs);
|
||||
m_hasAlphaChannel = alphaChannelFromDrmFormat(m_attrs.format);
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1ClientBuffer::initialize(wl_resource *resource)
|
||||
{
|
||||
m_resource = resource;
|
||||
wl_resource_set_implementation(resource, &implementation, this, buffer_destroy_resource);
|
||||
|
||||
connect(this, &GraphicsBuffer::released, [this]() {
|
||||
wl_buffer_send_release(m_resource);
|
||||
});
|
||||
}
|
||||
|
||||
const DmaBufAttributes *LinuxDmaBufV1ClientBuffer::dmabufAttributes() const
|
||||
{
|
||||
return &m_attrs;
|
||||
}
|
||||
|
||||
QSize LinuxDmaBufV1ClientBuffer::size() const
|
||||
{
|
||||
return QSize(m_attrs.width, m_attrs.height);
|
||||
}
|
||||
|
||||
bool LinuxDmaBufV1ClientBuffer::hasAlphaChannel() const
|
||||
{
|
||||
return m_hasAlphaChannel;
|
||||
}
|
||||
|
||||
LinuxDmaBufV1ClientBuffer *LinuxDmaBufV1ClientBuffer::get(wl_resource *resource)
|
||||
{
|
||||
if (wl_resource_instance_of(resource, &wl_buffer_interface, &implementation)) {
|
||||
return static_cast<LinuxDmaBufV1ClientBuffer *>(wl_resource_get_user_data(resource));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LinuxDmaBufV1Feedback::LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegrationPrivate *integration)
|
||||
: d(new LinuxDmaBufV1FeedbackPrivate(integration))
|
||||
{
|
||||
}
|
||||
|
||||
LinuxDmaBufV1Feedback::~LinuxDmaBufV1Feedback() = default;
|
||||
|
||||
void LinuxDmaBufV1Feedback::setScanoutTranches(DrmDevice *device, const QHash<uint32_t, QList<uint64_t>> &formats)
|
||||
{
|
||||
setTranches(createScanoutTranches(d->m_bufferintegration->defaultFeedback->d->m_tranches, device, formats));
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1Feedback::setTranches(const QList<Tranche> &tranches)
|
||||
{
|
||||
if (d->m_tranches != tranches) {
|
||||
d->m_tranches = tranches;
|
||||
const auto &map = d->resourceMap();
|
||||
for (const auto &resource : map) {
|
||||
d->send(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<LinuxDmaBufV1Feedback::Tranche> LinuxDmaBufV1Feedback::createScanoutTranches(const QList<Tranche> &tranches, DrmDevice *device, const QHash<uint32_t, QList<uint64_t>> &formats)
|
||||
{
|
||||
QList<LinuxDmaBufV1Feedback::Tranche> ret;
|
||||
for (const auto &tranche : tranches) {
|
||||
LinuxDmaBufV1Feedback::Tranche scanoutTranche;
|
||||
for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
|
||||
const uint32_t format = it.key();
|
||||
const auto trancheModifiers = it.value();
|
||||
const auto drmModifiers = formats[format];
|
||||
for (const auto &mod : trancheModifiers) {
|
||||
if (drmModifiers.contains(mod)) {
|
||||
scanoutTranche.formatTable[format] << mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!scanoutTranche.formatTable.isEmpty()) {
|
||||
scanoutTranche.device = device->deviceId();
|
||||
scanoutTranche.flags = LinuxDmaBufV1Feedback::TrancheFlag::Scanout;
|
||||
ret.push_back(scanoutTranche);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
LinuxDmaBufV1FeedbackPrivate *LinuxDmaBufV1FeedbackPrivate::get(LinuxDmaBufV1Feedback *q)
|
||||
{
|
||||
return q->d.get();
|
||||
}
|
||||
|
||||
LinuxDmaBufV1FeedbackPrivate::LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration)
|
||||
: m_bufferintegration(bufferintegration)
|
||||
{
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1FeedbackPrivate::send(Resource *resource)
|
||||
{
|
||||
send_format_table(resource->handle, m_bufferintegration->table->file.fd(), m_bufferintegration->table->file.size());
|
||||
QByteArray bytes;
|
||||
bytes.append(reinterpret_cast<const char *>(&m_bufferintegration->mainDevice), sizeof(dev_t));
|
||||
send_main_device(resource->handle, bytes);
|
||||
const auto &sendTranche = [this, resource](const LinuxDmaBufV1Feedback::Tranche &tranche) {
|
||||
QByteArray targetDevice;
|
||||
targetDevice.append(reinterpret_cast<const char *>(&tranche.device), sizeof(dev_t));
|
||||
QByteArray indices;
|
||||
for (auto it = tranche.formatTable.begin(); it != tranche.formatTable.end(); it++) {
|
||||
const uint32_t format = it.key();
|
||||
for (const auto &mod : std::as_const(it.value())) {
|
||||
uint16_t index = m_bufferintegration->table->indices[std::pair<uint32_t, uint64_t>(format, mod)];
|
||||
indices.append(reinterpret_cast<const char *>(&index), 2);
|
||||
}
|
||||
}
|
||||
send_tranche_target_device(resource->handle, targetDevice);
|
||||
send_tranche_formats(resource->handle, indices);
|
||||
send_tranche_flags(resource->handle, static_cast<uint32_t>(tranche.flags));
|
||||
send_tranche_done(resource->handle);
|
||||
};
|
||||
for (const auto &tranche : std::as_const(m_tranches)) {
|
||||
sendTranche(tranche);
|
||||
}
|
||||
// send default hints as the last fallback tranche
|
||||
const auto defaultFeedbackPrivate = get(m_bufferintegration->defaultFeedback.get());
|
||||
if (this != defaultFeedbackPrivate) {
|
||||
for (const auto &tranche : std::as_const(defaultFeedbackPrivate->m_tranches)) {
|
||||
sendTranche(tranche);
|
||||
}
|
||||
}
|
||||
send_done(resource->handle);
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource)
|
||||
{
|
||||
send(resource);
|
||||
}
|
||||
|
||||
void LinuxDmaBufV1FeedbackPrivate::zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
struct linux_dmabuf_feedback_v1_table_entry
|
||||
{
|
||||
uint32_t format;
|
||||
uint32_t pad; // unused
|
||||
uint64_t modifier;
|
||||
};
|
||||
|
||||
LinuxDmaBufV1FormatTable::LinuxDmaBufV1FormatTable(const QHash<uint32_t, QList<uint64_t>> &supportedModifiers)
|
||||
{
|
||||
QList<linux_dmabuf_feedback_v1_table_entry> data;
|
||||
for (auto it = supportedModifiers.begin(); it != supportedModifiers.end(); it++) {
|
||||
const uint32_t format = it.key();
|
||||
for (const uint64_t &mod : *it) {
|
||||
indices.insert({format, mod}, data.size());
|
||||
data.append({format, 0, mod});
|
||||
}
|
||||
}
|
||||
|
||||
const auto size = data.size() * sizeof(linux_dmabuf_feedback_v1_table_entry);
|
||||
file = RamFile("kwin-dmabuf-feedback-table", data.constData(), size, RamFile::Flag::SealWrite);
|
||||
if (!file.isValid()) {
|
||||
qCCritical(KWIN_CORE) << "Failed to create RamFile for LinuxDmaBufV1FormatTable";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_linuxdmabufv1clientbuffer_p.cpp"
|
||||
|
||||
#include "moc_linuxdmabufv1clientbuffer.cpp"
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/graphicsbuffer.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <sys/types.h>
|
||||
#include <wayland-server.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class LinuxDmaBufV1ClientBufferIntegrationPrivate;
|
||||
class LinuxDmaBufV1FeedbackPrivate;
|
||||
class RenderBackend;
|
||||
class DrmDevice;
|
||||
|
||||
class KWIN_EXPORT LinuxDmaBufV1Feedback : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~LinuxDmaBufV1Feedback() override;
|
||||
|
||||
enum class TrancheFlag : uint32_t {
|
||||
Scanout = 1,
|
||||
};
|
||||
Q_DECLARE_FLAGS(TrancheFlags, TrancheFlag)
|
||||
|
||||
struct Tranche
|
||||
{
|
||||
dev_t device;
|
||||
TrancheFlags flags;
|
||||
QHash<uint32_t, QList<uint64_t>> formatTable;
|
||||
};
|
||||
/**
|
||||
* Sets the list of tranches for this feedback object, with lower indices
|
||||
* indicating a higher priority / a more optimal configuration.
|
||||
* The main device does not need to be included
|
||||
*/
|
||||
void setScanoutTranches(DrmDevice *device, const QHash<uint32_t, QList<uint64_t>> &formats);
|
||||
void setTranches(const QList<Tranche> &tranches);
|
||||
|
||||
private:
|
||||
static QList<Tranche> createScanoutTranches(const QList<Tranche> &tranches, DrmDevice *device, const QHash<uint32_t, QList<uint64_t>> &formats);
|
||||
|
||||
LinuxDmaBufV1Feedback(LinuxDmaBufV1ClientBufferIntegrationPrivate *integration);
|
||||
friend class LinuxDmaBufV1ClientBufferIntegrationPrivate;
|
||||
friend class LinuxDmaBufV1FeedbackPrivate;
|
||||
std::unique_ptr<LinuxDmaBufV1FeedbackPrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* The LinuxDmaBufV1ClientBufferIntegration class provides support for linux dma-buf buffers.
|
||||
*/
|
||||
class KWIN_EXPORT LinuxDmaBufV1ClientBufferIntegration : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LinuxDmaBufV1ClientBufferIntegration(Display *display);
|
||||
~LinuxDmaBufV1ClientBufferIntegration() override;
|
||||
|
||||
RenderBackend *renderBackend() const;
|
||||
void setRenderBackend(RenderBackend *renderBackend);
|
||||
|
||||
void setSupportedFormatsWithModifiers(const QList<LinuxDmaBufV1Feedback::Tranche> &tranches);
|
||||
|
||||
private:
|
||||
friend class LinuxDmaBufV1ClientBufferIntegrationPrivate;
|
||||
std::unique_ptr<LinuxDmaBufV1ClientBufferIntegrationPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
Based on the libweston implementation,
|
||||
SPDX-FileCopyrightText: 2014, 2015 Collabora, Ltd.
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "display.h"
|
||||
#include "linuxdmabufv1clientbuffer.h"
|
||||
#include "utils/ramfile.h"
|
||||
|
||||
#include "qwayland-server-linux-dmabuf-unstable-v1.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class LinuxDmaBufV1FormatTable;
|
||||
|
||||
class LinuxDmaBufV1ClientBufferIntegrationPrivate : public QtWaylandServer::zwp_linux_dmabuf_v1
|
||||
{
|
||||
public:
|
||||
LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display);
|
||||
|
||||
LinuxDmaBufV1ClientBufferIntegration *q;
|
||||
std::unique_ptr<LinuxDmaBufV1Feedback> defaultFeedback;
|
||||
std::unique_ptr<LinuxDmaBufV1FormatTable> table;
|
||||
dev_t mainDevice;
|
||||
QPointer<RenderBackend> renderBackend;
|
||||
QHash<uint32_t, QList<uint64_t>> supportedModifiers;
|
||||
|
||||
protected:
|
||||
void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override;
|
||||
void zwp_linux_dmabuf_v1_destroy(Resource *resource) override;
|
||||
void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override;
|
||||
void zwp_linux_dmabuf_v1_get_default_feedback(Resource *resource, uint32_t id) override;
|
||||
void zwp_linux_dmabuf_v1_get_surface_feedback(Resource *resource, uint32_t id, wl_resource *surface) override;
|
||||
};
|
||||
|
||||
class LinuxDmaBufParamsV1 : public QtWaylandServer::zwp_linux_buffer_params_v1
|
||||
{
|
||||
public:
|
||||
LinuxDmaBufParamsV1(LinuxDmaBufV1ClientBufferIntegration *integration, ::wl_resource *resource);
|
||||
|
||||
protected:
|
||||
void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override;
|
||||
void zwp_linux_buffer_params_v1_destroy(Resource *resource) override;
|
||||
void zwp_linux_buffer_params_v1_add(Resource *resource,
|
||||
int32_t fd,
|
||||
uint32_t plane_idx,
|
||||
uint32_t offset,
|
||||
uint32_t stride,
|
||||
uint32_t modifier_hi,
|
||||
uint32_t modifier_lo) override;
|
||||
void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
|
||||
void
|
||||
zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) override;
|
||||
|
||||
private:
|
||||
bool test(Resource *resource, uint32_t width, uint32_t height);
|
||||
|
||||
LinuxDmaBufV1ClientBufferIntegration *m_integration;
|
||||
DmaBufAttributes m_attrs;
|
||||
bool m_isUsed = false;
|
||||
};
|
||||
|
||||
class LinuxDmaBufV1ClientBuffer : public GraphicsBuffer
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LinuxDmaBufV1ClientBuffer(DmaBufAttributes &&attrs);
|
||||
|
||||
QSize size() const override;
|
||||
bool hasAlphaChannel() const override;
|
||||
const DmaBufAttributes *dmabufAttributes() const override;
|
||||
|
||||
static LinuxDmaBufV1ClientBuffer *get(wl_resource *resource);
|
||||
|
||||
private:
|
||||
void initialize(wl_resource *resource);
|
||||
|
||||
static void buffer_destroy_resource(wl_resource *resource);
|
||||
static void buffer_destroy(wl_client *client, wl_resource *resource);
|
||||
static const struct wl_buffer_interface implementation;
|
||||
|
||||
wl_resource *m_resource = nullptr;
|
||||
DmaBufAttributes m_attrs;
|
||||
bool m_hasAlphaChannel = false;
|
||||
|
||||
friend class LinuxDmaBufParamsV1;
|
||||
};
|
||||
|
||||
class LinuxDmaBufV1FormatTable
|
||||
{
|
||||
public:
|
||||
LinuxDmaBufV1FormatTable(const QHash<uint32_t, QList<uint64_t>> &supportedModifiers);
|
||||
|
||||
RamFile file;
|
||||
QMap<std::pair<uint32_t, uint64_t>, uint16_t> indices;
|
||||
};
|
||||
|
||||
class LinuxDmaBufV1FeedbackPrivate : public QtWaylandServer::zwp_linux_dmabuf_feedback_v1
|
||||
{
|
||||
public:
|
||||
LinuxDmaBufV1FeedbackPrivate(LinuxDmaBufV1ClientBufferIntegrationPrivate *bufferintegration);
|
||||
|
||||
static LinuxDmaBufV1FeedbackPrivate *get(LinuxDmaBufV1Feedback *q);
|
||||
void send(Resource *resource);
|
||||
|
||||
QList<LinuxDmaBufV1Feedback::Tranche> m_tranches;
|
||||
LinuxDmaBufV1ClientBufferIntegrationPrivate *m_bufferintegration;
|
||||
|
||||
protected:
|
||||
void zwp_linux_dmabuf_feedback_v1_bind_resource(Resource *resource) override;
|
||||
void zwp_linux_dmabuf_feedback_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "lockscreen_overlay_v1.h"
|
||||
#include "display.h"
|
||||
#include "seat.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include "qwayland-server-kde-lockscreen-overlay-v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static constexpr int s_version = 1;
|
||||
|
||||
class LockscreenOverlayV1InterfacePrivate : public QtWaylandServer::kde_lockscreen_overlay_v1
|
||||
{
|
||||
public:
|
||||
LockscreenOverlayV1InterfacePrivate(Display *display, LockscreenOverlayV1Interface *q)
|
||||
: QtWaylandServer::kde_lockscreen_overlay_v1(*display, s_version)
|
||||
, q(q)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void kde_lockscreen_overlay_v1_allow(Resource *resource, struct ::wl_resource *surface) override
|
||||
{
|
||||
auto surfaceIface = SurfaceInterface::get(surface);
|
||||
if (surfaceIface->isMapped()) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_surface_state, "surface is already mapped");
|
||||
return;
|
||||
}
|
||||
Q_EMIT q->allowRequested(surfaceIface);
|
||||
}
|
||||
void kde_lockscreen_overlay_v1_destroy(Resource *resource) override
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
private:
|
||||
LockscreenOverlayV1Interface *const q;
|
||||
};
|
||||
|
||||
LockscreenOverlayV1Interface::~LockscreenOverlayV1Interface() = default;
|
||||
|
||||
LockscreenOverlayV1Interface::LockscreenOverlayV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(std::make_unique<LockscreenOverlayV1InterfacePrivate>(display, this))
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_lockscreen_overlay_v1.cpp"
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
|
||||
class LockscreenOverlayV1InterfacePrivate;
|
||||
|
||||
class KWIN_EXPORT LockscreenOverlayV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(LockscreenOverlayV1Interface)
|
||||
public:
|
||||
explicit LockscreenOverlayV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~LockscreenOverlayV1Interface() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/// Notifies about the @p surface being activated
|
||||
void allowRequested(SurfaceInterface *surface);
|
||||
|
||||
private:
|
||||
friend class LockscreenOverlayV1InterfacePrivate;
|
||||
LockscreenOverlayV1Interface(LockscreenOverlayV1Interface *parent);
|
||||
std::unique_ptr<LockscreenOverlayV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "output.h"
|
||||
#include "display.h"
|
||||
#include "display_p.h"
|
||||
#include "utils/resource.h"
|
||||
|
||||
#include "core/output.h"
|
||||
|
||||
#include "qwayland-server-wayland.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const int s_version = 4;
|
||||
|
||||
class OutputInterfacePrivate : public QtWaylandServer::wl_output
|
||||
{
|
||||
public:
|
||||
explicit OutputInterfacePrivate(Display *display, OutputInterface *q, Output *handle);
|
||||
|
||||
void sendScale(Resource *resource);
|
||||
void sendGeometry(Resource *resource);
|
||||
void sendMode(Resource *resource);
|
||||
void sendDone(Resource *resource);
|
||||
|
||||
OutputInterface *q;
|
||||
QPointer<Display> display;
|
||||
QPointer<Output> handle;
|
||||
QSize physicalSize;
|
||||
QPoint globalPosition;
|
||||
QString manufacturer;
|
||||
QString model;
|
||||
int scale = 1;
|
||||
Output::SubPixel subPixel = Output::SubPixel::Unknown;
|
||||
OutputTransform transform = OutputTransform::Normal;
|
||||
QSize modeSize;
|
||||
int refreshRate = 0;
|
||||
QString name;
|
||||
QString description;
|
||||
QTimer doneTimer;
|
||||
|
||||
private:
|
||||
void output_destroy_global() override;
|
||||
void output_bind_resource(Resource *resource) override;
|
||||
void output_release(Resource *resource) override;
|
||||
};
|
||||
|
||||
OutputInterfacePrivate::OutputInterfacePrivate(Display *display, OutputInterface *q, Output *handle)
|
||||
: QtWaylandServer::wl_output(*display, s_version)
|
||||
, q(q)
|
||||
, display(display)
|
||||
, handle(handle)
|
||||
{
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::sendMode(Resource *resource)
|
||||
{
|
||||
send_mode(resource->handle, mode_current, modeSize.width(), modeSize.height(), refreshRate);
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::sendScale(Resource *resource)
|
||||
{
|
||||
if (resource->version() >= WL_OUTPUT_SCALE_SINCE_VERSION) {
|
||||
send_scale(resource->handle, scale);
|
||||
}
|
||||
}
|
||||
|
||||
static quint32 kwaylandServerTransformToWaylandTransform(OutputTransform transform)
|
||||
{
|
||||
switch (transform.kind()) {
|
||||
case OutputTransform::Normal:
|
||||
return OutputInterfacePrivate::transform_normal;
|
||||
case OutputTransform::Rotate90:
|
||||
return OutputInterfacePrivate::transform_90;
|
||||
case OutputTransform::Rotate180:
|
||||
return OutputInterfacePrivate::transform_180;
|
||||
case OutputTransform::Rotate270:
|
||||
return OutputInterfacePrivate::transform_270;
|
||||
case OutputTransform::FlipX:
|
||||
return OutputInterfacePrivate::transform_flipped;
|
||||
case OutputTransform::FlipX90:
|
||||
return OutputInterfacePrivate::transform_flipped_90;
|
||||
case OutputTransform::FlipX180:
|
||||
return OutputInterfacePrivate::transform_flipped_180;
|
||||
case OutputTransform::FlipX270:
|
||||
return OutputInterfacePrivate::transform_flipped_270;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
static quint32 kwaylandServerSubPixelToWaylandSubPixel(Output::SubPixel subPixel)
|
||||
{
|
||||
switch (subPixel) {
|
||||
case Output::SubPixel::Unknown:
|
||||
return OutputInterfacePrivate::subpixel_unknown;
|
||||
case Output::SubPixel::None:
|
||||
return OutputInterfacePrivate::subpixel_none;
|
||||
case Output::SubPixel::Horizontal_RGB:
|
||||
return OutputInterfacePrivate::subpixel_horizontal_rgb;
|
||||
case Output::SubPixel::Horizontal_BGR:
|
||||
return OutputInterfacePrivate::subpixel_horizontal_bgr;
|
||||
case Output::SubPixel::Vertical_RGB:
|
||||
return OutputInterfacePrivate::subpixel_vertical_rgb;
|
||||
case Output::SubPixel::Vertical_BGR:
|
||||
return OutputInterfacePrivate::subpixel_vertical_bgr;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::sendGeometry(Resource *resource)
|
||||
{
|
||||
send_geometry(resource->handle,
|
||||
globalPosition.x(),
|
||||
globalPosition.y(),
|
||||
physicalSize.width(),
|
||||
physicalSize.height(),
|
||||
kwaylandServerSubPixelToWaylandSubPixel(subPixel),
|
||||
manufacturer,
|
||||
model,
|
||||
kwaylandServerTransformToWaylandTransform(transform));
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::sendDone(Resource *resource)
|
||||
{
|
||||
if (resource->version() >= WL_OUTPUT_DONE_SINCE_VERSION) {
|
||||
send_done(resource->handle);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::output_destroy_global()
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::output_release(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void OutputInterfacePrivate::output_bind_resource(Resource *resource)
|
||||
{
|
||||
if (isGlobalRemoved()) {
|
||||
return; // We are waiting for the wl_output global to be destroyed.
|
||||
}
|
||||
|
||||
if (resource->version() >= WL_OUTPUT_NAME_SINCE_VERSION) {
|
||||
send_name(resource->handle, name);
|
||||
}
|
||||
if (resource->version() >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) {
|
||||
send_description(resource->handle, description);
|
||||
}
|
||||
|
||||
sendMode(resource);
|
||||
sendScale(resource);
|
||||
sendGeometry(resource);
|
||||
sendDone(resource);
|
||||
|
||||
Q_EMIT q->bound(display->getConnection(resource->client()), resource->handle);
|
||||
}
|
||||
|
||||
OutputInterface::OutputInterface(Display *display, Output *handle, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new OutputInterfacePrivate(display, this, handle))
|
||||
{
|
||||
DisplayPrivate *displayPrivate = DisplayPrivate::get(display);
|
||||
displayPrivate->outputs.append(this);
|
||||
|
||||
// Delay the done event to batch property updates.
|
||||
d->doneTimer.setSingleShot(true);
|
||||
d->doneTimer.setInterval(0);
|
||||
connect(&d->doneTimer, &QTimer::timeout, this, [this]() {
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->sendDone(resource);
|
||||
}
|
||||
});
|
||||
|
||||
d->name = handle->name();
|
||||
d->description = handle->description();
|
||||
d->transform = handle->transform();
|
||||
d->manufacturer = handle->manufacturer();
|
||||
d->model = handle->model();
|
||||
d->physicalSize = handle->physicalSize();
|
||||
d->globalPosition = handle->geometry().topLeft();
|
||||
d->scale = std::ceil(handle->scale());
|
||||
d->modeSize = handle->modeSize();
|
||||
d->refreshRate = handle->refreshRate();
|
||||
d->subPixel = handle->subPixel();
|
||||
|
||||
connect(handle, &Output::geometryChanged, this, [this]() {
|
||||
const QPoint position = d->handle->geometry().topLeft();
|
||||
if (d->globalPosition != position) {
|
||||
d->globalPosition = position;
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->sendGeometry(resource);
|
||||
}
|
||||
scheduleDone();
|
||||
}
|
||||
});
|
||||
|
||||
connect(handle, &Output::scaleChanged, this, [this]() {
|
||||
const int scale = std::ceil(d->handle->scale());
|
||||
if (d->scale != scale) {
|
||||
d->scale = scale;
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->sendScale(resource);
|
||||
}
|
||||
scheduleDone();
|
||||
}
|
||||
});
|
||||
|
||||
connect(handle, &Output::transformChanged, this, [this]() {
|
||||
const OutputTransform transform = d->handle->transform();
|
||||
if (d->transform != transform) {
|
||||
d->transform = transform;
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->sendGeometry(resource);
|
||||
}
|
||||
scheduleDone();
|
||||
}
|
||||
});
|
||||
|
||||
connect(handle, &Output::currentModeChanged, this, [this]() {
|
||||
const QSize size = d->handle->modeSize();
|
||||
const int refreshRate = d->handle->refreshRate();
|
||||
if (d->modeSize != size || d->refreshRate != refreshRate) {
|
||||
d->modeSize = size;
|
||||
d->refreshRate = refreshRate;
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->sendMode(resource);
|
||||
}
|
||||
scheduleDone();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
OutputInterface::~OutputInterface()
|
||||
{
|
||||
remove();
|
||||
}
|
||||
|
||||
Display *OutputInterface::display() const
|
||||
{
|
||||
return d->display;
|
||||
}
|
||||
|
||||
Output *OutputInterface::handle() const
|
||||
{
|
||||
return d->handle;
|
||||
}
|
||||
|
||||
bool OutputInterface::isRemoved() const
|
||||
{
|
||||
return d->isGlobalRemoved();
|
||||
}
|
||||
|
||||
void OutputInterface::remove()
|
||||
{
|
||||
if (d->isGlobalRemoved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->doneTimer.stop();
|
||||
if (d->handle) {
|
||||
disconnect(d->handle, nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
if (d->display) {
|
||||
DisplayPrivate *displayPrivate = DisplayPrivate::get(d->display);
|
||||
displayPrivate->outputs.removeOne(this);
|
||||
}
|
||||
|
||||
Q_EMIT removed();
|
||||
d->globalRemove();
|
||||
}
|
||||
|
||||
QList<wl_resource *> OutputInterface::clientResources(wl_client *client) const
|
||||
{
|
||||
const auto outputResources = d->resourceMap().values(client);
|
||||
QList<wl_resource *> ret;
|
||||
ret.reserve(outputResources.count());
|
||||
|
||||
for (OutputInterfacePrivate::Resource *resource : outputResources) {
|
||||
ret.append(resource->handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OutputInterface::scheduleDone()
|
||||
{
|
||||
if (!d->isGlobalRemoved()) {
|
||||
d->doneTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void OutputInterface::done(wl_client *client)
|
||||
{
|
||||
if (!d->isGlobalRemoved()) {
|
||||
d->sendDone(d->resourceMap().value(client));
|
||||
}
|
||||
}
|
||||
|
||||
OutputInterface *OutputInterface::get(wl_resource *native)
|
||||
{
|
||||
if (auto outputPrivate = resource_cast<OutputInterfacePrivate *>(native)) {
|
||||
return outputPrivate->q;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "wayland/moc_output.cpp"
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "core/output.h"
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
|
||||
struct wl_resource;
|
||||
struct wl_client;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Output;
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class ClientConnection;
|
||||
class Display;
|
||||
class OutputInterfacePrivate;
|
||||
|
||||
/**
|
||||
* The OutputInterface class represents a screen. This class corresponds to the Wayland
|
||||
* interface @c wl_output.
|
||||
*/
|
||||
class KWIN_EXPORT OutputInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OutputInterface(Display *display, Output *handle, QObject *parent = nullptr);
|
||||
~OutputInterface() override;
|
||||
|
||||
bool isRemoved() const;
|
||||
void remove();
|
||||
|
||||
Output *handle() const;
|
||||
|
||||
/**
|
||||
* @returns all wl_resources bound for the @p client
|
||||
*/
|
||||
QList<wl_resource *> clientResources(wl_client *client) const;
|
||||
|
||||
/**
|
||||
* Submit changes to all clients.
|
||||
*/
|
||||
void scheduleDone();
|
||||
|
||||
/**
|
||||
* Submit changes to @p client.
|
||||
*/
|
||||
void done(wl_client *client);
|
||||
|
||||
static OutputInterface *get(wl_resource *native);
|
||||
|
||||
Display *display() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void removed();
|
||||
|
||||
/**
|
||||
* Emitted when a client binds to a given output
|
||||
* @internal
|
||||
*/
|
||||
void bound(ClientConnection *client, wl_resource *boundResource);
|
||||
|
||||
private:
|
||||
std::unique_ptr<OutputInterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "output_order_v1.h"
|
||||
#include "core/output.h"
|
||||
#include "display.h"
|
||||
|
||||
#include "qwayland-server-kde-output-order-v1.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_version = 1;
|
||||
|
||||
class OutputOrderV1InterfacePrivate : public QtWaylandServer::kde_output_order_v1
|
||||
{
|
||||
public:
|
||||
OutputOrderV1InterfacePrivate(Display *display);
|
||||
|
||||
void sendList(wl_resource *resource);
|
||||
QList<Output *> outputOrder;
|
||||
|
||||
protected:
|
||||
void kde_output_order_v1_bind_resource(Resource *resource) override;
|
||||
void kde_output_order_v1_destroy(Resource *resource) override;
|
||||
};
|
||||
|
||||
OutputOrderV1Interface::OutputOrderV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(std::make_unique<OutputOrderV1InterfacePrivate>(display))
|
||||
{
|
||||
}
|
||||
|
||||
OutputOrderV1Interface::~OutputOrderV1Interface() = default;
|
||||
|
||||
void OutputOrderV1Interface::setOutputOrder(const QList<Output *> &outputOrder)
|
||||
{
|
||||
d->outputOrder = outputOrder;
|
||||
const auto resources = d->resourceMap();
|
||||
for (const auto &resource : resources) {
|
||||
d->sendList(resource->handle);
|
||||
}
|
||||
}
|
||||
|
||||
OutputOrderV1InterfacePrivate::OutputOrderV1InterfacePrivate(Display *display)
|
||||
: QtWaylandServer::kde_output_order_v1(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void OutputOrderV1InterfacePrivate::kde_output_order_v1_bind_resource(Resource *resource)
|
||||
{
|
||||
sendList(resource->handle);
|
||||
}
|
||||
|
||||
void OutputOrderV1InterfacePrivate::sendList(wl_resource *resource)
|
||||
{
|
||||
for (Output *const output : std::as_const(outputOrder)) {
|
||||
kde_output_order_v1_send_output(resource, output->name().toUtf8().constData());
|
||||
}
|
||||
kde_output_order_v1_send_done(resource);
|
||||
}
|
||||
|
||||
void OutputOrderV1InterfacePrivate::kde_output_order_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_output_order_v1.cpp"
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Output;
|
||||
class Display;
|
||||
class OutputOrderV1InterfacePrivate;
|
||||
|
||||
class OutputOrderV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OutputOrderV1Interface(Display *display, QObject *parent);
|
||||
~OutputOrderV1Interface() override;
|
||||
|
||||
void setOutputOrder(const QList<Output *> &outputOrder);
|
||||
|
||||
private:
|
||||
std::unique_ptr<OutputOrderV1InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
#include <QUuid>
|
||||
#include <memory>
|
||||
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Output;
|
||||
class OutputMode;
|
||||
class Display;
|
||||
class OutputDeviceV2InterfacePrivate;
|
||||
class OutputDeviceModeV2Interface;
|
||||
class OutputDeviceModeV2InterfacePrivate;
|
||||
|
||||
/** @class OutputDeviceV2Interface
|
||||
*
|
||||
* Represents an output device, the difference to Output is that this output can be disabled,
|
||||
* so not currently used to display content.
|
||||
*
|
||||
* @see OutputManagementV2Interface
|
||||
*/
|
||||
class KWIN_EXPORT OutputDeviceV2Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OutputDeviceV2Interface(Display *display, Output *handle, QObject *parent = nullptr);
|
||||
~OutputDeviceV2Interface() override;
|
||||
|
||||
void remove();
|
||||
|
||||
Output *handle() const;
|
||||
|
||||
static OutputDeviceV2Interface *get(wl_resource *native);
|
||||
|
||||
private:
|
||||
void updatePhysicalSize();
|
||||
void updateGlobalPosition();
|
||||
void updateManufacturer();
|
||||
void updateModel();
|
||||
void updateSerialNumber();
|
||||
void updateEisaId();
|
||||
void updateName();
|
||||
void updateScale();
|
||||
void updateSubPixel();
|
||||
void updateTransform();
|
||||
void updateModes();
|
||||
void updateCurrentMode();
|
||||
void updateEdid();
|
||||
void updateEnabled();
|
||||
void updateUuid();
|
||||
void updateCapabilities();
|
||||
void updateOverscan();
|
||||
void updateVrrPolicy();
|
||||
void updateRgbRange();
|
||||
void updateGeometry();
|
||||
void updateHighDynamicRange();
|
||||
void updateSdrBrightness();
|
||||
void updateWideColorGamut();
|
||||
void updateAutoRotate();
|
||||
void updateIccProfilePath();
|
||||
void updateBrightnessMetadata();
|
||||
void updateBrightnessOverrides();
|
||||
void updateSdrGamutWideness();
|
||||
void updateColorProfileSource();
|
||||
void updateBrightness();
|
||||
void updateColorPowerTradeoff();
|
||||
void updateDimming();
|
||||
|
||||
void scheduleDone();
|
||||
|
||||
std::unique_ptr<OutputDeviceV2InterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class OutputDeviceModeV2Interface
|
||||
*
|
||||
* Represents an output device mode.
|
||||
*
|
||||
* @see OutputDeviceV2Interface
|
||||
*/
|
||||
class KWIN_EXPORT OutputDeviceModeV2Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OutputDeviceModeV2Interface(std::shared_ptr<OutputMode> handle, QObject *parent = nullptr);
|
||||
~OutputDeviceModeV2Interface() override;
|
||||
|
||||
std::weak_ptr<OutputMode> handle() const;
|
||||
|
||||
static OutputDeviceModeV2Interface *get(wl_resource *native);
|
||||
|
||||
private:
|
||||
friend class OutputDeviceModeV2InterfacePrivate;
|
||||
std::unique_ptr<OutputDeviceModeV2InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "outputmanagement_v2.h"
|
||||
#include "core/iccprofile.h"
|
||||
#include "core/outputbackend.h"
|
||||
#include "core/outputconfiguration.h"
|
||||
#include "display.h"
|
||||
#include "main.h"
|
||||
#include "outputdevice_v2.h"
|
||||
#include "outputmanagement_v2.h"
|
||||
#include "utils/common.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#include "qwayland-server-kde-output-management-v2.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const quint32 s_version = 12;
|
||||
|
||||
class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
|
||||
{
|
||||
public:
|
||||
OutputManagementV2InterfacePrivate(Display *display);
|
||||
|
||||
protected:
|
||||
void kde_output_management_v2_create_configuration(Resource *resource, uint32_t id) override;
|
||||
};
|
||||
|
||||
class OutputConfigurationV2Interface : public QObject, QtWaylandServer::kde_output_configuration_v2
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OutputConfigurationV2Interface(wl_resource *resource);
|
||||
|
||||
bool applied = false;
|
||||
bool invalid = false;
|
||||
OutputConfiguration config;
|
||||
QList<std::pair<uint32_t, OutputDeviceV2Interface *>> outputOrder;
|
||||
QString failureReason;
|
||||
|
||||
protected:
|
||||
void kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable) override;
|
||||
void kde_output_configuration_v2_mode(Resource *resource, struct ::wl_resource *outputdevice, struct ::wl_resource *mode) override;
|
||||
void kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform) override;
|
||||
void kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y) override;
|
||||
void kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale) override;
|
||||
void kde_output_configuration_v2_apply(Resource *resource) override;
|
||||
void kde_output_configuration_v2_destroy(Resource *resource) override;
|
||||
void kde_output_configuration_v2_destroy_resource(Resource *resource) override;
|
||||
void kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan) override;
|
||||
void kde_output_configuration_v2_set_vrr_policy(Resource *resource, struct ::wl_resource *outputdevice, uint32_t policy) override;
|
||||
void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override;
|
||||
void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override;
|
||||
void kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *output, uint32_t priority) override;
|
||||
void kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr) override;
|
||||
void kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness) override;
|
||||
void kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg) override;
|
||||
void kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy) override;
|
||||
void kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path) override;
|
||||
void kde_output_configuration_v2_set_brightness_overrides(Resource *resource, wl_resource *outputdevice, int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness) override;
|
||||
void kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness) override;
|
||||
void kde_output_configuration_v2_set_color_profile_source(Resource *resource, wl_resource *outputdevice, uint32_t source) override;
|
||||
void kde_output_configuration_v2_set_brightness(Resource *resource, wl_resource *outputdevice, uint32_t brightness) override;
|
||||
void kde_output_configuration_v2_set_color_power_tradeoff(Resource *resource, wl_resource *outputdevice, uint32_t preference) override;
|
||||
void kde_output_configuration_v2_set_dimming(Resource *resource, ::wl_resource *outputdevice, uint32_t multiplier) override;
|
||||
|
||||
void sendFailure(Resource *resource, const QString &reason);
|
||||
};
|
||||
|
||||
OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(Display *display)
|
||||
: QtWaylandServer::kde_output_management_v2(*display, s_version)
|
||||
{
|
||||
}
|
||||
|
||||
void OutputManagementV2InterfacePrivate::kde_output_management_v2_create_configuration(Resource *resource, uint32_t id)
|
||||
{
|
||||
wl_resource *config_resource = wl_resource_create(resource->client(), &kde_output_configuration_v2_interface, resource->version(), id);
|
||||
if (!config_resource) {
|
||||
wl_client_post_no_memory(resource->client());
|
||||
return;
|
||||
}
|
||||
new OutputConfigurationV2Interface(config_resource);
|
||||
}
|
||||
|
||||
OutputManagementV2Interface::OutputManagementV2Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new OutputManagementV2InterfacePrivate(display))
|
||||
{
|
||||
}
|
||||
|
||||
OutputManagementV2Interface::~OutputManagementV2Interface() = default;
|
||||
|
||||
OutputConfigurationV2Interface::OutputConfigurationV2Interface(wl_resource *resource)
|
||||
: QtWaylandServer::kde_output_configuration_v2(resource)
|
||||
{
|
||||
const auto reject = [this](Output *output) {
|
||||
invalid = true;
|
||||
};
|
||||
connect(workspace(), &Workspace::outputAdded, this, reject);
|
||||
connect(workspace(), &Workspace::outputRemoved, this, reject);
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_enable(Resource *resource, wl_resource *outputdevice, int32_t enable)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->enabled = enable;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_mode(Resource *resource, wl_resource *outputdevice, wl_resource *modeResource)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice);
|
||||
OutputDeviceModeV2Interface *mode = OutputDeviceModeV2Interface::get(modeResource);
|
||||
if (output && mode) {
|
||||
const auto change = config.changeSet(output->handle());
|
||||
const auto modePtr = mode->handle().lock();
|
||||
if (!modePtr) {
|
||||
invalid = true;
|
||||
return;
|
||||
}
|
||||
change->mode = modePtr;
|
||||
change->desiredModeSize = modePtr->size();
|
||||
change->desiredModeRefreshRate = modePtr->refreshRate();
|
||||
} else {
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_transform(Resource *resource, wl_resource *outputdevice, int32_t transform)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
auto toTransform = [transform]() {
|
||||
switch (transform) {
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
return OutputTransform::Rotate90;
|
||||
case WL_OUTPUT_TRANSFORM_180:
|
||||
return OutputTransform::Rotate180;
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
return OutputTransform::Rotate270;
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
return OutputTransform::FlipX;
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
return OutputTransform::FlipX90;
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
return OutputTransform::FlipX180;
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
return OutputTransform::FlipX270;
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
default:
|
||||
return OutputTransform::Normal;
|
||||
}
|
||||
};
|
||||
auto _transform = toTransform();
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
const auto changeset = config.changeSet(output->handle());
|
||||
changeset->transform = changeset->manualTransform = _transform;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_position(Resource *resource, wl_resource *outputdevice, int32_t x, int32_t y)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->pos = QPoint(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_scale(Resource *resource, wl_resource *outputdevice, wl_fixed_t scale)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
qreal doubleScale = wl_fixed_to_double(scale);
|
||||
|
||||
// the fractional scaling protocol only speaks in unit of 120ths
|
||||
// using the same scale throughout makes that simpler
|
||||
// this also eliminates most loss from wl_fixed
|
||||
doubleScale = std::round(doubleScale * 120) / 120;
|
||||
|
||||
if (doubleScale <= 0) {
|
||||
qCWarning(KWIN_CORE) << "Requested to scale output device to" << doubleScale << ", but I can't do that.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->scale = doubleScale;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_overscan(Resource *resource, wl_resource *outputdevice, uint32_t overscan)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (overscan > 100) {
|
||||
qCWarning(KWIN_CORE) << "Invalid overscan requested:" << overscan;
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->overscan = overscan;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_vrr_policy(Resource *resource, wl_resource *outputdevice, uint32_t policy)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (policy > static_cast<uint32_t>(VrrPolicy::Automatic)) {
|
||||
qCWarning(KWIN_CORE) << "Invalid Vrr Policy requested:" << policy;
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->vrrPolicy = static_cast<VrrPolicy>(policy);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (rgbRange > static_cast<uint32_t>(Output::RgbRange::Limited)) {
|
||||
qCWarning(KWIN_CORE) << "Invalid Rgb Range requested:" << rgbRange;
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->rgbRange = static_cast<Output::RgbRange>(rgbRange);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output)
|
||||
{
|
||||
// intentionally ignored
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *outputResource, uint32_t priority)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputResource)) {
|
||||
outputOrder.push_back(std::make_pair(priority, output));
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->highDynamicRange = enable_hdr == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->referenceLuminance = sdr_brightness;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->wideColorGamut = enable_wcg == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->autoRotationPolicy = static_cast<Output::AutoRotationPolicy>(auto_rotation_policy);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
const auto set = config.changeSet(output->handle());
|
||||
set->iccProfilePath = profile_path;
|
||||
if (auto ret = IccProfile::load(profile_path); ret.profile.has_value()) {
|
||||
set->iccProfile = std::move(ret.profile.value());
|
||||
} else {
|
||||
failureReason = ret.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_brightness_overrides(Resource *resource, wl_resource *outputdevice, int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional<double>(max_peak_brightness);
|
||||
config.changeSet(output->handle())->maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional<double>(max_average_brightness);
|
||||
config.changeSet(output->handle())->minBrightnessOverride = min_brightness == -1 ? std::nullopt : std::optional<double>(min_brightness / 10'000.0);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->sdrGamutWideness = gamut_wideness / 10'000.0;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_color_profile_source(Resource *resource, wl_resource *outputdevice, uint32_t source)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->colorProfileSource = [source]() {
|
||||
switch (source) {
|
||||
case color_profile_source_sRGB:
|
||||
return Output::ColorProfileSource::sRGB;
|
||||
case color_profile_source_ICC:
|
||||
return Output::ColorProfileSource::ICC;
|
||||
case color_profile_source_EDID:
|
||||
return Output::ColorProfileSource::EDID;
|
||||
};
|
||||
Q_UNREACHABLE();
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_brightness(Resource *resource, wl_resource *outputdevice, uint32_t brightness)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->brightness = brightness / 10'000.0;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_color_power_tradeoff(Resource *resource, wl_resource *outputdevice, uint32_t preference)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
const auto tradeoff = [preference]() -> std::optional<Output::ColorPowerTradeoff> {
|
||||
switch (preference) {
|
||||
case color_power_tradeoff_efficiency:
|
||||
return Output::ColorPowerTradeoff::PreferEfficiency;
|
||||
case color_power_tradeoff_accuracy:
|
||||
return Output::ColorPowerTradeoff::PreferAccuracy;
|
||||
}
|
||||
return std::nullopt;
|
||||
}();
|
||||
if (!tradeoff) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->colorPowerTradeoff = *tradeoff;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_dimming(Resource *resource, ::wl_resource *outputdevice, uint32_t multiplier)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->dimming = multiplier / 10'000.0;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::sendFailure(Resource *resource, const QString &reason)
|
||||
{
|
||||
if (resource->version() >= KDE_OUTPUT_CONFIGURATION_V2_FAILURE_REASON_SINCE_VERSION) {
|
||||
send_failure_reason(reason);
|
||||
}
|
||||
send_failed();
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_apply(Resource *resource)
|
||||
{
|
||||
if (applied) {
|
||||
wl_resource_post_error(resource->handle, error_already_applied, "an output configuration can be applied only once");
|
||||
return;
|
||||
}
|
||||
|
||||
applied = true;
|
||||
if (invalid) {
|
||||
sendFailure(resource, i18n("One of the relevant outputs is no longer available"));
|
||||
return;
|
||||
}
|
||||
if (!failureReason.isEmpty()) {
|
||||
sendFailure(resource, failureReason);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto allOutputs = kwinApp()->outputBackend()->outputs();
|
||||
const bool allDisabled = !std::any_of(allOutputs.begin(), allOutputs.end(), [this](const auto &output) {
|
||||
const auto changeset = config.constChangeSet(output);
|
||||
if (changeset && changeset->enabled.has_value()) {
|
||||
return *changeset->enabled;
|
||||
} else {
|
||||
return output->isEnabled();
|
||||
}
|
||||
});
|
||||
if (allDisabled) {
|
||||
sendFailure(resource, i18n("Disabling all outputs through configuration changes is not allowed"));
|
||||
return;
|
||||
}
|
||||
|
||||
std::optional<QList<Output *>> sortedOrder;
|
||||
if (!outputOrder.empty()) {
|
||||
const int desktopOutputs = std::count_if(allOutputs.begin(), allOutputs.end(), [](Output *output) {
|
||||
return !output->isNonDesktop();
|
||||
});
|
||||
if (outputOrder.size() != desktopOutputs) {
|
||||
sendFailure(resource, i18n("The provided output order doesn't contain all outputs"));
|
||||
return;
|
||||
}
|
||||
outputOrder.erase(std::remove_if(outputOrder.begin(), outputOrder.end(), [this](const auto &pair) {
|
||||
const auto changeset = config.constChangeSet(pair.second->handle());
|
||||
if (changeset && changeset->enabled.has_value()) {
|
||||
return !changeset->enabled.value();
|
||||
} else {
|
||||
return !pair.second->handle()->isEnabled();
|
||||
}
|
||||
}),
|
||||
outputOrder.end());
|
||||
std::sort(outputOrder.begin(), outputOrder.end(), [](const auto &pair1, const auto &pair2) {
|
||||
return pair1.first < pair2.first;
|
||||
});
|
||||
uint32_t i = 1;
|
||||
for (const auto &[index, name] : std::as_const(outputOrder)) {
|
||||
if (index != i) {
|
||||
sendFailure(resource, i18n("The provided output order is invalid"));
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
sortedOrder = QList<Output *>();
|
||||
sortedOrder->reserve(outputOrder.size());
|
||||
std::transform(outputOrder.begin(), outputOrder.end(), std::back_inserter(*sortedOrder), [](const auto &pair) {
|
||||
return pair.second->handle();
|
||||
});
|
||||
}
|
||||
if (workspace()->applyOutputConfiguration(config, sortedOrder)) {
|
||||
send_applied();
|
||||
} else {
|
||||
// TODO provide a more accurate error reason once the driver actually gives us anything
|
||||
sendFailure(resource, i18n("The driver rejected the output configuration"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#include "outputmanagement_v2.moc"
|
||||
|
||||
#include "moc_outputmanagement_v2.cpp"
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Sebastian Kügler <sebas@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class Display;
|
||||
class OutputManagementV2InterfacePrivate;
|
||||
|
||||
/**
|
||||
* @class OutputManagementInterface
|
||||
*
|
||||
* This class is used to change the configuration of the Wayland server's outputs.
|
||||
* The client requests an OutputConfiguration, changes its OutputDevices and then
|
||||
* calls OutputConfiguration::apply, which makes this class emit a signal, carrying
|
||||
* the new configuration.
|
||||
* The server is then expected to make the requested changes by applying the settings
|
||||
* of the OutputDevices to the Outputs.
|
||||
*
|
||||
* @see OutputConfiguration
|
||||
* @see OutputConfigurationInterface
|
||||
*/
|
||||
class KWIN_EXPORT OutputManagementV2Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit OutputManagementV2Interface(Display *display, QObject *parent = nullptr);
|
||||
~OutputManagementV2Interface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<OutputManagementV2InterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "plasmashell.h"
|
||||
#include "display.h"
|
||||
#include "surface.h"
|
||||
#include "utils/resource.h"
|
||||
|
||||
#include <qwayland-server-plasma-shell.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 8;
|
||||
static QList<PlasmaShellSurfaceInterface *> s_shellSurfaces;
|
||||
|
||||
struct PlasmaShellSurfaceCommit
|
||||
{
|
||||
std::optional<QPoint> globalPosition;
|
||||
};
|
||||
|
||||
class PlasmaShellInterfacePrivate : public QtWaylandServer::org_kde_plasma_shell
|
||||
{
|
||||
public:
|
||||
PlasmaShellInterfacePrivate(PlasmaShellInterface *q, Display *display);
|
||||
|
||||
private:
|
||||
void org_kde_plasma_shell_get_surface(Resource *resource, uint32_t id, struct ::wl_resource *surface) override;
|
||||
PlasmaShellInterface *q;
|
||||
};
|
||||
|
||||
PlasmaShellInterfacePrivate::PlasmaShellInterfacePrivate(PlasmaShellInterface *_q, Display *display)
|
||||
: QtWaylandServer::org_kde_plasma_shell(*display, s_version)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
class PlasmaShellSurfaceInterfacePrivate : public SurfaceExtension<PlasmaShellSurfaceCommit>, public QtWaylandServer::org_kde_plasma_surface
|
||||
{
|
||||
public:
|
||||
PlasmaShellSurfaceInterfacePrivate(PlasmaShellSurfaceInterface *q, SurfaceInterface *surface, wl_resource *resource);
|
||||
void apply(PlasmaShellSurfaceCommit *commit) override;
|
||||
|
||||
QPointer<SurfaceInterface> surface;
|
||||
PlasmaShellSurfaceInterface *q;
|
||||
|
||||
std::optional<QPoint> m_globalPos;
|
||||
PlasmaShellSurfaceInterface::Role m_role = PlasmaShellSurfaceInterface::Role::Normal;
|
||||
PlasmaShellSurfaceInterface::PanelBehavior m_panelBehavior = PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible;
|
||||
bool m_skipTaskbar = false;
|
||||
bool m_skipSwitcher = false;
|
||||
bool m_panelTakesFocus = false;
|
||||
bool m_openUnderCursorRequested = false;
|
||||
|
||||
private:
|
||||
void org_kde_plasma_surface_destroy_resource(Resource *resource) override;
|
||||
void org_kde_plasma_surface_destroy(Resource *resource) override;
|
||||
void org_kde_plasma_surface_set_output(Resource *resource, struct ::wl_resource *output) override;
|
||||
void org_kde_plasma_surface_set_position(Resource *resource, int32_t x, int32_t y) override;
|
||||
void org_kde_plasma_surface_set_role(Resource *resource, uint32_t role) override;
|
||||
void org_kde_plasma_surface_set_panel_behavior(Resource *resource, uint32_t flag) override;
|
||||
void org_kde_plasma_surface_set_skip_taskbar(Resource *resource, uint32_t skip) override;
|
||||
void org_kde_plasma_surface_panel_auto_hide_hide(Resource *resource) override;
|
||||
void org_kde_plasma_surface_panel_auto_hide_show(Resource *resource) override;
|
||||
void org_kde_plasma_surface_set_panel_takes_focus(Resource *resource, uint32_t takes_focus) override;
|
||||
void org_kde_plasma_surface_set_skip_switcher(Resource *resource, uint32_t skip) override;
|
||||
void org_kde_plasma_surface_open_under_cursor(Resource *resource) override;
|
||||
};
|
||||
|
||||
PlasmaShellInterface::PlasmaShellInterface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new PlasmaShellInterfacePrivate(this, display))
|
||||
{
|
||||
}
|
||||
|
||||
PlasmaShellInterface::~PlasmaShellInterface() = default;
|
||||
|
||||
void PlasmaShellInterfacePrivate::org_kde_plasma_shell_get_surface(QtWaylandServer::org_kde_plasma_shell::Resource *resource,
|
||||
uint32_t id,
|
||||
struct ::wl_resource *surface)
|
||||
{
|
||||
SurfaceInterface *s = SurfaceInterface::get(surface);
|
||||
if (!s) {
|
||||
wl_resource_post_error(resource->handle, 0, "Invalid surface");
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlasmaShellSurfaceInterface::get(s)) {
|
||||
wl_resource_post_error(resource->handle, 0, "org_kde_plasma_shell_surface already exists");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource *shell_resource = wl_resource_create(resource->client(), &org_kde_plasma_surface_interface, resource->version(), id);
|
||||
|
||||
auto shellSurface = new PlasmaShellSurfaceInterface(s, shell_resource);
|
||||
s_shellSurfaces.append(shellSurface);
|
||||
|
||||
QObject::connect(shellSurface, &QObject::destroyed, [shellSurface]() {
|
||||
s_shellSurfaces.removeOne(shellSurface);
|
||||
});
|
||||
|
||||
Q_EMIT q->surfaceCreated(shellSurface);
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* ShellSurfaceInterface
|
||||
*********************************/
|
||||
PlasmaShellSurfaceInterfacePrivate::PlasmaShellSurfaceInterfacePrivate(PlasmaShellSurfaceInterface *_q, SurfaceInterface *surface, wl_resource *resource)
|
||||
: SurfaceExtension<PlasmaShellSurfaceCommit>(surface)
|
||||
, QtWaylandServer::org_kde_plasma_surface(resource)
|
||||
, surface(surface)
|
||||
, q(_q)
|
||||
{
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::apply(PlasmaShellSurfaceCommit *commit)
|
||||
{
|
||||
if (commit->globalPosition.has_value() && commit->globalPosition != m_globalPos) {
|
||||
m_globalPos = commit->globalPosition;
|
||||
Q_EMIT q->positionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
PlasmaShellSurfaceInterface::PlasmaShellSurfaceInterface(SurfaceInterface *surface, wl_resource *resource)
|
||||
: d(new PlasmaShellSurfaceInterfacePrivate(this, surface, resource))
|
||||
{
|
||||
}
|
||||
|
||||
PlasmaShellSurfaceInterface::~PlasmaShellSurfaceInterface() = default;
|
||||
|
||||
SurfaceInterface *PlasmaShellSurfaceInterface::surface() const
|
||||
{
|
||||
return d->surface;
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete q;
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_output(Resource *resource, struct ::wl_resource *output)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_position(Resource *resource, int32_t x, int32_t y)
|
||||
{
|
||||
pending.globalPosition = QPoint(x, y);
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_open_under_cursor(Resource *resource)
|
||||
{
|
||||
if (surface && surface->buffer()) {
|
||||
wl_resource_post_error(resource->handle, -1, "open_under_cursor: surface has a buffer");
|
||||
}
|
||||
m_openUnderCursorRequested = true;
|
||||
Q_EMIT q->openUnderCursorRequested();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_role(Resource *resource, uint32_t role)
|
||||
{
|
||||
PlasmaShellSurfaceInterface::Role r = PlasmaShellSurfaceInterface::Role::Normal;
|
||||
switch (role) {
|
||||
case role_desktop:
|
||||
r = PlasmaShellSurfaceInterface::Role::Desktop;
|
||||
break;
|
||||
case role_panel:
|
||||
r = PlasmaShellSurfaceInterface::Role::Panel;
|
||||
break;
|
||||
case role_onscreendisplay:
|
||||
r = PlasmaShellSurfaceInterface::Role::OnScreenDisplay;
|
||||
break;
|
||||
case role_notification:
|
||||
r = PlasmaShellSurfaceInterface::Role::Notification;
|
||||
break;
|
||||
case role_tooltip:
|
||||
r = PlasmaShellSurfaceInterface::Role::ToolTip;
|
||||
break;
|
||||
case role_criticalnotification:
|
||||
r = PlasmaShellSurfaceInterface::Role::CriticalNotification;
|
||||
break;
|
||||
case role_appletpopup:
|
||||
r = PlasmaShellSurfaceInterface::Role::AppletPopup;
|
||||
break;
|
||||
case role_normal:
|
||||
default:
|
||||
r = PlasmaShellSurfaceInterface::Role::Normal;
|
||||
break;
|
||||
}
|
||||
if (r == m_role) {
|
||||
return;
|
||||
}
|
||||
m_role = r;
|
||||
Q_EMIT q->roleChanged();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_panel_behavior(Resource *resource, uint32_t flag)
|
||||
{
|
||||
PlasmaShellSurfaceInterface::PanelBehavior newBehavior = PlasmaShellSurfaceInterface::PanelBehavior::AlwaysVisible;
|
||||
switch (flag) {
|
||||
case panel_behavior_auto_hide:
|
||||
newBehavior = PlasmaShellSurfaceInterface::PanelBehavior::AutoHide;
|
||||
break;
|
||||
case panel_behavior_windows_can_cover:
|
||||
newBehavior = PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover;
|
||||
break;
|
||||
case panel_behavior_windows_go_below:
|
||||
newBehavior = PlasmaShellSurfaceInterface::PanelBehavior::WindowsGoBelow;
|
||||
break;
|
||||
case panel_behavior_always_visible:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (m_panelBehavior == newBehavior) {
|
||||
return;
|
||||
}
|
||||
m_panelBehavior = newBehavior;
|
||||
Q_EMIT q->panelBehaviorChanged();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_skip_taskbar(Resource *resource, uint32_t skip)
|
||||
{
|
||||
m_skipTaskbar = (bool)skip;
|
||||
Q_EMIT q->skipTaskbarChanged();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_skip_switcher(Resource *resource, uint32_t skip)
|
||||
{
|
||||
m_skipSwitcher = (bool)skip;
|
||||
Q_EMIT q->skipSwitcherChanged();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_panel_auto_hide_hide(Resource *resource)
|
||||
{
|
||||
if (m_role != PlasmaShellSurfaceInterface::Role::Panel
|
||||
|| (m_panelBehavior != PlasmaShellSurfaceInterface::PanelBehavior::AutoHide
|
||||
&& m_panelBehavior != PlasmaShellSurfaceInterface::PanelBehavior::WindowsCanCover)) {
|
||||
wl_resource_post_error(resource->handle, error_panel_not_auto_hide, "Not an auto hide panel");
|
||||
return;
|
||||
}
|
||||
Q_EMIT q->panelAutoHideHideRequested();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_panel_auto_hide_show(Resource *resource)
|
||||
{
|
||||
if (m_role != PlasmaShellSurfaceInterface::Role::Panel || m_panelBehavior != PlasmaShellSurfaceInterface::PanelBehavior::AutoHide) {
|
||||
wl_resource_post_error(resource->handle, error_panel_not_auto_hide, "Not an auto hide panel");
|
||||
return;
|
||||
}
|
||||
Q_EMIT q->panelAutoHideShowRequested();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterfacePrivate::org_kde_plasma_surface_set_panel_takes_focus(Resource *resource, uint32_t takesFocus)
|
||||
{
|
||||
if (m_panelTakesFocus == takesFocus) {
|
||||
return;
|
||||
}
|
||||
m_panelTakesFocus = takesFocus;
|
||||
Q_EMIT q->panelTakesFocusChanged();
|
||||
}
|
||||
|
||||
QPoint PlasmaShellSurfaceInterface::position() const
|
||||
{
|
||||
return d->m_globalPos.value_or(QPoint());
|
||||
}
|
||||
|
||||
PlasmaShellSurfaceInterface::Role PlasmaShellSurfaceInterface::role() const
|
||||
{
|
||||
return d->m_role;
|
||||
}
|
||||
|
||||
bool PlasmaShellSurfaceInterface::isPositionSet() const
|
||||
{
|
||||
return d->m_globalPos.has_value();
|
||||
}
|
||||
|
||||
bool PlasmaShellSurfaceInterface::wantsOpenUnderCursor() const
|
||||
{
|
||||
return d->m_openUnderCursorRequested;
|
||||
}
|
||||
|
||||
PlasmaShellSurfaceInterface::PanelBehavior PlasmaShellSurfaceInterface::panelBehavior() const
|
||||
{
|
||||
return d->m_panelBehavior;
|
||||
}
|
||||
|
||||
bool PlasmaShellSurfaceInterface::skipTaskbar() const
|
||||
{
|
||||
return d->m_skipTaskbar;
|
||||
}
|
||||
|
||||
bool PlasmaShellSurfaceInterface::skipSwitcher() const
|
||||
{
|
||||
return d->m_skipSwitcher;
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterface::hideAutoHidingPanel()
|
||||
{
|
||||
d->send_auto_hidden_panel_hidden();
|
||||
}
|
||||
|
||||
void PlasmaShellSurfaceInterface::showAutoHidingPanel()
|
||||
{
|
||||
d->send_auto_hidden_panel_shown();
|
||||
}
|
||||
|
||||
bool PlasmaShellSurfaceInterface::panelTakesFocus() const
|
||||
{
|
||||
return d->m_panelTakesFocus;
|
||||
}
|
||||
|
||||
PlasmaShellSurfaceInterface *PlasmaShellSurfaceInterface::get(wl_resource *native)
|
||||
{
|
||||
if (auto surfacePrivate = resource_cast<PlasmaShellSurfaceInterfacePrivate *>(native)) {
|
||||
return surfacePrivate->q;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PlasmaShellSurfaceInterface *PlasmaShellSurfaceInterface::get(SurfaceInterface *surface)
|
||||
{
|
||||
for (PlasmaShellSurfaceInterface *shellSurface : std::as_const(s_shellSurfaces)) {
|
||||
if (shellSurface->surface() == surface) {
|
||||
return shellSurface;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#include "moc_plasmashell.cpp"
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "kwin_export.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class QSize;
|
||||
struct wl_resource;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class Display;
|
||||
class SurfaceInterface;
|
||||
class PlasmaShellSurfaceInterface;
|
||||
|
||||
class PlasmaShellInterfacePrivate;
|
||||
class PlasmaShellSurfaceInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @brief Global for the org_kde_plasma_shell interface.
|
||||
*
|
||||
* The PlasmaShellInterface allows to add additional information to a SurfaceInterface.
|
||||
* It goes beyond what a ShellSurfaceInterface provides and is adjusted toward the needs
|
||||
* of the Plasma desktop.
|
||||
*
|
||||
* A server providing this interface should think about how to restrict access to it as
|
||||
* it allows to perform absolute window positioning.
|
||||
*/
|
||||
class KWIN_EXPORT PlasmaShellInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PlasmaShellInterface(Display *display, QObject *parent);
|
||||
virtual ~PlasmaShellInterface();
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted whenever a PlasmaShellSurfaceInterface got created.
|
||||
*/
|
||||
void surfaceCreated(KWin::PlasmaShellSurfaceInterface *);
|
||||
|
||||
private:
|
||||
std::unique_ptr<PlasmaShellInterfacePrivate> d;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Resource for the org_kde_plasma_shell_surface interface.
|
||||
*
|
||||
* PlasmaShellSurfaceInterface gets created by PlasmaShellInterface.
|
||||
*/
|
||||
class KWIN_EXPORT PlasmaShellSurfaceInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~PlasmaShellSurfaceInterface();
|
||||
|
||||
/**
|
||||
* @returns the SurfaceInterface this PlasmaShellSurfaceInterface got created for
|
||||
*/
|
||||
SurfaceInterface *surface() const;
|
||||
/**
|
||||
* @returns the requested position in global coordinates.
|
||||
*/
|
||||
QPoint position() const;
|
||||
/**
|
||||
* @returns Whether a global position has been requested.
|
||||
*/
|
||||
bool isPositionSet() const;
|
||||
|
||||
/**
|
||||
* @returns Whether the surface has requested to be opened under the cursor.
|
||||
*/
|
||||
bool wantsOpenUnderCursor() const;
|
||||
|
||||
/**
|
||||
* Describes possible roles this PlasmaShellSurfaceInterface can have.
|
||||
* The role can be used by the server to e.g. change the stacking order accordingly.
|
||||
*/
|
||||
enum class Role {
|
||||
Normal, ///< A normal surface
|
||||
Desktop, ///< The surface represents a desktop, normally stacked below all other surfaces
|
||||
Panel, ///< The surface represents a panel (dock), normally stacked above normal surfaces
|
||||
OnScreenDisplay, ///< The surface represents an on screen display, like a volume changed notification
|
||||
Notification, ///< The surface represents a notification
|
||||
ToolTip, ///< The surface represents a tooltip
|
||||
CriticalNotification, ///< The surface represents a critical notification, like battery is running out
|
||||
AppletPopup, ///< The surface represents an applet popup window
|
||||
};
|
||||
/**
|
||||
* @returns The requested role, default value is @c Role::Normal.
|
||||
*/
|
||||
Role role() const;
|
||||
/**
|
||||
* Describes how a PlasmaShellSurfaceInterface with role @c Role::Panel should behave.
|
||||
*
|
||||
* Deprecated
|
||||
*/
|
||||
enum class PanelBehavior {
|
||||
AlwaysVisible, ///< The panel should be always visible
|
||||
AutoHide, ///< The panel auto hides at a screen edge and returns on mouse press against edge
|
||||
WindowsCanCover, ///< Windows are allowed to go above the panel, it raises on mouse press against screen edge
|
||||
WindowsGoBelow, ///< Window are allowed to go below the panel
|
||||
};
|
||||
/**
|
||||
* @returns The PanelBehavior for a PlasmaShellSurfaceInterface with role @c Role::Panel
|
||||
* @see role
|
||||
*
|
||||
* Deprecated. This is now ignored
|
||||
*/
|
||||
PanelBehavior panelBehavior() const;
|
||||
|
||||
/**
|
||||
* @returns true if this window doesn't want to be listed
|
||||
* in the taskbar
|
||||
*/
|
||||
bool skipTaskbar() const;
|
||||
|
||||
/**
|
||||
* @returns true if this window doesn't want to be listed
|
||||
* in a window switcher
|
||||
*/
|
||||
bool skipSwitcher() const;
|
||||
|
||||
/**
|
||||
* Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got hidden.
|
||||
* Once it is shown again the method {@link showAutoHidingPanel} should be used.
|
||||
*
|
||||
* @see showAutoHidingPanel
|
||||
* @see panelAutoHideHideRequested
|
||||
* @see panelAutoHideShowRequested
|
||||
*/
|
||||
void hideAutoHidingPanel();
|
||||
|
||||
/**
|
||||
* Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got shown again.
|
||||
*
|
||||
* @see hideAutoHidingPanel
|
||||
* @see panelAutoHideHideRequested
|
||||
* @see panelAutoHideShowRequested
|
||||
* @see 5.28
|
||||
*/
|
||||
void showAutoHidingPanel();
|
||||
|
||||
/**
|
||||
* Whether a PlasmaShellSurfaceInterface wants to have focus.
|
||||
*
|
||||
* By default some PlasmaShell roles do not get focus, but the PlasmaShellSurfaceInterface can
|
||||
* request that it wants to have focus. The compositor can use this information to
|
||||
* pass focus to the surface.
|
||||
*/
|
||||
// TODO KF6 rename to something generic
|
||||
bool panelTakesFocus() const;
|
||||
|
||||
/**
|
||||
* @returns The PlasmaShellSurfaceInterface for the @p native resource.
|
||||
*/
|
||||
static PlasmaShellSurfaceInterface *get(wl_resource *native);
|
||||
static PlasmaShellSurfaceInterface *get(SurfaceInterface *surface);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* A change of global position has been requested.
|
||||
*/
|
||||
void positionChanged();
|
||||
|
||||
/**
|
||||
* The surface has requested to be initially shown under the cursor. Can only occur
|
||||
* before any buffer has been attached.
|
||||
*/
|
||||
void openUnderCursorRequested();
|
||||
/**
|
||||
* A change of the role has been requested.
|
||||
*/
|
||||
void roleChanged();
|
||||
/**
|
||||
* A change of the panel behavior has been requested.
|
||||
*/
|
||||
void panelBehaviorChanged();
|
||||
/**
|
||||
* A change in the skip taskbar property has been requested
|
||||
*/
|
||||
void skipTaskbarChanged();
|
||||
/**
|
||||
* A change in the skip switcher property has been requested
|
||||
*/
|
||||
void skipSwitcherChanged();
|
||||
|
||||
/**
|
||||
* A surface with Role Panel and PanelBehavior AutoHide requested to be hidden.
|
||||
*
|
||||
* The compositor should inform the PlasmaShellSurfaceInterface about the actual change.
|
||||
* Once the surface is hidden it should invoke {@link hideAutoHidingPanel}. If the compositor
|
||||
* cannot hide the surface (e.g. because it doesn't border a screen edge) it should inform
|
||||
* the surface through invoking {@link showAutoHidingPanel}. This method should also be invoked
|
||||
* whenever the surface gets shown again due to triggering the screen edge.
|
||||
*
|
||||
* @see hideAutoHidingPanel
|
||||
* @see showAutoHidingPanel
|
||||
* @see panelAutoHideShowRequested
|
||||
*/
|
||||
void panelAutoHideHideRequested();
|
||||
|
||||
/**
|
||||
* A surface with Role Panel and PanelBehavior AutoHide requested to be shown.
|
||||
*
|
||||
* The compositor should inform the PlasmaShellSurfaceInterface about the actual change.
|
||||
* Once the surface is shown it should invoke {@link showAutoHidingPanel}.
|
||||
*
|
||||
* @see hideAutoHidingPanel
|
||||
* @see showAutoHidingPanel
|
||||
* @see panelAutoHideHideRequested
|
||||
*/
|
||||
void panelAutoHideShowRequested();
|
||||
|
||||
/*
|
||||
* Emitted when panelTakesFocus changes
|
||||
* @see panelTakesFocus
|
||||
*/
|
||||
void panelTakesFocusChanged();
|
||||
|
||||
private:
|
||||
friend class PlasmaShellInterfacePrivate;
|
||||
explicit PlasmaShellSurfaceInterface(SurfaceInterface *surface, wl_resource *resource);
|
||||
std::unique_ptr<PlasmaShellSurfaceInterfacePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::PlasmaShellSurfaceInterface::Role)
|
||||
Q_DECLARE_METATYPE(KWin::PlasmaShellSurfaceInterface::PanelBehavior)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user