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:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -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