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,194 @@
configure_file(config-kwindowsystem.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kwindowsystem.h )
add_library(KF6WindowSystem)
add_library(KF6::WindowSystem ALIAS KF6WindowSystem)
qt_extract_metatypes(KF6WindowSystem)
set_target_properties(KF6WindowSystem PROPERTIES
VERSION ${KWINDOWSYSTEM_VERSION}
SOVERSION ${KWINDOWSYSTEM_SOVERSION}
EXPORT_NAME WindowSystem
)
ecm_create_qm_loader(KF6WindowSystem kwindowsystem6_qt)
target_sources(KF6WindowSystem PRIVATE
kwindoweffects.cpp
kwindoweffects_dummy.cpp
kwindowshadow.cpp
kwindowsystem.cpp
pluginwrapper.cpp
kwindowsystemplugininterface.cpp
)
ecm_qt_declare_logging_category(KF6WindowSystem
HEADER kwindowsystem_debug.h
IDENTIFIER LOG_KWINDOWSYSTEM
CATEGORY_NAME kf.windowsystem
OLD_CATEGORY_NAMES org.kde.kwindowsystem
DEFAULT_SEVERITY Warning
DESCRIPTION "KWindowSystem"
EXPORT KWINDOWSYSTEM
)
if (KWINDOWSYSTEM_X11)
target_include_directories(KF6WindowSystem
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/platforms/xcb
)
target_link_libraries(KF6WindowSystem
PUBLIC
# public because they are used in kkeyserver_x11.h
X11::X11
PRIVATE
XCB::XCB
XCB::RES
X11::Xfixes
XCB::KEYSYMS
Qt6::GuiPrivate # qtx11extras_p.h
)
ecm_qt_declare_logging_category(KF6WindowSystem
HEADER kwindowsystem_xcb_debug.h
IDENTIFIER LOG_KKEYSERVER_X11
CATEGORY_NAME kf.windowsystem.keyserver.x11
DEFAULT_SEVERITY Warning
)
target_sources(KF6WindowSystem PRIVATE
platforms/xcb/kselectionowner.cpp
platforms/xcb/kselectionwatcher.cpp
platforms/xcb/kxmessages.cpp
platforms/xcb/kxutils.cpp
platforms/xcb/netwm.cpp
kkeyserver.cpp
kx11extras.cpp
kstartupinfo.cpp
kusertimestamp.cpp
kxerrorhandler.cpp
kwindowinfo.cpp
)
# we install kkeyserver_x11.h which needs the X11 headers available
# if we don't add the include path here code that includes kkeyserver.h will fail
# to compile unless X11 is installed in /usr/include
target_include_directories(KF6WindowSystem PUBLIC ${XCB_XCB_INCLUDE_DIR})
endif ()
if (KWINDOWSYSTEM_WAYLAND)
target_sources(KF6WindowSystem PRIVATE kwaylandextras.cpp)
endif()
ecm_generate_export_header(KF6WindowSystem
BASE_NAME KWindowSystem
GROUP_BASE_NAME KF
VERSION ${KF_VERSION}
USE_VERSION_HEADER
DEPRECATED_BASE_VERSION 0
DEPRECATION_VERSIONS 6.0
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
)
target_include_directories(KF6WindowSystem INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KWindowSystem>")
target_link_libraries(KF6WindowSystem
PUBLIC Qt6::Gui
)
ecm_generate_headers(KWindowSystem_HEADERS
HEADER_NAMES
KWindowEffects
KWindowShadow,KWindowShadowTile
KWindowSystem
REQUIRED_HEADERS KWindowSystem_HEADERS
)
if (KWINDOWSYSTEM_X11)
ecm_generate_headers(KWindowSystem_HEADERS
HEADER_NAMES
KX11Extras
KStartupInfo
KUserTimestamp
KKeyServer
KWindowInfo
REQUIRED_HEADERS KWindowSystem_HEADERS
)
endif()
if (KWINDOWSYSTEM_WAYLAND)
ecm_generate_headers(KWindowSystem_HEADERS
HEADER_NAMES
KWaylandExtras
REQUIRED_HEADERS KWindowSystem_HEADERS
)
endif()
install(TARGETS KF6WindowSystem EXPORT KF6WindowSystemTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES
# FIXME: It seems odd to install this.
${CMAKE_CURRENT_BINARY_DIR}/config-kwindowsystem.h
${CMAKE_CURRENT_BINARY_DIR}/kwindowsystem_export.h
${KWindowSystem_HEADERS}
netwm_def.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KWindowSystem COMPONENT Devel
)
install(
FILES
kwindoweffects_p.h
kwindowshadow_p.h
kwindowsystem_p.h
kwindowsystemplugininterface_p.h
DESTINATION
${KDE_INSTALL_INCLUDEDIR_KF}/KWindowSystem/private
COMPONENT
Devel
)
if(BUILD_QCH)
ecm_add_qch(
KF6WindowSystem_QCH
NAME KWindowSystem
BASE_NAME KF6WindowSystem
VERSION ${KF_VERSION}
ORG_DOMAIN org.kde
SOURCE_DIRS
# using dir for now, to cover any platform dependent code
# TODO: should only use public headers, to cover only public API
${CMAKE_CURRENT_SOURCE_DIR}
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
LINK_QCHS
Qt6Gui_QCH
INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
BLANK_MACROS
KWINDOWSYSTEM_EXPORT
KWINDOWSYSTEM_DEPRECATED
KWINDOWSYSTEM_DEPRECATED_EXPORT
"KWINDOWSYSTEM_DEPRECATED_VERSION(x, y, t)"
"KWINDOWSYSTEM_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
"KWINDOWSYSTEM_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
"KWINDOWSYSTEM_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
COMPONENT Devel
)
endif()
add_subdirectory(platforms)
if(KWINDOWSYSTEM_QML)
add_subdirectory(qml)
endif()
ecm_qt_install_logging_categories(
EXPORT KWINDOWSYSTEM
FILE kwindowsystem.categories
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
)
@@ -0,0 +1,6 @@
#!/bin/sh
# Extract strings from all source files.
# EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with
# lconvert.
$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/kwindowsystem6_qt.pot
@@ -0,0 +1,4 @@
/* config-kwindowsystem.h. Generated by cmake from config-kwindowsystem.h.cmake */
/* Define to 1 if you have the Xlib */
#cmakedefine01 KWINDOWSYSTEM_HAVE_X11
@@ -0,0 +1,18 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#pragma once
#include <memory>
struct CDeleter {
template<typename T>
void operator()(T *ptr)
{
free(ptr);
}
};
template<typename T>
using UniqueCPointer = std::unique_ptr<T, CDeleter>;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,236 @@
/*
SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
Win32 port:
SPDX-FileCopyrightText: 2004 Jarosław Staniek <staniek@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef _KKEYSERVER_H
#define _KKEYSERVER_H
#include <kwindowsystem_export.h>
#include <X11/Xlib.h>
#include <fixx11h.h>
#include <qglobal.h>
#include <xcb/xcb.h>
class QString;
/**
* A collection of functions for the conversion of key presses and
* their modifiers from the window system specific format
* to the generic format and vice-versa.
*/
namespace KKeyServer
{
/**
* Converts the mask of ORed KKey::ModFlag modifiers to a
* user-readable string.
* @param mod the mask of ORed KKey::ModFlag modifiers
* @return the user-readable string (in English)
*/
KWINDOWSYSTEM_EXPORT QString modToStringUser(uint mod);
/**
* Converts the modifier given as user-readable string (in English)
* to KKey::ModFlag modifier, or 0.
* @internal
*/
KWINDOWSYSTEM_EXPORT uint stringUserToMod(const QString &mod);
/**
* Test if the shift modifier should be recorded for a given key.
*
* For example, if shift+5 produces '%' Qt wants ctrl+shift+5 recorded as ctrl+% and
* in that case this function would return false.
*
* @since 4.7.1
*/
KWINDOWSYSTEM_EXPORT bool isShiftAsModifierAllowed(int keyQt);
static const int MODE_SWITCH = 0x2000;
/**
* Initialises the values to return for the mod*() functions below.
* Called automatically by those functions if not already initialized.
*/
KWINDOWSYSTEM_EXPORT bool initializeMods();
/**
* Returns true if the current keyboard layout supports the Meta key.
* Specifically, whether the Super or Meta keys are assigned to an X modifier.
* @return true if the keyboard has a Meta key
* @see modXMeta()
*/
KWINDOWSYSTEM_EXPORT bool keyboardHasMetaKey();
/**
* Returns the X11 Shift modifier mask/flag.
* @return the X11 Shift modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXShift();
/**
* Returns the X11 Lock modifier mask/flag.
* @return the X11 Lock modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXLock();
/**
* Returns the X11 Ctrl modifier mask/flag.
* @return the X11 Ctrl modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXCtrl();
/**
* Returns the X11 Alt (Mod1) modifier mask/flag.
* @return the X11 Alt (Mod1) modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXAlt();
/**
* Returns the X11 Win (Mod3) modifier mask/flag.
* @return the X11 Win (Mod3) modifier mask/flag.
* @see keyboardHasWinKey()
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXMeta();
/**
* Returns the X11 NumLock modifier mask/flag.
* @return the X11 NumLock modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXNumLock();
/**
* Returns the X11 ScrollLock modifier mask/flag.
* @return the X11 ScrollLock modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXScrollLock();
/**
* Returns the X11 Mode_switch modifier mask/flag.
* @return the X11 Mode_switch modifier mask/flag.
* @see accelModMaskX()
*/
KWINDOWSYSTEM_EXPORT uint modXModeSwitch();
/**
* Returns bitwise OR'ed mask containing Shift, Ctrl, Alt, and
* Win (if available).
* @see modXShift()
* @see modXLock()
* @see modXCtrl()
* @see modXAlt()
* @see modXNumLock()
* @see modXWin()
* @see modXScrollLock()
*/
KWINDOWSYSTEM_EXPORT uint accelModMaskX();
#if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(6, 0)
/**
* Extracts the symbol from the given Qt key and
* converts it to an X11 symbol + modifiers.
* @param keyQt the qt key code
* @param sym if successful, the symbol will be written here
* @return true if successful, false otherwise
*
* @deprecated Since 6.0, Use keyQtToSymXs(keyQt)
*/
KWINDOWSYSTEM_EXPORT
KWINDOWSYSTEM_DEPRECATED_VERSION(6, 0, "Use keyQtToSymXs(int keyQt)")
bool keyQtToSymX(int keyQt, int *sym);
#endif
/**
* Extracts the symbols from the given Qt key and
* converts it to an X11 symbol + modifiers.
* @param keyQt the qt key code
* @return the symbols; emtpy if unsuccessful
*/
KWINDOWSYSTEM_EXPORT QList<int> keyQtToSymXs(int keyQt);
#if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(6, 0)
/**
* Extracts the code from the given Qt key.
* @param keyQt the qt key code
* @param keyCode if successful, the symbol will be written here
* @return true if successful, false otherwise
*
* @deprecated Since 6.0, Use keyQtToCodeXs(keyQt)
*/
KWINDOWSYSTEM_EXPORT
KWINDOWSYSTEM_DEPRECATED_VERSION(6, 0, "Use keyQtToCodeXs(int keyQt)")
bool keyQtToCodeX(int keyQt, int *keyCode);
#endif
/**
* Extracts the codes from the given Qt key.
* @param keyQt the qt key code
* @param return the codes; empty if unsuccessful
*/
KWINDOWSYSTEM_EXPORT QList<int> keyQtToCodeXs(int keyQt);
/**
* Extracts the modifiers from the given Qt key and
* converts them in a mask of X11 modifiers.
* @param keyQt the qt key code
* @param mod if successful, the modifiers will be written here
* @return true if successful, false otherwise
*/
KWINDOWSYSTEM_EXPORT bool keyQtToModX(int keyQt, uint *mod);
/**
* Converts the given symbol and modifier combination to a Qt key code.
* @param keySym the X key symbol
* @param modX the mask of X11 modifiers
* @param keyQt if successful, the qt key code will be written here
* @return true if successful, false otherwise
*/
KWINDOWSYSTEM_EXPORT bool symXModXToKeyQt(uint32_t keySym, uint16_t modX, int *keyQt);
/**
* Converts the mask of ORed X11 modifiers to
* a mask of ORed Qt key code modifiers.
* @param modX the mask of X11 modifiers
* @param modQt the mask of Qt key code modifiers will be written here
* if successful
* @return true if successful, false otherwise
*/
KWINDOWSYSTEM_EXPORT bool modXToQt(uint modX, int *modQt);
/**
* Converts an X keypress event into a Qt key + modifier code
* @param e the X11 keypress event
* @param keyModQt the Qt keycode and mask of Qt key code modifiers will be written here
* if successful
* @return true if successful, false otherwise
*/
KWINDOWSYSTEM_EXPORT bool xEventToQt(XEvent *e, int *keyModQt);
/**
* Converts an XCB keypress event into a Qt key + modifier code
* @param e the XCB keypress event
* @param keyModQt the Qt keycode and mask of Qt key code modifiers will be written here
* if successful
* @return true if successful, false otherwise
*/
KWINDOWSYSTEM_EXPORT bool xcbKeyPressEventToQt(xcb_generic_event_t *e, int *keyModQt);
/**
* Overloaded method for convenience.
*/
KWINDOWSYSTEM_EXPORT bool xcbKeyPressEventToQt(xcb_key_press_event_t *e, int *keyModQt);
}; // namespace KKeyServer
#endif // !_KKEYSERVER_H
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,612 @@
/*
SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef KSTARTUPINFO_H
#define KSTARTUPINFO_H
#include <kwindowsystem_export.h>
#include <QChildEvent>
#include <QObject>
#include <QString>
#include <QWidgetList> // for WId
#include <QWindow>
#include <sys/types.h>
typedef struct _XDisplay Display;
struct xcb_connection_t;
class KStartupInfoId;
class KStartupInfoData;
/**
* Class for manipulating the application startup notification.
*
* This class can be used to send information about started application,
* change the information and receive this information. For detailed
* description, see kdelibs/kdecore/README.kstartupinfo.
*
* You usually don't need to use this class for sending the notification
* information, as KDE libraries should do this when an application is
* started (e.g. KRun class).
*
* For receiving the startup notification info, create an instance and connect
* to its slots. It will automatically detect started applications and when
* they are ready.
*
* @see KStartupInfoId
* @see KStartupInfoData
*
* @author Lubos Lunak <l.lunak@kde.org>
*/
class KWINDOWSYSTEM_EXPORT KStartupInfo : public QObject
{
Q_OBJECT
public:
/**
* Manual notification that the application has started.
* If you do not map a (toplevel) window, then startup
* notification will not disappear for the application
* until a timeout. You can use this as an alternative
* method in this case.
*/
static void appStarted();
/**
* Sends explicit notification that the startup notification
* with id startup_id should end.
*/
static void appStarted(const QByteArray &startup_id);
/**
* Sets a new value for the application startup notification window property for newly
* created toplevel windows.
* @param startup_id the startup notification identifier
* @see KStartupInfo::setNewStartupId
*/
static void setStartupId(const QByteArray &startup_id);
/**
* Use this function if the application got a request with startup
* notification from outside (for example, when KUniqueApplication::newInstance()
* is called, or e.g.\ when khelpcenter opens new URL in its window).
* The window can be either an already existing and visible window,
* or a new one, before being shown. Note that this function is usually
* needed only when a window is reused.
*/
static void setNewStartupId(QWindow *window, const QByteArray &startup_id);
/**
* Creates and returns new startup id. The id includes properly setup
* user timestamp.
*
* On the X11 platform the current timestamp will be fetched from the
* X-Server. If the caller has an adaquat timestamp (e.g. from a QMouseEvent)
* it should prefer using createNewStartupIdForTimestamp to not trigger a
* roundtrip to the X-Server
*
* @see createNewStartupIdForTimestamp
*/
static QByteArray createNewStartupId();
/**
* Creates and returns new startup id with @p timestamp as user timestamp part.
*
* @param timestamp The timestamp for the startup id.
* @see createNewStartupId
* @since 5.5
**/
static QByteArray createNewStartupIdForTimestamp(quint32 timestamp);
/**
*
*/
enum {
CleanOnCantDetect = 1 << 0,
DisableKWinModule = 1 << 1,
AnnounceSilenceChanges = 1 << 2,
};
/**
* Creates an instance that will receive the startup notifications.
* The various flags passed may be
* @li CleanOnCantDetect - when a new unknown window appears, all startup
* notifications for applications that are not compliant with
* the startup protocol are removed
* @li DisableKWinModule - KWinModule, which is normally used to detect
* new windows, is disabled. With this flag, checkStartup() must be
* called in order to check newly mapped windows.
* @li AnnounceSilenceChanges - normally, startup notifications are
* "removed" when they're silenced, and "recreated" when they're resumed.
* With this flag, the change is normally announced with gotStartupChange().
*
* @param flags OR-ed combination of flags
* @param parent the parent of this QObject (can be @c nullptr for no parent)
*
*/
explicit KStartupInfo(int flags, QObject *parent = nullptr);
~KStartupInfo() override;
/**
* Sends given notification data about started application
* with the given startup identification. If no notification for this identification
* exists yet, it is created, otherwise it's updated. Note that the name field
* in data is required.
*
* @param id the id of the application
* @param data the application's data
* @return true if successful, false otherwise
* @see KStartupInfoId
* @see KStartupInfoData
*/
static bool sendStartup(const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Like sendStartup , uses @p conn instead of QX11Info::connection() for sending the info.
* @param conn the xcb connection of the application. Note that the name field
* in data is required.
* @param screen The x11 screen the connection belongs to
* @param id the id of the application
* @param data the application's data
* @return true if successful, false otherwise
* @since 5.18
*/
static bool sendStartupXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Sends given notification data about started application
* with the given startup identification. This is used for updating the notification
* info, if no notification for this identification exists, it's ignored.
* @param id the id of the application
* @param data the application's data
* @return true if successful, false otherwise
* @see KStartupInfoId
* @see KStartupInfoData
*/
static bool sendChange(const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Like sendChange , uses @p conn instead of QX11Info::connection() for sending the info.
* @param conn the xcb connection of the application.
* @param screen The x11 screen the connection belongs to
* @param id the id of the application
* @param data the application's data
* @return true if successful, false otherwise
* @since 5.18
*/
static bool sendChangeXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Ends startup notification with the given identification.
* @param id the id of the application
* @return true if successful, false otherwise
*/
static bool sendFinish(const KStartupInfoId &id);
/**
* Like sendFinish , uses @p conn instead of QX11Info::connection() for sending the info.
* @param conn the xcb connection of the application.
* @param screen The x11 screen the connection belongs to
* @param id the id of the application
* @return true if successful, false otherwise
* @since 5.18
*/
static bool sendFinishXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id);
/**
* Ends startup notification with the given identification and the given data
* (e.g.\ PIDs of processes for this startup notification that exited).
* @param id the id of the application
* @param data the application's data
* @return true if successful, false otherwise
*/
static bool sendFinish(const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Like sendFinish , uses @p conn instead of QX11Info::connection() for sending the info.
* @param conn the xcb connection of the application.
* @param screen The x11 screen the connection belongs to
* @param id the id of the application
* @param data the application's data
* @return true if successful, false otherwise
* @since 5.18
*/
static bool sendFinishXcb(xcb_connection_t *conn, int screen, const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Unsets the startup notification environment variable.
*/
static void resetStartupEnv();
/**
* @li NoMatch - the window doesn't match any existing startup notification
* @li Match - the window matches an existing startup notification
* @li CantDetect - unable to detect if the window matches any existing
* startup notification
*/
enum startup_t { NoMatch, Match, CantDetect };
/**
* Checks if the given windows matches any existing startup notification.
* @param w the window id to check
* @return the result of the operation
*/
startup_t checkStartup(WId w);
/**
* Checks if the given windows matches any existing startup notification, and
* if yes, returns the identification in id.
* @param w the window id to check
* @param id if found, the id of the startup notification will be written here
* @return the result of the operation
*/
startup_t checkStartup(WId w, KStartupInfoId &id);
/**
* Checks if the given windows matches any existing startup notification, and
* if yes, returns the notification data in data.
* @param w the window id to check
* @param data if found, the data of the startup notification will be written here
* @return the result of the operation
*/
startup_t checkStartup(WId w, KStartupInfoData &data);
/**
* Checks if the given windows matches any existing startup notification, and
* if yes, returns the identification in id and notification data in data.
* @param w the window id to check
* @param id if found, the id of the startup notification will be written here
* @param data if found, the data of the startup notification will be written here
* @return the result of the operation
*/
startup_t checkStartup(WId w, KStartupInfoId &id, KStartupInfoData &data);
/**
* Sets the timeout for notifications, after this timeout a notification is removed.
* @param secs the new timeout in seconds
*/
void setTimeout(unsigned int secs);
/**
* Returns startup notification identification of the given window.
* @param w the id of the window
* @return the startup notification id. Can be null if not found.
*/
static QByteArray windowStartupId(WId w);
/**
* @internal
*/
class Data;
/**
* @internal
*/
class Private;
Q_SIGNALS:
/**
* Emitted when a new startup notification is created (i.e.\ a new application is
* being started).
* @param id the notification identification
* @param data the notification data
*/
void gotNewStartup(const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Emitted when a startup notification changes.
* @param id the notification identification
* @param data the notification data
*/
void gotStartupChange(const KStartupInfoId &id, const KStartupInfoData &data);
/**
* Emitted when a startup notification is removed (either because it was detected
* that the application is ready or because of a timeout).
* @param id the notification identification
* @param data the notification data
*/
void gotRemoveStartup(const KStartupInfoId &id, const KStartupInfoData &data);
protected:
/**
*
*/
void customEvent(QEvent *e_P) override;
private:
Q_PRIVATE_SLOT(d, void startups_cleanup())
Q_PRIVATE_SLOT(d, void startups_cleanup_no_age())
Q_PRIVATE_SLOT(d, void got_message(const QString &msg))
Q_PRIVATE_SLOT(d, void window_added(WId w))
Q_PRIVATE_SLOT(d, void slot_window_added(WId w))
Private *const d;
Q_DISABLE_COPY(KStartupInfo)
};
/**
* Class representing an identification of application startup notification.
*
* Every existing notification about a starting application has its own unique
* identification, that's used to identify and manipulate the notification.
*
* @see KStartupInfo
* @see KStartupInfoData
*
* @author Lubos Lunak <l.lunak@kde.org>
*/
class KWINDOWSYSTEM_EXPORT KStartupInfoId
{
public:
/**
* Overloaded operator.
* @return true if the notification identifications are the same
*/
bool operator==(const KStartupInfoId &id) const;
/**
* Overloaded operator.
* @return true if the notification identifications are different
*/
bool operator!=(const KStartupInfoId &id) const;
/**
* Checks whether the identifier is valid.
* @return true if this object doesn't represent a valid notification identification
*/
bool isNull() const;
/**
* Initializes this object with the given identification ( which may be also "0"
* for no notification ), or if "" is given, tries to read it from the startup
* notification environment variable, and if it's not set, creates a new one.
* @param id the new identification, "0" for no notification or "" to read
* the environment variable
*/
void initId(const QByteArray &id = "");
/**
* Returns the notification identifier as string.
* @return the identification string for the notification
*/
const QByteArray &id() const;
/**
* Return the user timestamp for the startup notification, or 0 if no timestamp
* is set.
*/
unsigned long timestamp() const;
/**
* Sets the startup notification environment variable to this identification.
* @return true if successful, false otherwise
*/
bool setupStartupEnv() const;
/**
* Creates an empty identification
*/
KStartupInfoId();
/**
* Copy constructor.
*/
KStartupInfoId(const KStartupInfoId &data);
~KStartupInfoId();
KStartupInfoId &operator=(const KStartupInfoId &data);
bool operator<(const KStartupInfoId &id) const;
private:
explicit KStartupInfoId(const QString &txt);
friend class KStartupInfo;
friend class KStartupInfo::Private;
struct Private;
Private *const d;
};
/**
* Class representing data about an application startup notification.
*
* Such data include the icon of the starting application, the desktop on which
* the application should start, the binary name of the application, etc.
*
* @see KStartupInfo
* @see KStartupInfoId
*
* @author Lubos Lunak <l.lunak@kde.org>
*/
class KWINDOWSYSTEM_EXPORT KStartupInfoData
{
public:
/**
* Sets the binary name of the application (e.g.\ 'kcontrol').
* @param bin the new binary name of the application
*/
void setBin(const QString &bin);
/**
* Returns the binary name of the starting application
* @return the new binary name of the application
*/
const QString &bin() const;
/**
* Sets the name for the notification (e.g.\ 'Control Center').
*/
void setName(const QString &name);
/**
* Returns the name of the startup notification. If it's not available,
* it tries to use other information (binary name).
* @return the name of the startup notification
*/
const QString &findName() const;
/**
* Returns the name of the startup notification, or empty if not available.
* @return the name of the startup notification, or an empty string
* if not set.
*/
const QString &name() const;
/**
* Sets the description for the notification (e.g.\ 'Launching Control Center').
* I.e. name() describes what is being started, while description() is
* the actual action performed by the starting.
*/
void setDescription(const QString &descr);
/**
* Returns the description of the startup notification. If it's not available,
* it returns name().
* @return the description of the startup notification
*/
const QString &findDescription() const;
/**
* Returns the name of the startup notification, or empty if not available.
* @return the name of the startup notification, or an empty string
* if not set.
*/
const QString &description() const;
/**
* Sets the icon for the startup notification (e.g.\ 'kcontrol').
* @param icon the name of the icon
*/
void setIcon(const QString &icon);
/**
* Returns the icon of the startup notification, and if it's not available,
* tries to get it from the binary name.
* @return the name of the startup notification's icon, or the name of
* the binary if not set
*/
const QString &findIcon() const;
/**
* Returns the icon of the startup notification, or empty if not available.
* @return the name of the icon, or an empty string if not set.
*/
const QString &icon() const;
/**
* Sets the desktop for the startup notification (i.e.\ the desktop on which
* the starting application should appear ).
* @param desktop the desktop for the startup notification
*/
void setDesktop(int desktop);
/**
* Returns the desktop for the startup notification.
* @return the desktop for the startup notification
*/
int desktop() const;
/**
* Sets a WM_CLASS value for the startup notification, it may be used for increasing
* the chance that the windows created by the starting application will be
* detected correctly.
* @param wmclass the WM_CLASS value for the startup notification
*/
void setWMClass(const QByteArray &wmclass);
/**
* Returns the WM_CLASS value for the startup notification, or binary name if not
* available.
* @return the WM_CLASS value for the startup notification, or the binary name
* if not set
*/
const QByteArray findWMClass() const;
/**
* Returns the WM_CLASS value for the startup notification, or empty if not available.
* @return the WM_CLASS value for the startup notification, or empty
* if not set
*/
QByteArray WMClass() const;
/**
* Adds a PID to the list of processes that belong to the startup notification. It
* may be used to increase the chance that the windows created by the starting
* application will be detected correctly, and also for detecting if the application
* has quit without creating any window.
* @param pid the PID to add
*/
void addPid(pid_t pid);
/**
* Returns all PIDs for the startup notification.
* @return the list of all PIDs
*/
QList<pid_t> pids() const;
/**
* Checks whether the given @p pid is in the list of PIDs for startup
* notification.
* @return true if the given @p pid is in the list of PIDs for the startup notification
*/
bool is_pid(pid_t pid) const;
/**
* Sets the hostname on which the application is starting. It's necessary to set
* it if PIDs are set.
* @param hostname the application's hostname. If it's a null string, the current hostname is used
*/
void setHostname(const QByteArray &hostname = QByteArray());
/**
* Returns the hostname for the startup notification.
* @return the hostname
*/
QByteArray hostname() const;
/**
*
*/
enum TriState { Yes, No, Unknown };
/**
* Sets whether the visual feedback for this startup notification
* should be silenced (temporarily suspended).
*/
void setSilent(TriState state);
/**
* Return the silence status for the startup notification.
* @return KStartupInfoData::Yes if visual feedback is silenced
*/
TriState silent() const;
/**
* The X11 screen on which the startup notification is happening, -1 if unknown.
*/
int screen() const;
/**
* Sets the X11 screen on which the startup notification should happen.
* This is usually not necessary to set, as it's set by default to QX11Info::screen().
*/
void setScreen(int screen);
/**
* The Xinerama screen for the startup notification, -1 if unknown.
*/
int xinerama() const;
/**
* Sets the Xinerama screen for the startup notification ( i.e. the screeen on which
* the starting application should appear ).
* @param xinerama the Xinerama screen for the startup notification
*/
void setXinerama(int xinerama);
/**
* The .desktop file used to initiate this startup notification, or empty. This information
* should be used only to identify the application, not to read any additional information.
* @since 4.5
**/
QString applicationId() const;
/**
* Sets the .desktop file that was used to initiate the startup notification.
* @since 4.5
*/
void setApplicationId(const QString &desktop);
/**
* Updates the notification data from the given data. Some data, such as the desktop
* or the name, won't be rewritten if already set.
* @param data the data to update
*/
void update(const KStartupInfoData &data);
/**
* Constructor. Initializes all the data to their default empty values.
*/
KStartupInfoData();
/**
* Copy constructor.
*/
KStartupInfoData(const KStartupInfoData &data);
~KStartupInfoData();
KStartupInfoData &operator=(const KStartupInfoData &data);
private:
explicit KStartupInfoData(const QString &txt);
friend class KStartupInfo;
friend class KStartupInfo::Data;
friend class KStartupInfo::Private;
struct Private;
Private *const d;
};
#endif
@@ -0,0 +1,49 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2003 Luboš Luňák <l.lunak@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kusertimestamp.h"
#include "config-kwindowsystem.h"
#include "kwindowsystem.h"
#if KWINDOWSYSTEM_HAVE_X11
#include <private/qtx11extras_p.h>
#include <netwm.h>
#endif
unsigned long KUserTimestamp::userTimestamp()
{
if (KWindowSystem::isPlatformX11()) {
#if KWINDOWSYSTEM_HAVE_X11
return QX11Info::appUserTime();
#endif
}
return 0;
}
void KUserTimestamp::updateUserTimestamp(unsigned long time)
{
#if KWINDOWSYSTEM_HAVE_X11
if (!KWindowSystem::isPlatformX11()) {
return;
}
if (time == 0) { // get current X timestamp
time = QX11Info::getTimestamp();
}
if (QX11Info::appUserTime() == 0 || NET::timestampCompare(time, QX11Info::appUserTime()) > 0) { // time > appUserTime
QX11Info::setAppUserTime(time);
}
if (QX11Info::appTime() == 0 || NET::timestampCompare(time, QX11Info::appTime()) > 0) { // time > appTime
QX11Info::setAppTime(time);
}
#else
Q_UNUSED(time)
#endif
}
@@ -0,0 +1,29 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2003 Luboš Luňák <l.lunak@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KUSERTIMESTAMP_H
#define KUSERTIMESTAMP_H
#include <kwindowsystem_export.h>
namespace KUserTimestamp
{
/**
* Returns the last user action timestamp or 0 if no user activity has taken place yet.
* @see updateuserTimestamp
*/
KWINDOWSYSTEM_EXPORT unsigned long userTimestamp();
/**
* Updates the last user action timestamp to the given time, or to the current time,
* if 0 is given. Do not use unless you're really sure what you're doing.
* Consult focus stealing prevention section in kdebase/kwin/README.
*/
KWINDOWSYSTEM_EXPORT void updateUserTimestamp(unsigned long time = 0);
}
#endif
@@ -0,0 +1,66 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2021 Aleix Pol <aleixpol@kde.org>
SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwaylandextras.h"
#include "kwindowsystem.h"
#include "kwindowsystem_p.h"
#include <QTimer>
KWaylandExtras::KWaylandExtras()
: QObject()
{
}
KWaylandExtras::~KWaylandExtras() = default;
KWaylandExtras *KWaylandExtras::self()
{
static KWaylandExtras instance;
return &instance;
}
void KWaylandExtras::requestXdgActivationToken(QWindow *window, uint32_t serial, const QString &app_id)
{
auto dv2 = dynamic_cast<KWindowSystemPrivateV2 *>(KWindowSystem::d_func());
if (!dv2) {
// Ensure that xdgActivationTokenArrived is always emitted asynchronously
QTimer::singleShot(0, [serial] {
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {});
});
return;
}
dv2->requestToken(window, serial, app_id);
}
quint32 KWaylandExtras::lastInputSerial(QWindow *window)
{
auto dv2 = dynamic_cast<KWindowSystemPrivateV2 *>(KWindowSystem::d_func());
if (!dv2) {
return 0;
}
return dv2->lastInputSerial(window);
}
void KWaylandExtras::exportWindow(QWindow *window)
{
if (auto dv2 = dynamic_cast<KWindowSystemPrivateV2 *>(KWindowSystem::d_func())) {
dv2->exportWindow(window);
}
}
void KWaylandExtras::unexportWindow(QWindow *window)
{
if (auto dv2 = dynamic_cast<KWindowSystemPrivateV2 *>(KWindowSystem::d_func())) {
dv2->unexportWindow(window);
}
}
#include "moc_kwaylandextras.cpp"
@@ -0,0 +1,90 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2021 Aleix Pol <aleixpol@kde.org>
SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWAYLANDEXTRAS_H
#define KWAYLANDEXTRAS_H
#include <QObject>
#include <QWindow>
#include <kwindowsystem_export.h>
/**
* A collection of functions to do Wayland things
* @since 6.0
*/
class KWINDOWSYSTEM_EXPORT KWaylandExtras : public QObject
{
Q_OBJECT
public:
static KWaylandExtras *self();
/**
* Requests an xdg_activation_v1 token for a specific window.
*
* @param win window in behalf this request is made
* @param serial of the event that triggered the request
* @param app_id identifier of the application that we are launching
*
* @see lastInputSerial
*/
Q_INVOKABLE static void requestXdgActivationToken(QWindow *win, uint32_t serial, const QString &app_id);
/**
* Offers the seat's current serial
*/
Q_INVOKABLE static quint32 lastInputSerial(QWindow *window);
/**
* Requests to export the given window using xdg_foreign_v2.
*
* @param window The window to export.
*
* @see windowExported
* @since 6.0
*/
Q_INVOKABLE static void exportWindow(QWindow *window);
/**
* Unexport the window previously exported using xdg_foreign_v2.
*
* Asks the compositor to revoke the handle.
*
* @param window The window to unexport.
* @since 6.0
*/
Q_INVOKABLE static void unexportWindow(QWindow *window);
Q_SIGNALS:
/**
* Activation @p token to pass to the client.
*
* @see requestXdgActivationToken
*/
void xdgActivationTokenArrived(int serial, const QString &token);
/**
* Window @p handle to pass to the client.
*
* @param window The window that requested the handle.
* @param handle The handle.
*
* @see exportWindow
* @since 6.0
*/
void windowExported(QWindow *window, const QString &handle);
private:
KWaylandExtras();
~KWaylandExtras();
void *d;
};
#endif
@@ -0,0 +1,40 @@
/*
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 "kwindoweffects_p.h"
#include "pluginwrapper_p.h"
#include <QWindow>
KWindowEffectsPrivate::KWindowEffectsPrivate()
{
}
KWindowEffectsPrivate::~KWindowEffectsPrivate()
{
}
namespace KWindowEffects
{
bool isEffectAvailable(Effect effect)
{
return KWindowSystemPluginWrapper::self().effects()->isEffectAvailable(effect);
}
void enableBlurBehind(QWindow *window, bool enable, const QRegion &region)
{
KWindowSystemPluginWrapper::self().effects()->enableBlurBehind(window, enable, region);
}
void enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion &region)
{
KWindowSystemPluginWrapper::self().effects()->enableBackgroundContrast(window, enable, contrast, intensity, saturation, region);
}
void slideWindow(QWindow *window, SlideFromLocation location, int offset)
{
KWindowSystemPluginWrapper::self().effects()->slideWindow(window, location, offset);
}
}
@@ -0,0 +1,102 @@
/*
SPDX-FileCopyrightText: 2009 Marco Martin <notmart@gmail.com>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWEFFECTS_H
#define KWINDOWEFFECTS_H
#include "kwindowsystem_export.h"
#include <optional>
#include <QWidgetList> // for WId, etc.
#include <QColor>
#include <QRegion>
/**
* Namespace for common standardized window effects
*/
namespace KWindowEffects
{
enum Effect {
Slide = 1,
BlurBehind = 7,
BackgroundContrast = 9,
};
enum SlideFromLocation {
NoEdge = 0,
TopEdge,
RightEdge,
BottomEdge,
LeftEdge,
};
/**
* @return if an atom property is available
*
* @param effect the effect we want to check
*/
KWINDOWSYSTEM_EXPORT bool isEffectAvailable(Effect effect);
/**
* Instructs the window manager to blur the background
* in the specified region behind the given window.
* The given region will overwrite any previous blur-behind region.
* Passing a null region will enable the blur effect for the whole window.
* The region is relative to the top-left corner of the client area.
*
* If @a enable is @c false, blur will be disabled for the whole window
* (@a region is ignored).
*
* Note that you will usually want to set the region to the shape of the window,
* excluding any shadow or halo.
*
* @param window The window for which to enable the blur effect
* @param enable Enable the effect if @c true, disable it if @c false
* @param region The region within the window where the background will be blurred, specified in logical pixels
*
* @since 5.82
*/
KWINDOWSYSTEM_EXPORT void enableBlurBehind(QWindow *window, bool enable = true, const QRegion &region = QRegion());
/**
* Instructs the window manager to modify the color of the background
* in the specified region behind the given window,
* in order to improve the contrast and readability of any text
* in the translucent window.
* The given region will overwrite any previous backgroundcontrast region.
* Passing a null region will enable the blur effect for the whole window.
* The region is relative to the top-left corner of the client area.
*
* If @a enable is @c false, blur will be disabled for the whole window
* (@a region is ignored).
*
* Note that you will usually want to set the region to the shape of the window,
* excluding any shadow or halo.
*
* @param window The window for which to enable the background contrast effect
* @param enable Enable the effect if @c true, disable it if @c false
* @param brightness How to modify the area brightness: from 0 (make it black) to 2 (make it white), 1 leaves it unchanged
* @param region The region within the window where the background will be modified, specified in logical pixels
*
* @since 5.82
*/
KWINDOWSYSTEM_EXPORT void
enableBackgroundContrast(QWindow *window, bool enable = true, qreal contrast = 1, qreal intensity = 1, qreal saturation = 1, const QRegion &region = QRegion());
/**
* Mark a window as sliding from screen edge
*
* @param id of the window on which we want to apply the effect
* @param location edge of the screen from which we want the sliding effect.
* Desktop and Floating won't have effect.
* @param offset distance in pixels from the screen edge defined by location
*
* @since 5.82
*/
KWINDOWSYSTEM_EXPORT void slideWindow(QWindow *window, SlideFromLocation location, int offset = -1);
}
#endif
@@ -0,0 +1,53 @@
/*
SPDX-FileCopyrightText: 2009 Marco Martin <notmart@gmail.com>
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwindoweffects_dummy_p.h"
#include <QList>
KWindowEffectsPrivateDummy::KWindowEffectsPrivateDummy()
{
}
KWindowEffectsPrivateDummy::~KWindowEffectsPrivateDummy()
{
}
bool KWindowEffectsPrivateDummy::isEffectAvailable(KWindowEffects::Effect effect)
{
Q_UNUSED(effect)
return false;
}
void KWindowEffectsPrivateDummy::slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset)
{
Q_UNUSED(window)
Q_UNUSED(location)
Q_UNUSED(offset)
}
void KWindowEffectsPrivateDummy::enableBlurBehind(QWindow *window, bool enable, const QRegion &region)
{
Q_UNUSED(window)
Q_UNUSED(enable)
Q_UNUSED(region)
}
void KWindowEffectsPrivateDummy::enableBackgroundContrast(QWindow *window,
bool enable,
qreal contrast,
qreal intensity,
qreal saturation,
const QRegion &region)
{
Q_UNUSED(window)
Q_UNUSED(enable)
Q_UNUSED(contrast)
Q_UNUSED(intensity)
Q_UNUSED(saturation)
Q_UNUSED(region)
}
@@ -0,0 +1,27 @@
/*
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
*/
#ifndef KWINDOWEFFECTS_DUMMY_P_H
#define KWINDOWEFFECTS_DUMMY_P_H
#include "kwindoweffects_p.h"
class KWindowEffectsPrivateDummy : public KWindowEffectsPrivate
{
public:
KWindowEffectsPrivateDummy();
~KWindowEffectsPrivateDummy() override;
bool isEffectAvailable(KWindowEffects::Effect effect) override;
void slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) override;
void enableBlurBehind(QWindow *window, bool enable = true, const QRegion &region = QRegion()) override;
void enableBackgroundContrast(QWindow *window,
bool enable = true,
qreal contrast = 1,
qreal intensity = 1,
qreal saturation = 1,
const QRegion &region = QRegion()) override;
};
#endif
@@ -0,0 +1,28 @@
/*
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
*/
#ifndef KWINDOWEFFECTS_P_H
#define KWINDOWEFFECTS_P_H
#include "kwindoweffects.h"
class KWINDOWSYSTEM_EXPORT KWindowEffectsPrivate
{
public:
virtual ~KWindowEffectsPrivate();
virtual bool isEffectAvailable(KWindowEffects::Effect effect) = 0;
virtual void slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) = 0;
virtual void enableBlurBehind(QWindow *window, bool enable = true, const QRegion &region = QRegion()) = 0;
virtual void enableBackgroundContrast(QWindow *window,
bool enable = true,
qreal contrast = 1,
qreal intensity = 1,
qreal saturation = 1,
const QRegion &region = QRegion()) = 0;
protected:
KWindowEffectsPrivate();
};
#endif
@@ -0,0 +1,606 @@
/*
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 "kwindowinfo.h"
#include "kwindowsystem.h"
#include "kwindowsystem_debug.h"
#include "kx11extras.h"
#include "netwm.h"
#include <config-kwindowsystem.h>
#include "private/qtx11extras_p.h"
#include <QDebug>
#include <QRect>
#include "kxerrorhandler_p.h"
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <xcb/res.h>
#include "cptr_p.h"
static bool haveXRes()
{
static bool s_checked = false;
static bool s_haveXRes = false;
if (!s_checked) {
auto cookie = xcb_res_query_version(QX11Info::connection(), XCB_RES_MAJOR_VERSION, XCB_RES_MINOR_VERSION);
UniqueCPointer<xcb_res_query_version_reply_t> reply(xcb_res_query_version_reply(QX11Info::connection(), cookie, nullptr));
s_haveXRes = reply != nullptr;
s_checked = true;
}
return s_haveXRes;
}
class Q_DECL_HIDDEN KWindowInfoPrivate : public QSharedData
{
public:
WId window;
NET::Properties properties;
NET::Properties2 properties2;
std::unique_ptr<NETWinInfo> m_info;
QString m_name;
QString m_iconic_name;
QRect m_geometry;
QRect m_frame_geometry;
int m_pid = -1; // real PID from XResources. Valid if > 0
bool m_valid = false;
};
KWindowInfo::KWindowInfo(WId window, NET::Properties properties, NET::Properties2 properties2)
: d(new KWindowInfoPrivate)
{
d->window = window;
d->properties = properties;
d->properties2 = properties2;
if (!KWindowSystem::isPlatformX11()) {
return;
}
KXErrorHandler handler;
if (properties & NET::WMVisibleIconName) {
properties |= NET::WMIconName | NET::WMVisibleName; // force, in case it will be used as a fallback
}
if (properties & NET::WMVisibleName) {
properties |= NET::WMName; // force, in case it will be used as a fallback
}
if (properties2 & NET::WM2ExtendedStrut) {
properties |= NET::WMStrut; // will be used as fallback
}
if (properties & NET::WMWindowType) {
properties2 |= NET::WM2TransientFor; // will be used when type is not set
}
if ((properties & NET::WMDesktop) && KX11Extras::mapViewport()) {
properties |= NET::WMGeometry; // for viewports, the desktop (workspace) is determined from the geometry
}
properties |= NET::XAWMState; // force to get error detection for valid()
d->m_info.reset(new NETWinInfo(QX11Info::connection(), d->window, QX11Info::appRootWindow(), properties, properties2));
if (properties & NET::WMName) {
if (d->m_info->name() && d->m_info->name()[0] != '\0') {
d->m_name = QString::fromUtf8(d->m_info->name());
} else {
d->m_name = KX11Extras::readNameProperty(d->window, XA_WM_NAME);
}
}
if (properties & NET::WMIconName) {
if (d->m_info->iconName() && d->m_info->iconName()[0] != '\0') {
d->m_iconic_name = QString::fromUtf8(d->m_info->iconName());
} else {
d->m_iconic_name = KX11Extras::readNameProperty(d->window, XA_WM_ICON_NAME);
}
}
if (properties & (NET::WMGeometry | NET::WMFrameExtents)) {
NETRect frame;
NETRect geom;
d->m_info->kdeGeometry(frame, geom);
d->m_geometry.setRect(geom.pos.x, geom.pos.y, geom.size.width, geom.size.height);
d->m_frame_geometry.setRect(frame.pos.x, frame.pos.y, frame.size.width, frame.size.height);
}
d->m_valid = !handler.error(false); // no sync - NETWinInfo did roundtrips
if (haveXRes()) {
xcb_res_client_id_spec_t specs;
specs.client = win();
specs.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
auto cookie = xcb_res_query_client_ids(QX11Info::connection(), 1, &specs);
UniqueCPointer<xcb_res_query_client_ids_reply_t> reply(xcb_res_query_client_ids_reply(QX11Info::connection(), cookie, nullptr));
if (reply && xcb_res_query_client_ids_ids_length(reply.get()) > 0) {
uint32_t pid = *xcb_res_client_id_value_value((xcb_res_query_client_ids_ids_iterator(reply.get()).data));
d->m_pid = pid;
}
}
}
KWindowInfo::KWindowInfo(const KWindowInfo &other)
: d(other.d)
{
}
KWindowInfo::~KWindowInfo()
{
}
KWindowInfo &KWindowInfo::operator=(const KWindowInfo &other)
{
if (d != other.d) {
d = other.d;
}
return *this;
}
bool KWindowInfo::valid(bool withdrawn_is_valid) const
{
if (!KWindowSystem::isPlatformX11()) {
return false;
}
if (!d->m_valid) {
return false;
}
if (!withdrawn_is_valid && mappingState() == NET::Withdrawn) {
return false;
}
return true;
}
WId KWindowInfo::win() const
{
return d->window;
}
#define CHECK_X11 \
if (!KWindowSystem::isPlatformX11()) { \
qCWarning(LOG_KWINDOWSYSTEM) << "KWindowInfo is only functional when running on X11"; \
return {}; \
}
NET::States KWindowInfo::state() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMState)) {
qWarning() << "Pass NET::WMState to KWindowInfo";
}
#endif
return d->m_info->state();
}
bool KWindowInfo::hasState(NET::States s) const
{
CHECK_X11
return (state() & s) == s;
}
bool KWindowInfo::icccmCompliantMappingState() const
{
CHECK_X11
static enum { noidea, yes, no } wm_is_1_2_compliant = noidea;
if (wm_is_1_2_compliant == noidea) {
NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen());
wm_is_1_2_compliant = info.isSupported(NET::Hidden) ? yes : no;
}
return wm_is_1_2_compliant == yes;
}
// see NETWM spec section 7.6
bool KWindowInfo::isMinimized() const
{
CHECK_X11
if (mappingState() != NET::Iconic) {
return false;
}
// NETWM 1.2 compliant WM - uses NET::Hidden for minimized windows
if ((state() & NET::Hidden) != 0 && (state() & NET::Shaded) == 0) { // shaded may have NET::Hidden too
return true;
}
// older WMs use WithdrawnState for other virtual desktops
// and IconicState only for minimized
return icccmCompliantMappingState() ? false : true;
}
NET::MappingState KWindowInfo::mappingState() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::XAWMState)) {
qWarning() << "Pass NET::XAWMState to KWindowInfo";
}
#endif
return d->m_info->mappingState();
}
NETExtendedStrut KWindowInfo::extendedStrut() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2ExtendedStrut)) {
qWarning() << "Pass NET::WM2ExtendedStrut to KWindowInfo";
}
#endif
NETExtendedStrut ext = d->m_info->extendedStrut();
NETStrut str = d->m_info->strut();
if (ext.left_width == 0 && ext.right_width == 0 && ext.top_width == 0 && ext.bottom_width == 0
&& (str.left != 0 || str.right != 0 || str.top != 0 || str.bottom != 0)) {
// build extended from simple
if (str.left != 0) {
ext.left_width = str.left;
ext.left_start = 0;
ext.left_end = XDisplayHeight(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
if (str.right != 0) {
ext.right_width = str.right;
ext.right_start = 0;
ext.right_end = XDisplayHeight(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
if (str.top != 0) {
ext.top_width = str.top;
ext.top_start = 0;
ext.top_end = XDisplayWidth(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
if (str.bottom != 0) {
ext.bottom_width = str.bottom;
ext.bottom_start = 0;
ext.bottom_end = XDisplayWidth(QX11Info::display(), DefaultScreen(QX11Info::display()));
}
}
return ext;
}
NET::WindowType KWindowInfo::windowType(NET::WindowTypes supported_types) const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMWindowType)) {
qWarning() << "Pass NET::WMWindowType to KWindowInfo";
}
#endif
if (!d->m_info->hasWindowType()) { // fallback, per spec recommendation
if (transientFor() != XCB_WINDOW_NONE) { // dialog
if (supported_types & NET::DialogMask) {
return NET::Dialog;
}
} else {
if (supported_types & NET::NormalMask) {
return NET::Normal;
}
}
}
return d->m_info->windowType(supported_types);
}
QString KWindowInfo::visibleName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMVisibleName)) {
qWarning() << "Pass NET::WMVisibleName to KWindowInfo";
}
#endif
return d->m_info->visibleName() && d->m_info->visibleName()[0] != '\0' ? QString::fromUtf8(d->m_info->visibleName()) : name();
}
QString KWindowInfo::visibleNameWithState() const
{
CHECK_X11
QString s = visibleName();
if (isMinimized()) {
s.prepend(QLatin1Char('('));
s.append(QLatin1Char(')'));
}
return s;
}
QString KWindowInfo::name() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMName)) {
qWarning() << "Pass NET::WMName to KWindowInfo";
}
#endif
return d->m_name;
}
QString KWindowInfo::visibleIconName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMVisibleIconName)) {
qWarning() << "Pass NET::WMVisibleIconName to KWindowInfo";
}
#endif
if (d->m_info->visibleIconName() && d->m_info->visibleIconName()[0] != '\0') {
return QString::fromUtf8(d->m_info->visibleIconName());
}
if (d->m_info->iconName() && d->m_info->iconName()[0] != '\0') {
return QString::fromUtf8(d->m_info->iconName());
}
if (!d->m_iconic_name.isEmpty()) {
return d->m_iconic_name;
}
return visibleName();
}
QString KWindowInfo::visibleIconNameWithState() const
{
CHECK_X11
QString s = visibleIconName();
if (isMinimized()) {
s.prepend(QLatin1Char('('));
s.append(QLatin1Char(')'));
}
return s;
}
QString KWindowInfo::iconName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMIconName)) {
qWarning() << "Pass NET::WMIconName to KWindowInfo";
}
#endif
if (d->m_info->iconName() && d->m_info->iconName()[0] != '\0') {
return QString::fromUtf8(d->m_info->iconName());
}
if (!d->m_iconic_name.isEmpty()) {
return d->m_iconic_name;
}
return name();
}
bool KWindowInfo::isOnCurrentDesktop() const
{
CHECK_X11
return isOnDesktop(KX11Extras::currentDesktop());
}
bool KWindowInfo::isOnDesktop(int desktop) const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMDesktop)) {
qWarning() << "Pass NET::WMDesktop to KWindowInfo";
}
#endif
if (KX11Extras::mapViewport()) {
if (onAllDesktops()) {
return true;
}
return KX11Extras::viewportWindowToDesktop(d->m_geometry) == desktop;
}
return d->m_info->desktop() == desktop || d->m_info->desktop() == NET::OnAllDesktops;
}
bool KWindowInfo::onAllDesktops() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMDesktop)) {
qWarning() << "Pass NET::WMDesktop to KWindowInfo";
}
#endif
if (KX11Extras::mapViewport()) {
if (d->m_info->passedProperties() & NET::WMState) {
return d->m_info->state() & NET::Sticky;
}
NETWinInfo info(QX11Info::connection(), win(), QX11Info::appRootWindow(), NET::WMState, NET::Properties2());
return info.state() & NET::Sticky;
}
return d->m_info->desktop() == NET::OnAllDesktops;
}
int KWindowInfo::desktop() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMDesktop)) {
qWarning() << "Pass NET::WMDesktop to KWindowInfo";
}
#endif
if (KX11Extras::mapViewport()) {
if (onAllDesktops()) {
return NET::OnAllDesktops;
}
return KX11Extras::viewportWindowToDesktop(d->m_geometry);
}
return d->m_info->desktop();
}
QStringList KWindowInfo::activities() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2Activities)) {
qWarning() << "Pass NET::WM2Activities to KWindowInfo";
}
#endif
const QStringList result = QString::fromLatin1(d->m_info->activities()).split(QLatin1Char(','), Qt::SkipEmptyParts);
return result.contains(QStringLiteral(KDE_ALL_ACTIVITIES_UUID)) ? QStringList() : result;
}
QRect KWindowInfo::geometry() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMGeometry)) {
qWarning() << "Pass NET::WMGeometry to KWindowInfo";
}
#endif
return d->m_geometry;
}
QRect KWindowInfo::frameGeometry() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMFrameExtents)) {
qWarning() << "Pass NET::WMFrameExtents to KWindowInfo";
}
#endif
return d->m_frame_geometry;
}
WId KWindowInfo::transientFor() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2TransientFor)) {
qWarning() << "Pass NET::WM2TransientFor to KWindowInfo";
}
#endif
return d->m_info->transientFor();
}
WId KWindowInfo::groupLeader() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2GroupLeader)) {
qWarning() << "Pass NET::WM2GroupLeader to KWindowInfo";
}
#endif
return d->m_info->groupLeader();
}
QByteArray KWindowInfo::windowClassClass() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2WindowClass)) {
qWarning() << "Pass NET::WM2WindowClass to KWindowInfo";
}
#endif
return d->m_info->windowClassClass();
}
QByteArray KWindowInfo::windowClassName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2WindowClass)) {
qWarning() << "Pass NET::WM2WindowClass to KWindowInfo";
}
#endif
return d->m_info->windowClassName();
}
QByteArray KWindowInfo::windowRole() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2WindowRole)) {
qWarning() << "Pass NET::WM2WindowRole to KWindowInfo";
}
#endif
return d->m_info->windowRole();
}
QByteArray KWindowInfo::clientMachine() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2ClientMachine)) {
qWarning() << "Pass NET::WM2ClientMachine to KWindowInfo";
}
#endif
return d->m_info->clientMachine();
}
bool KWindowInfo::allowedActionsSupported() const
{
CHECK_X11
static enum { noidea, yes, no } wm_supports_allowed_actions = noidea;
if (wm_supports_allowed_actions == noidea) {
NETRootInfo info(QX11Info::connection(), NET::Supported, NET::Properties2(), QX11Info::appScreen());
wm_supports_allowed_actions = info.isSupported(NET::WM2AllowedActions) ? yes : no;
}
return wm_supports_allowed_actions == yes;
}
bool KWindowInfo::actionSupported(NET::Action action) const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2AllowedActions)) {
qWarning() << "Pass NET::WM2AllowedActions to KWindowInfo";
}
#endif
if (allowedActionsSupported()) {
return d->m_info->allowedActions() & action;
} else {
return true; // no idea if it's supported or not -> pretend it is
}
}
QByteArray KWindowInfo::desktopFileName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2DesktopFileName)) {
qWarning() << "Pass NET::WM2DesktopFileName to KWindowInfo";
}
#endif
return QByteArray(d->m_info->desktopFileName());
}
QByteArray KWindowInfo::gtkApplicationId() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2DesktopFileName)) {
qWarning() << "Pass NET::WM2DesktopFileName to KWindowInfo";
}
#endif
return QByteArray(d->m_info->gtkApplicationId());
}
QByteArray KWindowInfo::applicationMenuServiceName() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2AppMenuServiceName)) {
qWarning() << "Pass NET::WM2AppMenuServiceName to KWindowInfo";
}
#endif
return QByteArray(d->m_info->appMenuServiceName());
}
QByteArray KWindowInfo::applicationMenuObjectPath() const
{
CHECK_X11
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties2() & NET::WM2AppMenuObjectPath)) {
qWarning() << "Pass NET::WM2AppMenuObjectPath to KWindowInfo";
}
#endif
return QByteArray(d->m_info->appMenuObjectPath());
}
int KWindowInfo::pid() const
{
CHECK_X11
// If pid is found using the XRes extension use that instead.
// It is more reliable than the app reporting it's own PID as apps
// within an app namespace are unable to do so correctly
if (d->m_pid > 0) {
return d->m_pid;
}
#if !defined(KDE_NO_WARNING_OUTPUT)
if (!(d->m_info->passedProperties() & NET::WMPid)) {
qWarning() << "Pass NET::WMPid to KWindowInfo";
}
#endif
return d->m_info->pid();
}
@@ -0,0 +1,619 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
/*
* kwindowinfo.h. Part of the KDE project.
*/
#ifndef KWINDOWINFO_H
#define KWINDOWINFO_H
#include <QExplicitlySharedDataPointer>
#include <QStringList>
#include <QWidgetList> //For WId
#include <kwindowsystem_export.h>
#include <netwm_def.h>
class KWindowInfoPrivate;
/**
* This class provides information about a given X11 window. It provides the information
* for the current state when a KWindowInfo instance gets created.
* The instance does not get updated when the
* window changes. To get update about window changes connect to the
* @link KX11Extras::windowChanged windowChanged@endlink signal of KX11Extras
* and create a new KWindowInfo instance to reflect the current state.
*
* KWindowInfo does not encapsulate all information about the window. One needs to
* request which information is required by passing the appropriate NET::Property and
* NET::Property2 flags to the constructor. Please refer to the documentation of the
* methods to see which flags are required. This is done to limit the interaction with
* the xserver and avoid excess roundtrips.
*
* KWindowInfo does nothing when running outside of X11.
*
* Example usage of this class illustrated by monitoring a QWidget for change of the
* demands attention window state:
*
* @code
* QWidget *widget = new QWidget(nullptr);
* widget->show(); // ensures native window gets created
* connect(KX11Extras::self(), &KX11Extras::KWindowSystem::windowChanged,
* [window](WId id, NET::Properties properties, NET::Properties2 properties2) {
* if (widget->winId() != winId) {
* return; // not our window
* }
* if (properties & NET::WMState) {
* // let's check whether our window is demanding attention
* KWindowInfo info(widget->winId(), NET::WMState);
* qDebug() << "Has demands attention: " << info.hasState(NET::DemandsAttention);
* }
* });
* @endcode
*/
class KWINDOWSYSTEM_EXPORT KWindowInfo
{
public:
/**
* Reads all the info about the given window.
*
* Only the information requested through the @p properties and @p properties2
* parameters are fetched. Refer to the methods you are interested in to see
* which flags to pass.
*
* @param window The platform specific window identifier
* @param properties Bitmask of NET::Property
* @param properties2 Bitmask of NET::Property2
*/
KWindowInfo(WId window, NET::Properties properties, NET::Properties2 properties2 = NET::Properties2());
~KWindowInfo();
/**
* Returns false if this window info is not valid.
*
* In case the window does not exist @c false is returned.
*
* @param withdrawn_is_valid if true, windows in the withdrawn state
* (i.e. not managed) are also considered. This is usually not the case.
*/
bool valid(bool withdrawn_is_valid = false) const;
/**
* Returns the window identifier.
*/
WId win() const;
/**
* Returns the window's state flags.
*
* Requires NET::WMState passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMState);
* if (info.valid())
* info.state();
* @endcode
*
* @see NET::State
*/
NET::States state() const;
/**
* Returns true if the window has the given state flag set.
*
* Requires NET::WMState passed as properties parameter to the constructor.
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMState);
* if (info.valid())
* info.hasState(NET::DemandsAttention);
* @endcode
*
* @see NET::State
*/
bool hasState(NET::States s) const;
/**
* Returns true if the window is minimized.
*
* Note that it is true only if the window is truly minimized,
* not shaded or on another virtual desktops,
* which makes it different from mappingState() == NET::Iconic
* or QWidget::isMinimized().
* Requires NET::WMState and NET::XAWMState passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMState | NET::XAWMState);
* if (info.valid())
* info.isMinimized();
* @endcode
*/
bool isMinimized() const;
/**
* Returns the mapping state of the window.
*
* Note that it's very likely that you don't want to use this function,
* and use isOnDesktop(), isMinimized() etc. instead.
* Requires NET::XAWMState passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::XAWMState);
* if (info.valid())
* info.mappingState();
* @endcode
*
* @see NET::MappingState
* @see isOnDesktop()
* @see isMinimzed()
*/
NET::MappingState mappingState() const;
/**
* Returns the window extended (partial) strut.
*
* Requires NET::WM2ExtendedStrut passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2ExtendedStrut);
* if (info.valid())
* info.extendedStrut();
* @endcode
*/
NETExtendedStrut extendedStrut() const;
/**
* Returns the window type of this window.
*
* The argument should be all window types your application supports.
* Requires NET::WMWindowType passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMWindowType);
* if (info.valid())
* info.windowType(NET::NormalMask | NET::DialogMask);
* @endcode
*
* @see NET::WindowType
* @see NET::WindowTypeMask
*/
NET::WindowType windowType(NET::WindowTypes supported_types) const;
/**
* Returns the visible name of the window.
*
* The visible name differs from the name by including possible <2> appended
* when there are two or more windows with the same name.
* Requires NET::WMVisibleName passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMVisibleName);
* if (info.valid())
* info.visibleName();
* @endcode
*
* @see name()
*/
QString visibleName() const;
/**
* Returns a visible name with state.
*
* This is a simple convenience function that returns the
* visible name but with parentheses around minimized windows.
* Requires NET::WMVisibleName, NET::WMState and NET::XAWMState passed
* as properties parameter to the constructor.
* @return the window name with state
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMVisibleName | NET::WMState | NET::XAWMState);
* if (info.valid())
* info.visibleNameWithState();
* @endcode
*
* @see visibleName()
*/
QString visibleNameWithState() const;
/**
* Returns the name of the window, as specified by the application.
*
* The difference to visibleName() is that this is the name provided by
* the application without any modifications by the window manager.
* You should often use visibleName() instead.
* Requires NET::WMName passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMName);
* if (info.valid())
* info.name();
* @endcode
*
* @see visibleName()
*/
QString name() const;
/**
* Returns the visible name of the window that should be shown in a taskbar.
*
* Note that this has nothing to do with normal icons but with an "iconic"
* representation of the window.
* Requires NET::WMVisibleIconName passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMVisibleIconName);
* if (info.valid())
* info.visibleIconName();
* @endcode
*/
QString visibleIconName() const;
/**
* Returns a visible icon name with state.
*
* This is a simple convenience function that returns the
* visible iconic name but with parentheses around minimized windows.
* Note that this has nothing to do with normal icons.
* Requires NET::WMVisibleIconName, NET::WMState and NET::XAWMState passed
* as properties parameter to the constructor.
* @return the window iconic name with state
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMVisibleIconName | NET::WMState | NET::XAWMState);
* if (info.valid())
* info.visibleIconNameWithState();
* @endcode
*
* @see visibleIconName()
*/
QString visibleIconNameWithState() const;
/**
* Returns the name of the window that should be shown in taskbar.
*
* Note that this has nothing to do with normal icons but with an "iconic"
* representation of the window.
* Requires NET::WMIconName passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMIconName);
* if (info.valid())
* info.iconName();
* @endcode
*/
QString iconName() const;
/**
* Returns true if the window is on the currently active virtual desktop.
*
* Requires NET::WMDesktop passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMDesktop);
* if (info.valid())
* info.isOnCurrentDesktop();
* @endcode
*/
bool isOnCurrentDesktop() const;
/**
* Returns true if the window is on the given virtual desktop.
*
* Requires NET::WMDesktop passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMDesktop);
* if (info.valid())
* info.isOnDesktop(KWindowSystem::currentDesktop());
* @endcode
*/
bool isOnDesktop(int desktop) const;
/**
* Returns true if the window is on all desktops.
*
* A window is on all desktops if desktop() returns NET::OnAllDesktops.
* Requires NET::WMDesktop passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMDesktop);
* if (info.valid())
* info.onAllDesktops();
* @endcode
*
* @see desktop()
*/
bool onAllDesktops() const;
/**
* Returns the virtual desktop this window is on.
*
* If the window is on all desktops NET::OnAllDesktops is returned.
* You should prefer using isOnDesktop().
* Requires NET::WMDesktop passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMDesktop);
* if (info.valid())
* info.desktop();
* @endcode
*
* @see isOnDesktop()
*/
int desktop() const;
/**
* Returns the list of activity UUIDs this window belongs to.
*
* The Plasma workspace allows the user to separate her work into
* different activities, by assigning windows, documents etc. to
* the specific ones. An activity is an abstract concept whose meaning
* can differ from one user to another. Typical examples of activities
* are "developing a KDE project", "studying the 19th century art",
* "composing music", "lazing on a Sunday afternoon" etc.
*
* If the list is empty, or contains a null UUID, the window is on
* all activities.
*
* Requires NET::WM2Activities passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2Activities);
* if (info.valid())
* info.desktop();
* @endcode
*
* @note Activities are only supported on Plasma Workspace on X11
*
* @since 5.0
*/
QStringList activities() const;
/**
* Returns the position and size of the window contents.
*
* Requires NET::WMGeometry passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMGeometry);
* if (info.valid())
* info.geometry();
* @endcode
*/
QRect geometry() const;
/**
* Returns the frame geometry of the window, i.e. including the window decoration.
*
* Requires NET::WMFrameExtents passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMFrameExtents);
* if (info.valid())
* info.frameGeometry();
* @endcode
*/
QRect frameGeometry() const;
/**
* Returns the window identifier of the main window this window belongs to.
*
* This is the value of the WM_TRANSIENT_FOR X11 property.
*
* Requires NET::WM2TransientFor passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2TransientFor);
* if (info.valid())
* info.transientFor();
* @endcode
*/
WId transientFor() const;
/**
* Returns the leader window for the group the window is in, if any.
*
* Requires NET::WM2GroupLeader passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2GroupLeader);
* if (info.valid())
* info.groupLeader();
* @endcode
*/
WId groupLeader() const;
/**
* Returns the class component of the WM_CLASS X11 property for the window.
*
* Requires NET::WM2WindowClass passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2WindowClass);
* if (info.valid())
* info.windowClassClass();
* @endcode
*/
QByteArray windowClassClass() const;
/**
* Returns the name component of the WM_CLASS X11 property for the window.
*
* Requires NET::WM2WindowClass passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2WindowClass);
* if (info.valid())
* info.windowClassName();
* @endcode
*/
QByteArray windowClassName() const;
/**
* Returns the WM_WINDOW_ROLE X11 property for the window.
*
* Requires NET::WM2WindowRole passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2WindowRole);
* if (info.valid())
* info.windowRole();
* @endcode
*/
QByteArray windowRole() const;
/**
* Returns the WM_CLIENT_MACHINE property for the window.
*
* Requires NET::WM2ClientMachine passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2ClientMachine);
* if (info.valid())
* info.clientMachine();
* @endcode
*/
QByteArray clientMachine() const;
/**
* Returns true if the given action is currently supported for the window.
*
* The supported actions are set by the window manager and
* can differ depending on the window manager.
* Requires NET::WM2AllowedActions passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2AllowedActions);
* if (info.valid())
* info.actionSupported(NET::ActionClose);
* @endcode
*/
bool actionSupported(NET::Action action) const;
/**
* Returns the desktop file name of the window's application if present.
*
* This is either the base name without full path and without file extension of the
* desktop file for the window's application (e.g. "org.kde.foo").
*
* If the application's desktop file name is not at a standard location it should be
* the full path to the desktop file name (e.g. "/opt/kde/share/org.kde.foo.desktop").
*
* Requires NET::WM2DesktopFileName passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2DesktopFileName);
* if (info.valid())
* info.desktopFileName();
* @endcode
*
* @since 5.29
**/
QByteArray desktopFileName() const;
/**
* Returns the GTK application id of the window if present.
*
* This is comparable to desktopFileName.
*
* Requires NET::WM2GTKApplicationId passed as properties2 parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), 0, NET::WM2GTKApplicationId);
* if (info.valid())
* info.gtkApplicationId();
* @endcode
*
* @since 5.91
**/
QByteArray gtkApplicationId() const;
/**
* Returns the process ID of the window's application if present.
*
* Requires NET::WMPid passed as properties parameter to the constructor.
*
* @code
* QWidget *window = new QWidget(nullptr);
* window->show();
* KWindowInfo info(window->winId(), NET::WMPid);
* if (info.valid())
* info.pid();
* @endcode
*
* @since 5.29
*/
int pid() const;
/**
* Returns service name of a window's application menu if present.
*
* Requires NET::WMPid passed as properties parameter to the constructor.
*
* @since 5.69
*/
QByteArray applicationMenuServiceName() const;
/**
* Returns object path of a window's application menu if present.
*
* Requires NET::WMPid passed as properties parameter to the constructor.
*
* @since 5.69
*/
QByteArray applicationMenuObjectPath() const;
/**
* Copy constructor.
*/
KWindowInfo(const KWindowInfo &);
/**
* Assignment operator.
*/
KWindowInfo &operator=(const KWindowInfo &);
private:
bool KWINDOWSYSTEM_NO_EXPORT icccmCompliantMappingState() const;
bool KWINDOWSYSTEM_NO_EXPORT allowedActionsSupported() const;
QExplicitlySharedDataPointer<KWindowInfoPrivate> d;
};
#endif // multiple inclusion guard
@@ -0,0 +1,338 @@
/*
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwindowshadow.h"
#include "kwindowshadow_dummy_p.h"
#include "kwindowshadow_p.h"
#include "kwindowsystem_debug.h"
#include "pluginwrapper_p.h"
#include <array>
KWindowShadowTile::KWindowShadowTile()
: d(KWindowSystemPluginWrapper::self().createWindowShadowTile())
{
}
KWindowShadowTile::~KWindowShadowTile()
{
if (d->isCreated) {
d->destroy();
}
}
QImage KWindowShadowTile::image() const
{
return d->image;
}
void KWindowShadowTile::setImage(const QImage &image)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot change the image on a tile that already has native "
"platform resources allocated.");
return;
}
d->image = image;
}
bool KWindowShadowTile::isCreated() const
{
return d->isCreated;
}
bool KWindowShadowTile::create()
{
if (d->isCreated) {
return true;
}
d->isCreated = d->create();
return d->isCreated;
}
KWindowShadow::KWindowShadow(QObject *parent)
: QObject(parent)
, d(KWindowSystemPluginWrapper::self().createWindowShadow())
{
}
KWindowShadow::~KWindowShadow()
{
destroy();
}
KWindowShadowTile::Ptr KWindowShadow::leftTile() const
{
return d->leftTile;
}
void KWindowShadow::setLeftTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a left tile to a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and then "
"setLeftTile() and create()");
return;
}
d->leftTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::topLeftTile() const
{
return d->topLeftTile;
}
void KWindowShadow::setTopLeftTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a top-left tile to a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and then "
"setTopLeftTile() and create()");
return;
}
d->topLeftTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::topTile() const
{
return d->topTile;
}
void KWindowShadow::setTopTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a top tile to a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and then "
"setTopTile() and create()");
return;
}
d->topTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::topRightTile() const
{
return d->topRightTile;
}
void KWindowShadow::setTopRightTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a top-right tile to a shadow that already "
"has native platform resources allocated. To do so, destroy() the shadow and "
"then setTopRightTile() and create()");
return;
}
d->topRightTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::rightTile() const
{
return d->rightTile;
}
void KWindowShadow::setRightTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a right tile to a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and then "
"setRightTile() and create()");
return;
}
d->rightTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::bottomRightTile() const
{
return d->bottomRightTile;
}
void KWindowShadow::setBottomRightTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a bottom-right tile to a shadow that already "
"has native platform resources allocated. To do so, destroy() the shadow and "
"then setBottomRightTile() and create()");
return;
}
d->bottomRightTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::bottomTile() const
{
return d->bottomTile;
}
void KWindowShadow::setBottomTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a bottom tile to a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and then "
"setBottomTile() and create()");
return;
}
d->bottomTile = tile;
}
KWindowShadowTile::Ptr KWindowShadow::bottomLeftTile() const
{
return d->bottomLeftTile;
}
void KWindowShadow::setBottomLeftTile(KWindowShadowTile::Ptr tile)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot attach a bottom-left tile to a shadow that already "
"has native platform resources allocated. To do so, destroy() the shadow and "
"then setBottomLeftTile() and create()");
return;
}
d->bottomLeftTile = tile;
}
QMargins KWindowShadow::padding() const
{
return d->padding;
}
void KWindowShadow::setPadding(const QMargins &padding)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot set the padding on a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and "
"then setPadding() and create()");
return;
}
d->padding = padding;
}
QWindow *KWindowShadow::window() const
{
return d->window;
}
void KWindowShadow::setWindow(QWindow *window)
{
if (d->isCreated) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot set the target window on a shadow that already has "
"native platform resources allocated. To do so, destroy() the shadow and then "
"setWindow() and create()");
return;
}
d->window = window;
}
bool KWindowShadow::isCreated() const
{
return d->isCreated;
}
bool KWindowShadow::create()
{
if (d->isCreated) {
return true;
}
if (!d->window) {
qCWarning(LOG_KWINDOWSYSTEM,
"Cannot allocate the native platform resources for the shadow "
"because the target window is not specified.");
return false;
}
if (!d->prepareTiles()) {
return false;
}
d->isCreated = d->create();
return d->isCreated;
}
void KWindowShadow::destroy()
{
if (!d->isCreated) {
return;
}
d->destroy();
d->isCreated = false;
}
KWindowShadowTilePrivate::~KWindowShadowTilePrivate()
{
}
KWindowShadowTilePrivate *KWindowShadowTilePrivate::get(const KWindowShadowTile *tile)
{
return tile->d.data();
}
KWindowShadowPrivate::~KWindowShadowPrivate()
{
}
bool KWindowShadowPrivate::create()
{
return false;
}
void KWindowShadowPrivate::destroy()
{
}
bool KWindowShadowPrivate::prepareTiles()
{
const std::array<KWindowShadowTile *, 8> tiles{
leftTile.data(),
topLeftTile.data(),
topTile.data(),
topRightTile.data(),
rightTile.data(),
bottomRightTile.data(),
bottomTile.data(),
bottomLeftTile.data(),
};
for (KWindowShadowTile *tile : tiles) {
if (!tile) {
continue;
}
if (tile->isCreated()) {
continue;
}
if (!tile->create()) {
return false;
}
}
return true;
}
bool KWindowShadowTilePrivateDummy::create()
{
return false;
}
void KWindowShadowTilePrivateDummy::destroy()
{
}
bool KWindowShadowPrivateDummy::create()
{
return false;
}
void KWindowShadowPrivateDummy::destroy()
{
}
#include "moc_kwindowshadow.cpp"
@@ -0,0 +1,225 @@
/*
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWSHADOW_H
#define KWINDOWSHADOW_H
#include "kwindowsystem_export.h"
#include <QImage>
#include <QMargins>
#include <QSharedPointer>
#include <QWindow>
class KWindowShadowPrivate;
class KWindowShadowTilePrivate;
/**
* The KWindowShadowTile class provides a platform-indendent shadow tile representation.
*/
class KWINDOWSYSTEM_EXPORT KWindowShadowTile
{
public:
using Ptr = QSharedPointer<KWindowShadowTile>;
KWindowShadowTile();
~KWindowShadowTile();
/**
* Returns the image stored in the KWindowShadowTile.
*/
QImage image() const;
/**
* Sets the image on the KWindowShadowTile.
*
* Notice that once the native platform resources have been allocated for the tile, you are
* not allowed to change the image. In order to do so, you need to create a new tile.
*/
void setImage(const QImage &image);
/**
* Returns @c true if the platform resources associated with the tile have been allocated.
*/
bool isCreated() const;
/**
* Allocates the native platform resources associated with the KWindowShadowTile.
*
* Normally it should not be necessary to call this method as KWindowShadow will implicitly
* call create() on your behalf.
*
* Returns @c true if the creation succeeded, otherwise returns @c false.
*/
bool create();
private:
QScopedPointer<KWindowShadowTilePrivate> d;
friend class KWindowShadowTilePrivate;
};
/**
* The KWindowShadow class represents a drop-shadow that is drawn by the compositor.
*
* The KWindowShadow is composed of multiple tiles. The top left tile, the top right tile, the bottom
* left tile, and the bottom right tile are rendered as they are. The top tile and the bottom tile are
* stretched in x direction; the left tile and the right tile are stretched in y direction. Several
* KWindowShadow objects can share shadow tiles to reduce memory usage. You have to specify padding()
* along the shadow tiles. The padding values indicate how much the KWindowShadow sticks outside the
* decorated window.
*
* Once the KWindowShadow is created, you're not allowed to attach or detach any shadow tiles, change
* padding(), or change window(). In order to do so, you have to destroy() the shadow first, update
* relevant properties, and create() the shadow again.
*/
class KWINDOWSYSTEM_EXPORT KWindowShadow : public QObject
{
Q_OBJECT
public:
explicit KWindowShadow(QObject *parent = nullptr);
~KWindowShadow() override;
/**
* Returns the left tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr leftTile() const;
/**
* Attaches the left @p tile to the KWindowShadow.
*/
void setLeftTile(KWindowShadowTile::Ptr tile);
/**
* Returns the top-left tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr topLeftTile() const;
/**
* Attaches the top-left @p tile to the KWindowShadow.
*/
void setTopLeftTile(KWindowShadowTile::Ptr tile);
/**
* Returns the top tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr topTile() const;
/**
* Attaches the top @p tile to the KWindowShadow.
*/
void setTopTile(KWindowShadowTile::Ptr tile);
/**
* Returns the top-right tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr topRightTile() const;
/**
* Attaches the top-right @p tile to the KWindowShadow.
*/
void setTopRightTile(KWindowShadowTile::Ptr tile);
/**
* Returns the right tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr rightTile() const;
/**
* Attaches the right @p tile to the KWindowShadow.
*/
void setRightTile(KWindowShadowTile::Ptr tile);
/**
* Returns the bottom-right tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr bottomRightTile() const;
/**
* Attaches the bottom-right tile to the KWindowShadow.
*/
void setBottomRightTile(KWindowShadowTile::Ptr tile);
/**
* Returns the bottom tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr bottomTile() const;
/**
* Attaches the bottom @p tile to the KWindowShadow.
*/
void setBottomTile(KWindowShadowTile::Ptr tile);
/**
* Returns the bottom-left tile attached to the KWindowShadow.
*/
KWindowShadowTile::Ptr bottomLeftTile() const;
/**
* Attaches the bottom-left @p tile to the KWindowShadow.
*/
void setBottomLeftTile(KWindowShadowTile::Ptr tile);
/**
* Returns the padding of the KWindowShadow.
*
* The padding values specify the visible extents of the shadow. The top left tile is rendered
* with an offset of -padding().left() and -padding().top().
*/
QMargins padding() const;
/**
* Sets the padding on the KWindowShadow.
*
* If the padding values are smaller than the sizes of the shadow tiles, then the shadow will
* overlap with the window() and will be rendered behind window(). E.g. if all padding values
* are set to 0, then the shadow will be completely occluded by the window().
*/
void setPadding(const QMargins &padding);
/**
* Returns the window behind which the KWindowShadow will be rendered.
*/
QWindow *window() const;
/**
* Sets the window behind which the KWindowShadow will be rendered.
*
* Note that the KWindowShadow does not track the platform surface. If for whatever reason the
* native platform surface is deleted and then created, you must to destroy() the shadow and
* create() it again yourself.
*/
void setWindow(QWindow *window);
/**
* Returns @c true if the platform resources associated with the shadow have been allocated.
*/
bool isCreated() const;
/**
* Allocates the platform resources associated with the KWindowShadow.
*
* Once the native platform resources have been allocated, you're not allowed to attach or
* detach shadow tiles, change the padding or the target window. If you want to do so, you
* must destroy() the shadow, change relevant attributes and call create() again.
*
* Returns @c true if the creation succeeded, otherwise returns @c false.
*/
bool create();
/**
* Releases the platform resources associated with the KWindowShadow.
*
* Calling destroy() after window() had been destroyed will result in a no-op.
*/
void destroy();
private:
QScopedPointer<KWindowShadowPrivate> d;
};
#endif // KWINDOWSHADOW_H
@@ -0,0 +1,26 @@
/*
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWSHADOW_DUMMY_P_H
#define KWINDOWSHADOW_DUMMY_P_H
#include "kwindowshadow_p.h"
class KWindowShadowTilePrivateDummy final : public KWindowShadowTilePrivate
{
public:
bool create() override;
void destroy() override;
};
class KWindowShadowPrivateDummy final : public KWindowShadowPrivate
{
public:
bool create() override;
void destroy() override;
};
#endif // KWINDOWSHADOW_DUMMY_P_H
@@ -0,0 +1,62 @@
/*
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWSHADOW_P_H
#define KWINDOWSHADOW_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the KF API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "kwindowshadow.h"
#include <QPointer>
class KWINDOWSYSTEM_EXPORT KWindowShadowTilePrivate
{
public:
virtual ~KWindowShadowTilePrivate();
virtual bool create() = 0;
virtual void destroy() = 0;
static KWindowShadowTilePrivate *get(const KWindowShadowTile *tile);
QImage image;
bool isCreated = false;
};
class KWINDOWSYSTEM_EXPORT KWindowShadowPrivate
{
public:
virtual ~KWindowShadowPrivate();
virtual bool create() = 0;
virtual void destroy() = 0;
bool prepareTiles();
QPointer<QWindow> window;
KWindowShadowTile::Ptr leftTile;
KWindowShadowTile::Ptr topLeftTile;
KWindowShadowTile::Ptr topTile;
KWindowShadowTile::Ptr topRightTile;
KWindowShadowTile::Ptr rightTile;
KWindowShadowTile::Ptr bottomRightTile;
KWindowShadowTile::Ptr bottomTile;
KWindowShadowTile::Ptr bottomLeftTile;
QMargins padding;
bool isCreated = false;
};
#endif // KWINDOWSHADOW_P_H
@@ -0,0 +1,200 @@
/*
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 "kwindowsystem.h"
#include "kwindowsystem_debug.h"
#include "kwindowsystem_dummy_p.h"
#include "pluginwrapper_p.h"
#include <config-kwindowsystem.h>
#if KWINDOWSYSTEM_HAVE_X11
#include "kstartupinfo.h"
#endif
#include <QGuiApplication>
#include <QMetaMethod>
#include <QPixmap>
#include <QPluginLoader>
#include <QTimer>
#include <QWindow>
#if KWINDOWSYSTEM_HAVE_X11
#include <private/qtx11extras_p.h>
#endif
// QPoint and QSize all have handy / operators which are useful for scaling, positions and sizes for high DPI support
// QRect does not, so we create one for internal purposes within this class
inline QRect operator/(const QRect &rectangle, qreal factor)
{
return QRect(rectangle.topLeft() / factor, rectangle.size() / factor);
}
class KWindowSystemStaticContainer
{
public:
KWindowSystemStaticContainer()
{
d.reset(KWindowSystemPluginWrapper::self().createWindowSystem());
if (QCoreApplication::instance()) {
kwm.moveToThread(QCoreApplication::instance()->thread());
}
}
KWindowSystem kwm;
std::unique_ptr<KWindowSystemPrivate> d;
};
Q_GLOBAL_STATIC(KWindowSystemStaticContainer, g_kwmInstanceContainer)
KWindowSystemPrivate::~KWindowSystemPrivate()
{
}
void KWindowSystemPrivateDummy::activateWindow(QWindow *win, long time)
{
Q_UNUSED(win)
Q_UNUSED(time)
}
bool KWindowSystemPrivateDummy::showingDesktop()
{
return false;
}
void KWindowSystemPrivateDummy::setShowingDesktop(bool showing)
{
Q_UNUSED(showing);
}
KWindowSystem *KWindowSystem::self()
{
return &(g_kwmInstanceContainer()->kwm);
}
KWindowSystemPrivate *KWindowSystem::d_func()
{
return g_kwmInstanceContainer()->d.get();
}
void KWindowSystem::activateWindow(QWindow *win, long time)
{
Q_D(KWindowSystem);
d->activateWindow(win, time);
}
void KWindowSystem::setMainWindow(QWindow *subWindow, WId mainWindowId)
{
QWindow *mainWindow = QWindow::fromWinId(mainWindowId);
if (mainWindow) { // foreign windows not supported on all platforms
subWindow->setTransientParent(mainWindow);
// mainWindow is not the child of any object, so make sure it gets deleted at some point
connect(subWindow, &QObject::destroyed, mainWindow, &QObject::deleteLater);
}
}
void KWindowSystem::setMainWindow(QWindow *subWindow, const QString &mainWindowId)
{
Q_D(KWindowSystem);
if (isPlatformWayland()) {
if (auto dv2 = dynamic_cast<KWindowSystemPrivateV2 *>(d)) {
dv2->setMainWindow(subWindow, mainWindowId);
}
} else {
bool ok = false;
// base 0 means "C style" parsing with 0x for base 16, 0b for base 2, etc.
WId wid = mainWindowId.toULongLong(&ok, 0);
if (ok) {
setMainWindow(subWindow, wid);
} else {
qCWarning(LOG_KWINDOWSYSTEM) << "Failed to convert" << mainWindowId << "to WId";
}
}
}
bool KWindowSystem::showingDesktop()
{
Q_D(KWindowSystem);
return d->showingDesktop();
}
void KWindowSystem::setShowingDesktop(bool showing)
{
Q_D(KWindowSystem);
return d->setShowingDesktop(showing);
}
static inline KWindowSystem::Platform initPlatform()
{
auto platformName = QGuiApplication::platformName();
if (platformName == QLatin1String("flatpak")) {
// here we cannot know what is the actual windowing system, let's try it's env variable
const auto flatpakPlatform = QString::fromLocal8Bit(qgetenv("QT_QPA_FLATPAK_PLATFORM"));
if (!flatpakPlatform.isEmpty()) {
platformName = flatpakPlatform;
}
}
#if KWINDOWSYSTEM_HAVE_X11
if (platformName == QLatin1String("xcb")) {
return KWindowSystem::Platform::X11;
}
#endif
if (platformName.startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) {
return KWindowSystem::Platform::Wayland;
}
return KWindowSystem::Platform::Unknown;
}
KWindowSystem::Platform KWindowSystem::platform()
{
static Platform s_platform = initPlatform();
return s_platform;
}
bool KWindowSystem::isPlatformX11()
{
return platform() == Platform::X11;
}
bool KWindowSystem::isPlatformWayland()
{
return platform() == Platform::Wayland;
}
void KWindowSystem::updateStartupId(QWindow *window)
{
// clang-format off
// TODO: move to a new KWindowSystemPrivate interface
#if KWINDOWSYSTEM_HAVE_X11
if (isPlatformX11()) {
const QByteArray startupId = QX11Info::nextStartupId();
if (!startupId.isEmpty()) {
KStartupInfo::setNewStartupId(window, startupId);
}
} else
#else
Q_UNUSED(window);
#endif
if (isPlatformWayland()) {
const QString token = qEnvironmentVariable("XDG_ACTIVATION_TOKEN");
if (!token.isEmpty()) {
setCurrentXdgActivationToken(token);
qunsetenv("XDG_ACTIVATION_TOKEN");
}
}
// clang-format on
}
void KWindowSystem::setCurrentXdgActivationToken(const QString &token)
{
Q_D(KWindowSystem);
auto dv2 = dynamic_cast<KWindowSystemPrivateV2 *>(d);
if (!dv2) {
return;
}
dv2->setCurrentToken(token);
}
#include "moc_kwindowsystem.cpp"
@@ -0,0 +1,196 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
/*
* kwindowsystem.h. Part of the KDE project.
*/
#ifndef KWINDOWSYSTEM_H
#define KWINDOWSYSTEM_H
#include <QObject>
#include <QWidgetList> //For WId
#include <kwindowsystem_export.h>
class KWindowSystemPrivate;
/**
*
* Convenience access to certain properties and features of window systems.
*
*/
class KWINDOWSYSTEM_EXPORT KWindowSystem : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isPlatformWayland READ isPlatformWayland CONSTANT)
Q_PROPERTY(bool isPlatformX11 READ isPlatformX11 CONSTANT)
/**
* @brief Whether "show desktop" is currently active
*/
Q_PROPERTY(bool showingDesktop READ showingDesktop WRITE setShowingDesktop NOTIFY showingDesktopChanged)
public:
/**
* Access to the singleton instance. Useful mainly for connecting to signals.
*/
static KWindowSystem *self();
/**
* Requests that window @p window is activated.
*
* Applications shouldn't make attempts to explicitly activate
* their windows, and instead let the user activate them.
* In the special cases where this may be needed, applications
* can use activateWindow(). The window manager may consider whether
* this request wouldn't result in focus stealing, which
* would be obtrusive, and may refuse the request.
*
* In case of problems, consult KWin's README.md file, or ask on the kwin@kde.org
* mailing list.
*
* @param window the window to make active
* @param time X server timestamp of the user activity that
* caused this request
*
* @since 5.89
*/
Q_INVOKABLE static void activateWindow(QWindow *window, long time = 0);
/**
* Returns the state of showing the desktop.
*/
static bool showingDesktop();
/**
* Sets the state of the "showing desktop" mode of the window manager. If on,
* windows are hidden and desktop background is shown and focused.
*
* @param showing if true, the window manager is put in "showing desktop" mode.
* If false, the window manager is put out of that mode.
*
* @since 5.7.0
*/
static void setShowingDesktop(bool showing);
/**
* Sets the parent window of @p subwindow to be @p mainwindow.
* This overrides the parent set the usual way as the QWidget or QWindow parent,
* but only for the window manager - e.g. stacking order and window grouping
* will be affected, but features like automatic deletion of children
* when the parent is deleted are unaffected and normally use
* the QObject parent.
*
* This function should be used before a dialog is shown for a window
* that belongs to another application.
*
* On Wayland, use the QString overload to provide an XDG Foreign token.
*/
static void setMainWindow(QWindow *subwindow, WId mainwindow);
/**
* Sets the parent window of @p subwindow to be @p mainwindow.
*
* This function should be used before a dialog is shown for a window
* that belongs to another application.
*
* @param window the sub window
* @param mainwindow The main window ID or XDG Foreign token
*
* @since 6.0
*/
static void setMainWindow(QWindow *subwindow, const QString &mainwindow);
/**
* Updates the platform-specific startup id, if any.
*
* This method is to be called when a running application instance
* is reused for handling the request to start this application.
* A typical use would be in the handler of the KDBusService activation signal.
*
* For X11, this updates the id for the Startup Notification protocol,
* taking the id from QX11Info::nextStartupId(), if not empty.
* For Wayland, this updates the token for the XDG Activation protocol,
* taking the token from the "XDG_ACTIVATION_TOKEN" environment variable
* and then unsetting it, if not empty.
*
* @param window the main window (needed by X11 platform)
*
* @since 5.91
*/
static void updateStartupId(QWindow *window);
/**
* Enum describing the windowing system platform used by the QGuiApplication.
* @see platform
* @since 5.25
**/
enum class Platform {
/**
* A platform unknown to the application is used
**/
Unknown,
/**
* The X11 window system.
**/
X11,
/**
* The Wayland window system.
**/
Wayland,
};
Q_ENUM(Platform)
/**
* Returns the Platform used by the QGuiApplication.
* The Platform gets resolved the first time the method is invoked and cached for further
* usages.
* @returns The Platform used by the QGuiApplication.
* @since 5.25
**/
static Platform platform();
/**
* Convenience method to check whether the Platform is X11.
* @see platform
* @see isPlatformWayland
* @since 5.25
**/
static bool isPlatformX11();
/**
* Convenience method to check whether the Platform is Wayland.
* @see platform
* @see isPlatformX11
* @since 5.25
**/
static bool isPlatformWayland();
/**
* Sets the @p token that will be used when activateWindow is called next
*
* @since 5.83
*/
Q_INVOKABLE static void setCurrentXdgActivationToken(const QString &token);
Q_SIGNALS:
/**
* The state of showing the desktop has changed.
*/
void showingDesktopChanged(bool showing);
private:
friend class KWindowSystemStaticContainer;
friend class KX11Extras;
friend class KWaylandExtras;
KWindowSystem()
{
}
static KWindowSystemPrivate *d_func();
};
#endif
@@ -0,0 +1,20 @@
/*
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
*/
#ifndef KWINDOWSYSTEM_DUMMY_P_H
#define KWINDOWSYSTEM_DUMMY_P_H
#include "kwindowsystem_p.h"
class KWindowSystemPrivateDummy : public KWindowSystemPrivate
{
public:
void activateWindow(QWindow *win, long time) override;
bool showingDesktop() override;
void setShowingDesktop(bool showing) override;
};
#endif
@@ -0,0 +1,36 @@
/*
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
*/
#ifndef KWINDOWSYSTEM_P_H
#define KWINDOWSYSTEM_P_H
#include "netwm_def.h"
#include <QStringList>
#include <QWidgetList> //For WId
#include <kwindowsystem_export.h>
class NETWinInfo;
class KWINDOWSYSTEM_EXPORT KWindowSystemPrivate : public NET
{
public:
virtual ~KWindowSystemPrivate();
virtual void activateWindow(QWindow *win, long time = 0) = 0;
virtual bool showingDesktop() = 0;
virtual void setShowingDesktop(bool showing) = 0;
};
class KWINDOWSYSTEM_EXPORT KWindowSystemPrivateV2 : public KWindowSystemPrivate
{
public:
virtual void requestToken(QWindow *win, uint32_t serial, const QString &app_id) = 0;
virtual void setCurrentToken(const QString &token) = 0;
virtual quint32 lastInputSerial(QWindow *window) = 0;
virtual void setMainWindow(QWindow *window, const QString &handle) = 0;
virtual void exportWindow(QWindow *window) = 0;
virtual void unexportWindow(QWindow *window) = 0;
};
#endif
@@ -0,0 +1,38 @@
/*
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 "kwindowsystemplugininterface_p.h"
KWindowSystemPluginInterface::KWindowSystemPluginInterface(QObject *parent)
: QObject(parent)
{
}
KWindowSystemPluginInterface::~KWindowSystemPluginInterface()
{
}
KWindowEffectsPrivate *KWindowSystemPluginInterface::createEffects()
{
return nullptr;
}
KWindowSystemPrivate *KWindowSystemPluginInterface::createWindowSystem()
{
return nullptr;
}
KWindowShadowPrivate *KWindowSystemPluginInterface::createWindowShadow()
{
return nullptr;
}
KWindowShadowTilePrivate *KWindowSystemPluginInterface::createWindowShadowTile()
{
return nullptr;
}
#include "moc_kwindowsystemplugininterface_p.cpp"
@@ -0,0 +1,35 @@
/*
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
*/
#ifndef KWINDOWSYSTEMPLUGININTERFACE_P_H
#define KWINDOWSYSTEMPLUGININTERFACE_P_H
#include <kwindowsystem_export.h>
#include <QObject>
#include <QWidgetList> //For WId
class KWindowEffectsPrivate;
class KWindowShadowPrivate;
class KWindowShadowTilePrivate;
class KWindowSystemPrivate;
#define KWindowSystemPluginInterface_iid "org.kde.kwindowsystem.KWindowSystemPluginInterface"
class KWINDOWSYSTEM_EXPORT KWindowSystemPluginInterface : public QObject
{
Q_OBJECT
public:
explicit KWindowSystemPluginInterface(QObject *parent = nullptr);
~KWindowSystemPluginInterface() override;
virtual KWindowEffectsPrivate *createEffects();
virtual KWindowSystemPrivate *createWindowSystem();
virtual KWindowShadowPrivate *createWindowShadow();
virtual KWindowShadowTilePrivate *createWindowShadowTile();
};
Q_DECLARE_INTERFACE(KWindowSystemPluginInterface, KWindowSystemPluginInterface_iid)
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,530 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KX11EXTRAS_H
#define KX11EXTRAS_H
#include <QObject>
#include <QWindow>
#include <kwindowsystem_export.h>
#include "netwm_def.h"
class NETWinInfo;
class NETEventFilter;
/**
* A collection of functions to obtain information from and manipulate
* X11 windows. These are generally not applicable to other window systems
*
* @since 5.101
*/
class KWINDOWSYSTEM_EXPORT KX11Extras : public QObject
{
Q_OBJECT
/**
* @brief Whether desktop compositing is active
*/
Q_PROPERTY(bool compositingActive READ compositingActive NOTIFY compositingChanged)
public:
static KX11Extras *self();
/**
* Returns the list of all toplevel windows currently managed by the
* window manager in the order of creation. Please do not rely on
* indexes of this list: Whenever you enter Qt's event loop in your
* application, it may happen that entries are removed or added.
* Your module should perhaps work on a copy of this list and verify a
* window with hasWId() before any operations.
*
* Iteration over this list can be done easily with
* \code
* QList<WId> windows = KWindowSystem::windows();
* for (auto it = windows.cbegin(), end = windows.cend(); it != end; ++it) {
* ... do something here, (*it) is the current WId.
* }
* \endcode
* @return the list of all toplevel windows
*/
static QList<WId> windows();
/**
* Test to see if @p id still managed at present.
* @param id the window id to test
* @return true if the window id is still managed
**/
static bool hasWId(WId id);
/**
* Returns the list of all toplevel windows currently managed by the
* window manager in the current stacking order (from lower to
* higher). May be useful for pagers.
* @return the list of all toplevel windows in stacking order
*/
static QList<WId> stackingOrder();
/**
* Returns the currently active window, or 0 if no window is active.
* @return the window id of the active window, or 0 if no window is
* active
**/
static WId activeWindow();
/**
* Requests that window @p win is activated.
*
* There are two ways how to activate a window, by calling
* activateWindow() and forceActiveWindow(). Generally,
* applications shouldn't make attempts to explicitly activate
* their windows, and instead let the user to activate them.
* In the special cases where this may be needed, applications
* should use activateWindow(). Window manager may consider whether
* this request wouldn't result in focus stealing, which
* would be obtrusive, and may refuse the request.
*
* The usage of forceActiveWindow() is meant only for pagers
* and similar tools, which represent direct user actions
* related to window manipulation.
* Except for rare cases, this request will be always honored,
* and normal applications are forbidden to use it.
*
* In case of problems, consult the KWin README in the kdebase
* package (kdebase/kwin/README), or ask on the kwin@kde.org
* mailing list.
*
* @param win the id of the window to make active
* @param time X server timestamp of the user activity that
* caused this request
*/
static void activateWindow(WId win, long time = 0);
/**
* Sets window @p win to be the active window. Note that this
* should be called only in special cases, applications
* shouldn't force themselves or other windows to be the active
* window. Generally, this call should used only by pagers
* and similar tools. See the explanation in description
* of activateWindow().
*
* @param win the id of the window to make active
* @param time X server timestamp of the user activity that
* caused this request
*/
static void forceActiveWindow(WId win, long time = 0);
/**
* Sets window @p win to be the active window. Note that this
* should be called only in special cases, applications
* shouldn't force themselves or other windows to be the active
* window. Generally, this call should used only by pagers
* and similar tools. See the explanation in the description
* of activateWindow().
*
* @param win the window to make active
* @param time X server timestamp of the user activity that
* caused this request
* @since 6.0
*/
Q_INVOKABLE static void forceActiveWindow(QWindow *window, long time = 0);
/**
* Returns true if a compositing manager is running (i.e. ARGB windows
* are supported, effects will be provided, etc.).
*/
static bool compositingActive();
/**
* Returns the current virtual desktop.
* @return the current virtual desktop
**/
static int currentDesktop();
/**
* Returns the number of virtual desktops.
* @return the number of virtual desktops
**/
static int numberOfDesktops();
/**
* Convenience function to set the current desktop to @p desktop.
* See NETRootInfo.
* @param desktop the number of the new desktop
*/
static void setCurrentDesktop(int desktop);
/**
* Sets window @p win to be present on all virtual desktops if @p
* is true. Otherwise the window lives only on one single desktop.
*
* @param win the id of the window
* @param b true to show the window on all desktops, false
* otherwise
*/
static void setOnAllDesktops(WId win, bool b);
/**
* Moves window @p win to desktop @p desktop.
*
* @param win the id of the window
* @param desktop the number of the new desktop
*/
static void setOnDesktop(WId win, int desktop);
/**
* Moves window @p win to activities @p activities.
*
* @param win the id of the window
* @param activities the list of activity UUIDs
*
* @see KWindowInfo::activities
*/
static void setOnActivities(WId win, const QStringList &activities);
/**
* Returns an icon for window @p win.
*
* If @p width and @p height are specified, the best icon for the requested
* size is returned.
*
* If @p scale is true, the icon is smooth-scaled to have exactly
* the requested size.
*
* @param win the id of the window
* @param width the desired width, or -1
* @param height the desired height, or -1
* @param scale if true the icon will be scaled to the desired size. Otherwise the
* icon will not be modified.
* @return the icon of the window
*/
static QPixmap icon(WId win, int width = -1, int height = -1, bool scale = false);
/**
* Masks specifying from which sources to read an icon. They are tried from the best
* until an icon is found.
* @li NETWM from property from the window manager specification
* @li WMHints from WMHints property
* @li ClassHint load icon after getting name from the classhint
* @li XApp load the standard X icon (last fallback)
*/
enum IconSource {
NETWM = 1, //!< read from property from the window manager specification
WMHints = 2, //!< read from WMHints property
ClassHint = 4, //!< load icon after getting name from the classhint
XApp = 8, //!< load the standard X icon (last fallback)
};
/**
* @overload
*
* Overloaded variant that allows specifying from which sources the icon should be read.
* You should usually prefer the simpler variant which tries all possibilities to get
* an icon.
*
* @param win the id of the window
* @param width the desired width, or -1
* @param height the desired height, or -1
* @param scale if true the icon will be scaled to the desired size. Otherwise the
* icon will not be modified.
* @param flags OR-ed flags from the IconSource enum
*/
static QPixmap icon(WId win, int width, int height, bool scale, int flags);
/**
* @overload
*
* Overloaded variant that allows passing in the NETWinInfo to use for reading the
* information. This variant is only useful on the X11 platform, other platforms do not
* use NETWinInfo and delegate to the variant without NETWinInfo. Though if compiled with
* X11 support the X11 variant is used on other platforms if info is not @c nullptr.
* This can be used by applications using e.g. platform wayland but also connecting to an
* XServer.
*
* The NETWinInfo must be constructed with property NET::WMIcon in order to use the
* IconSource flag NETWM. NET::WM2IconPixmap for IconSource flag WMHints and
* NET::WM2WindowClass for IconSource flag ClassHint.
*
* @param win the id of the window
* @param width the desired width, or -1
* @param height the desired height, or -1
* @param scale if true the icon will be scaled to the desired size. Otherwise the
* icon will not be modified.
* @param flags OR-ed flags from the IconSource enum
* @param into the NETWinInfo to use for reading properties.
**/
static QPixmap icon(WId win, int width, int height, bool scale, int flags, NETWinInfo *info);
/**
* Minimizes the window with id @p win.
* On X11 this follows the protocol described in ICCCM section 4.1.4.
*
* @param win The window to minimize
* @see unminimizeWindow()
*/
static void minimizeWindow(WId win);
/**
* Unminimizes the window with id @p win.
* On X11 this follows the protocol described in ICCCM section 4.1.4.
*
* @param win The window to unminimize
* @see minimizeWindow()
**/
static void unminimizeWindow(WId win);
/**
* Returns the workarea for the specified desktop, or the current
* work area if no desktop has been specified.
* @param desktop the number of the desktop to check, -1 for the
* current desktop
* @return the size and position of the desktop
**/
static QRect workArea(int desktop = -1);
/**
* Returns the workarea for the specified desktop, or the current
* work area if no desktop has been specified. Excludes struts of
* clients in the exclude List.
*
* @param excludes the list of clients whose struts will be excluded
* @param desktop the number of the desktop to check, -1 for the
* current desktop
* @return the size and position of the desktop
**/
static QRect workArea(const QList<WId> &excludes, int desktop = -1);
/**
* Returns the name of the specified desktop.
* @param desktop the number of the desktop
* @return the name of the desktop
**/
static QString desktopName(int desktop);
/**
* Sets the name of the specified desktop.
* @param desktop the number of the desktop
* @param name the new name for the desktop
**/
static void setDesktopName(int desktop, const QString &name);
/**
* Function that reads and returns the contents of the given text
* property (WM_NAME, WM_ICON_NAME,...).
*/
static QString readNameProperty(WId window, unsigned long atom);
/**
* Returns true if viewports are mapped to virtual desktops.
*/
static bool mapViewport();
/**
* Sets the strut of window @p win to @p left_width
* ranging from @p left_start to @p left_end on the left edge,
* and simiarly for the other edges. For not reserving a strut, pass 0 as the width.
* E.g. to reserve 10x10 square in the topleft corner, use e.g.
* setExtendedStrut( w, 10, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0 ).
*
* @param win the id of the window
* @param left_width width of the strut at the left edge
* @param left_start starting y coordinate of the strut at the left edge
* @param left_end ending y coordinate of the strut at the left edge
* @param right_width width of the strut at the right edge
* @param right_start starting y coordinate of the strut at the right edge
* @param right_end ending y coordinate of the strut at the right edge
* @param top_width width of the strut at the top edge
* @param top_start starting x coordinate of the strut at the top edge
* @param top_end ending x coordinate of the strut at the top edge
* @param bottom_width width of the strut at the bottom edge
* @param bottom_start starting x coordinate of the strut at the bottom edge
* @param bottom_end ending x coordinate of the strut at the bottom edge
*/
static void setExtendedStrut(WId win,
qreal left_width,
qreal left_start,
qreal left_end,
qreal right_width,
qreal right_start,
qreal right_end,
qreal top_width,
qreal top_start,
qreal top_end,
qreal bottom_width,
qreal bottom_start,
qreal bottom_end);
/**
* Convenience function for setExtendedStrut() that automatically makes struts
* as wide/high as the screen width/height.
* Sets the strut of window @p win to @p left, @p right, @p top, @p bottom.
*
* @param win the id of the window
* @param left the left strut
* @param right the right strut
* @param top the top strut
* @param bottom the bottom strut
*/
static void setStrut(WId win, qreal left, qreal right, qreal top, qreal bottom);
/**
* Sets the type of window @p win to @p windowType.
*
* @param win the id of the window
* @param windowType the type of the window (see NET::WindowType)
*
* @since 6.0
*/
static void setType(WId win, NET::WindowType windowType);
/**
* Clears the state of window @p win from @p state.
*
* Possible values are or'ed combinations of NET::Modal,
* NET::Sticky, NET::MaxVert, NET::MaxHoriz, NET::Shaded,
* NET::SkipTaskbar, NET::SkipPager, NET::Hidden,
* NET::FullScreen, NET::KeepAbove, NET::KeepBelow,
* NET::SkipSwitcher
*
* @param win the id of the window
* @param state the flags that will be cleared
*
* @since 6.0
*/
static void clearState(WId win, NET::States state);
/**
* Sets the state of window @p win to @p state.
*
* Possible values are or'ed combinations of NET::Modal,
* NET::Sticky, NET::MaxVert, NET::MaxHoriz, NET::Shaded,
* NET::SkipTaskbar, NET::SkipPager, NET::Hidden,
* NET::FullScreen, NET::KeepAbove, NET::KeepBelow,
* NET::SkipSwitcher
*
* @param win the id of the window
* @param state the new flags that will be set
*
* @since 6.0
*/
static void setState(WId win, NET::States state);
Q_SIGNALS:
/**
* Switched to another virtual desktop.
* @param desktop the number of the new desktop
*/
void currentDesktopChanged(int desktop);
/**
* A window has been added.
* @param id the id of the window
*/
void windowAdded(WId id);
/**
* A window has been removed.
* @param id the id of the window that has been removed
*/
void windowRemoved(WId id);
/**
* Hint that \<Window> is active (= has focus) now.
* @param id the id of the window that is active
*/
void activeWindowChanged(WId id);
/**
* Desktops have been renamed.
*/
void desktopNamesChanged();
/**
* The number of desktops changed.
* @param num the new number of desktops
*/
void numberOfDesktopsChanged(int num);
/**
* The workarea has changed.
*/
void workAreaChanged();
/**
* Something changed with the struts, may or may not have changed
* the work area. Usually just using the workAreaChanged() signal
* is sufficient.
*/
void strutChanged();
/**
* Emitted when the stacking order of the window changed. The new order
* can be obtained with stackingOrder().
*/
void stackingOrderChanged();
/**
* The window changed.
*
* Carries the NET::Properties and NET::Properties2 that were changed.
*
* @param id the id of the window
* @param properties the properties that were modified
* @param properties2 the properties2 that were modified
*/
void windowChanged(WId id, NET::Properties properties, NET::Properties2 properties2);
/**
* Compositing was enabled or disabled.
*
* Note that this signal may be emitted before any compositing plugins
* have been initialized in the window manager.
*
* If you need to check if a specific compositing plugin such as the
* blur effect is enabled, you should track that separately rather
* than test for it in a slot connected to this signal.
*/
void compositingChanged(bool enabled);
protected:
void connectNotify(const QMetaMethod &signal) override;
private:
friend class KWindowInfo;
friend class KWindowSystemPrivateX11;
friend class NETEventFilter;
friend class MainThreadInstantiator;
enum FilterInfo {
INFO_BASIC = 1, // desktop info, not per-window
INFO_WINDOWS = 2, // also per-window info
};
KWINDOWSYSTEM_NO_EXPORT void init(FilterInfo info);
KWINDOWSYSTEM_NO_EXPORT QPoint desktopToViewport(int desktop, bool absolute);
KWINDOWSYSTEM_NO_EXPORT int viewportToDesktop(const QPoint &pos);
KWINDOWSYSTEM_NO_EXPORT QPoint constrainViewportRelativePosition(const QPoint &pos);
// used in xcb/kwindowsystem.cpp
static bool showingDesktop();
static void setShowingDesktop(bool showing);
/**
* @internal
* Returns mapped virtual desktop for the given window geometry.
*/
KWINDOWSYSTEM_NO_EXPORT static int viewportWindowToDesktop(const QRect &r);
KWINDOWSYSTEM_NO_EXPORT NETEventFilter *s_d_func()
{
return d.get();
}
std::unique_ptr<NETEventFilter> d;
};
#endif
@@ -0,0 +1,57 @@
/*
SPDX-FileCopyrightText: 2024 Andreas Hartmetz <ahartmetz@gmail.com>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <string.h>
/**
* KXcbEvent allocates an xcb event in a 32 bytes large, zero-initialized buffer to avoid
* out-of-bounds reads and uninitialized memory reads in xcb_send_event(). According to
* XCB documentation, the wire size of all XCB events is 32 bytes, and that many bytes will
* be read by xcb_send_event().
*/
template<class EventType, bool needsPadding = (sizeof(EventType) < 32)>
struct KXcbEvent;
template<class EventType>
class KXcbEvent<EventType, false> : public EventType
{
public:
inline KXcbEvent()
{
static_assert(sizeof(*this) == s_evtSize);
memset(this, 0, s_evtSize);
}
inline const char *buffer() const
{
return reinterpret_cast<const char *>(this);
}
private:
static constexpr size_t s_evtSize = 32;
};
template<class EventType>
class KXcbEvent<EventType, true> : public EventType
{
public:
inline KXcbEvent()
{
static_assert(sizeof(*this) == s_evtSize);
memset(this, 0, s_evtSize);
}
inline const char *buffer() const
{
return reinterpret_cast<const char *>(this);
}
private:
static constexpr size_t s_evtSize = 32;
char m_filler[s_evtSize - sizeof(EventType)];
};
@@ -0,0 +1,234 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#include "kxerrorhandler_p.h"
#include <config-kwindowsystem.h>
#include <fixx11h.h>
#include "netwm_def.h"
#include <stdio.h>
#include <QByteArray>
#include <QString>
class KXErrorHandlerPrivate
{
public:
KXErrorHandlerPrivate(Display *dpy)
: first_request(XNextRequest(dpy))
, display(dpy)
, was_error(false)
{
}
unsigned long first_request;
Display *display;
bool was_error;
XErrorEvent error_event;
};
KXErrorHandler **KXErrorHandler::handlers = nullptr;
int KXErrorHandler::pos = 0;
int KXErrorHandler::size = 0;
KXErrorHandler::KXErrorHandler(Display *dpy)
: user_handler1(nullptr)
, user_handler2(nullptr)
, old_handler(XSetErrorHandler(handler_wrapper))
, d(new KXErrorHandlerPrivate(dpy))
{
addHandler();
}
KXErrorHandler::KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy)
: user_handler1(nullptr)
, user_handler2(handler)
, old_handler(XSetErrorHandler(handler_wrapper))
, d(new KXErrorHandlerPrivate(dpy))
{
addHandler();
}
KXErrorHandler::~KXErrorHandler()
{
XSetErrorHandler(old_handler);
Q_ASSERT_X(this == handlers[pos - 1], "KXErrorHandler", "out of order");
--pos;
delete d;
}
void KXErrorHandler::addHandler()
{
if (size == pos) {
size += 16;
handlers = static_cast<KXErrorHandler **>(realloc(handlers, size * sizeof(KXErrorHandler *)));
}
handlers[pos++] = this;
}
bool KXErrorHandler::error(bool sync) const
{
if (sync) {
XSync(d->display, False);
}
return d->was_error;
}
XErrorEvent KXErrorHandler::errorEvent() const
{
return d->error_event;
}
int KXErrorHandler::handler_wrapper(Display *dpy, XErrorEvent *e)
{
--pos;
int ret = handlers[pos]->handle(dpy, e);
++pos;
return ret;
}
int KXErrorHandler::handle(Display *dpy, XErrorEvent *e)
{
if (dpy == d->display
// e->serial >= d->first_request , compare like X timestamps to handle wrapping
&& NET::timestampCompare(e->serial, d->first_request) >= 0) {
// it's for us
// qDebug( "Handling: %p", static_cast< void* >( this ));
bool error = false;
if (user_handler1 != nullptr) {
if (user_handler1(e->request_code, e->error_code, e->resourceid)) {
error = true;
}
} else if (user_handler2 != nullptr) {
if (user_handler2(dpy, e) != 0) {
error = true;
}
} else { // no handler set, simply set that there was an error
error = true;
}
if (error && !d->was_error) {
// only remember the first
d->was_error = true;
d->error_event = *e;
}
return 0;
}
// qDebug( "Going deeper: %p", static_cast< void* >( this ));
return old_handler(dpy, e);
}
QByteArray KXErrorHandler::errorMessage(const XErrorEvent &event, Display *dpy)
{
// "Error: <error> (<value>), Request: <request>(<value>), Resource: <value>"
QByteArray ret;
char tmp[256];
#if 0 // see below
char num[ 256 ];
if (event.request_code < 128) // core request
#endif
{
XGetErrorText(dpy, event.error_code, tmp, 255);
if (char *paren = strchr(tmp, '(')) { // the explanation in parentheses just makes
*paren = '\0'; // it more verbose and is not really useful
}
// the various casts are to get overloads non-ambiguous :-/
/*
ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
sprintf(num, "%d", event.request_code);
XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 256);
ret += QByteArray(", request: ") + (const char *)tmp + '[' + QByteArray::number(event.request_code) + ']';
if (event.resourceid != 0) {
ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
}
*/
}
#if 0
else { // extensions
// XGetErrorText() currently has a bug that makes it fail to find text
// for some errors (when error==error_base), also XGetErrorDatabaseText()
// requires the right extension name, so it is needed to get info about
// all extensions. However that is almost impossible:
// - Xlib itself has it, but in internal data.
// - Opening another X connection now can cause deadlock with server grabs.
// - Fetching it at startup means a bunch of roundtrips.
// So if this becomes more useful in the future, do the roundtrips at startup,
// or fetch it in kded and export as an env.var or something.
Display *dpy2 = XOpenDisplay(XDisplayString(dpy));
int nextensions;
char **extensions = XListExtensions(dpy2, &nextensions);
int *majors = nullptr;
int *error_bases = nullptr;
if (extensions == nullptr) {
nextensions = 0;
} else {
majors = new int[ nextensions ];
error_bases = new int[ nextensions ];
for (int i = 0;
i < nextensions;
++i) {
int dummy;
if (!XQueryExtension(dpy2, extensions[ i ], &majors[ i ], &dummy, &error_bases[ i ])) {
majors[ i ] = 0;
error_bases[ i ] = 0;
}
}
}
XGetErrorText(dpy, event.error_code, tmp, 255);
int index = -1;
int base = 0;
for (int i = 0;
i < nextensions;
++i)
if (error_bases[ i ] != 0
&& event.error_code >= error_bases[ i ] && (index == -1 || error_bases[ i ] > base)) {
index = i;
base = error_bases[ i ];
}
if (tmp == QString::number(event.error_code)) { // XGetErrorText() failed,
// or it has a bug that causes not finding all errors, check ourselves
if (index != -1) {
qsnprintf(num, 255, "%s.%d", extensions[ index ], event.error_code - base);
XGetErrorDatabaseText(dpy, "XProtoError", num, "<unknown>", tmp, 255);
} else {
strcpy(tmp, "<unknown>");
}
}
if (char *paren = strchr(tmp, '(')) {
*paren = '\0';
}
if (index != -1)
ret = QByteArray("error: ") + (const char *)tmp + '[' + (const char *)extensions[ index ]
+ '+' + QByteArray::number(event.error_code - base) + ']';
else {
ret = QByteArray("error: ") + (const char *)tmp + '[' + QByteArray::number(event.error_code) + ']';
}
tmp[ 0 ] = '\0';
for (int i = 0;
i < nextensions;
++i)
if (majors[ i ] == event.request_code) {
qsnprintf(num, 255, "%s.%d", extensions[ i ], event.minor_code);
XGetErrorDatabaseText(dpy, "XRequest", num, "<unknown>", tmp, 255);
ret += QByteArray(", request: ") + (const char *)tmp + '[' + (const char *)extensions[ i ] + '+'
+ QByteArray::number(event.minor_code) + ']';
}
if (tmp[ 0 ] == '\0') // not found???
ret += QByteArray(", request <unknown> [") + QByteArray::number(event.request_code) + ':'
+ QByteArray::number(event.minor_code) + ']';
if (event.resourceid != 0) {
ret += QByteArray(", resource: 0x") + QByteArray::number((qlonglong)event.resourceid, 16);
}
if (extensions != nullptr) {
XFreeExtensionList(extensions);
}
delete[] majors;
delete[] error_bases;
XCloseDisplay(dpy2);
}
#endif
return ret;
}
@@ -0,0 +1,913 @@
/*
SPDX-FileCopyrightText: 2000 Troll Tech AS
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef netwm_def_h
#define netwm_def_h
#include <QFlags>
#include <QRect>
#include <kwindowsystem_export.h>
/**
Simple point class for NET classes.
This class is a convenience class defining a point x, y. The existence of
this class is to keep the implementation from being dependent on a
separate framework/library.
NETPoint is only used by the NET API. Usually QPoint is the
appropriate class for representing a point.
@author Bradley T. Hughes <bhughes@trolltech.com>
**/
struct NETPoint {
/**
Constructor to initialize this point to 0,0.
**/
NETPoint()
: x(0)
, y(0)
{
}
NETPoint(const QPoint &p)
: x(p.x())
, y(p.y())
{
}
QPoint toPoint() const
{
return {x, y};
}
/*
Public data member.
**/
int x, ///< x coordinate.
y; ///< y coordinate
};
/**
Simple size class for NET classes.
This class is a convenience class defining a size width by height. The
existence of this class is to keep the implementation from being dependent
on a separate framework/library.
NETSize is only used by the NET API. Usually QSize is the
appropriate class for representing a size.
@author Bradley T. Hughes <bhughes@trolltech.com>
**/
struct NETSize {
/**
Constructor to initialize this size to 0x0
**/
NETSize()
: width(0)
, height(0)
{
}
NETSize(const QSize &size)
: width(size.width())
, height(size.height())
{
}
QSize toSize() const
{
return {width, height};
}
/*
Public data member.
**/
int width; ///< Width.
int height; ///< Height.
};
/**
Simple rectangle class for NET classes.
This class is a convenience class defining a rectangle as a point x,y with a
size width by height. The existence of this class is to keep the implementation
from being dependent on a separate framework/library;
NETRect is only used by the NET API. Usually QRect is the
appropriate class for representing a rectangle.
**/
struct NETRect {
NETRect()
{
}
NETRect(const QRect &rect)
: pos(rect.topLeft())
, size(rect.size())
{
}
QRect toRect() const
{
return QRect(pos.x, pos.y, size.width, size.height);
}
/**
Position of the rectangle.
@see NETPoint
**/
NETPoint pos;
/**
Size of the rectangle.
@see NETSize
**/
NETSize size;
};
/**
Simple icon class for NET classes.
This class is a convenience class defining an icon of size width by height.
The existence of this class is to keep the implementation from being
dependent on a separate framework/library.
NETIcon is only used by the NET API. Usually QIcon is the
appropriate class for representing an icon.
**/
struct NETIcon {
/**
Constructor to initialize this icon to 0x0 with data=0
**/
NETIcon()
: data(nullptr)
{
}
/**
Size of the icon.
@see NETSize
**/
NETSize size;
/**
Image data for the icon. This is an array of 32bit packed CARDINAL ARGB
with high byte being A, low byte being B. First two bytes are width, height.
Data is in rows, left to right and top to bottom.
**/
unsigned char *data;
};
/**
Partial strut class for NET classes.
This class is a convenience class defining a strut with left, right, top and
bottom border values, and ranges for them. The existence of this class is to
keep the implementation from being dependent on a separate framework/library.
See the _NET_WM_STRUT_PARTIAL property in the NETWM spec.
**/
struct NETExtendedStrut {
/**
Constructor to initialize this struct to 0,0,0,0
**/
NETExtendedStrut()
: left_width(0)
, left_start(0)
, left_end(0)
, right_width(0)
, right_start(0)
, right_end(0)
, top_width(0)
, top_start(0)
, top_end(0)
, bottom_width(0)
, bottom_start(0)
, bottom_end(0)
{
}
/**
Left border of the strut, width and range.
**/
int left_width, left_start, left_end;
/**
Right border of the strut, width and range.
**/
int right_width, right_start, right_end;
/**
Top border of the strut, width and range.
**/
int top_width, top_start, top_end;
/**
Bottom border of the strut, width and range.
**/
int bottom_width, bottom_start, bottom_end;
};
/**
@deprecated use NETExtendedStrut
Simple strut class for NET classes.
This class is a convenience class defining a strut with left, right, top and
bottom border values. The existence of this class is to keep the implementation
from being dependent on a separate framework/library. See the _NET_WM_STRUT
property in the NETWM spec.
**/
struct NETStrut {
/**
Constructor to initialize this struct to 0,0,0,0
**/
NETStrut()
: left(0)
, right(0)
, top(0)
, bottom(0)
{
}
/**
Left border of the strut.
**/
int left;
/**
Right border of the strut.
**/
int right;
/**
Top border of the strut.
**/
int top;
/**
Bottom border of the strut.
**/
int bottom;
};
/**
Simple multiple monitor topology class for NET classes.
This class is a convenience class, defining a multiple monitor topology
for fullscreen applications that wish to be present on more than one
monitor/head. As per the _NET_WM_FULLSCREEN_MONITORS hint in the EWMH spec,
this topology consists of 4 monitor indices such that the bounding rectangle
is defined by the top edge of the top monitor, the bottom edge of the bottom
monitor, the left edge of the left monitor, and the right edge of the right
monitor. See the _NET_WM_FULLSCREEN_MONITORS hint in the EWMH spec.
**/
struct NETFullscreenMonitors {
/**
Constructor to initialize this struct to -1,0,0,0 (an initialized,
albeit invalid, topology).
**/
NETFullscreenMonitors()
: top(-1)
, bottom(0)
, left(0)
, right(0)
{
}
/**
Monitor index whose top border defines the top edge of the topology.
**/
int top;
/**
Monitor index whose bottom border defines the bottom edge of the topology.
**/
int bottom;
/**
Monitor index whose left border defines the left edge of the topology.
**/
int left;
/**
Monitor index whose right border defines the right edge of the topology.
**/
int right;
/**
Convenience check to make sure that we are not holding the initial (invalid)
values. Note that we don't want to call this isValid() because we're not
actually validating the monitor topology here, but merely that our initial
values were overwritten at some point by real (non-negative) monitor indices.
**/
bool isSet() const
{
return (top != -1);
}
};
/**
Base namespace class.
The NET API is an implementation of the NET Window Manager Specification.
This class is the base class for the NETRootInfo and NETWinInfo classes, which
are used to retrieve and modify the properties of windows. To keep
the namespace relatively clean, all enums are defined here.
@see https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
**/
class KWINDOWSYSTEM_EXPORT NET
{
public:
/**
Application role. This is used internally to determine how several action
should be performed (if at all).
**/
enum Role {
/**
indicates that the application is a client application.
**/
Client,
/**
indicates that the application is a window manager application.
**/
WindowManager,
};
/**
Window type.
**/
enum WindowType {
/**
indicates that the window did not define a window type.
**/
Unknown = -1,
/**
indicates that this is a normal, top-level window
**/
Normal = 0,
/**
indicates a desktop feature. This can include a single window
containing desktop icons with the same dimensions as the screen, allowing
the desktop environment to have full control of the desktop, without the
need for proxying root window clicks.
**/
Desktop = 1,
/**
indicates a dock or panel feature
**/
Dock = 2,
/**
indicates a toolbar window
**/
Toolbar = 3,
/**
indicates a pinnable (torn-off) menu window
**/
Menu = 4,
/**
indicates that this is a dialog window
**/
Dialog = 5,
// cannot deprecate to compiler: used both by clients & manager, later needs to keep supporting it for now
// KF6: remove
/**
@deprecated has unclear meaning and is KDE-only
**/
Override = 6, // NON STANDARD
/**
indicates a toplevel menu (AKA macmenu). This is a KDE extension to the
_NET_WM_WINDOW_TYPE mechanism.
**/
TopMenu = 7, // NON STANDARD
/**
indicates a utility window
**/
Utility = 8,
/**
indicates that this window is a splash screen window.
**/
Splash = 9,
/**
indicates a dropdown menu (from a menubar typically)
**/
DropdownMenu = 10,
/**
indicates a popup menu (a context menu typically)
**/
PopupMenu = 11,
/**
indicates a tooltip window
**/
Tooltip = 12,
/**
indicates a notification window
**/
Notification = 13,
/**
indicates that the window is a list for a combobox
**/
ComboBox = 14,
/**
indicates a window that represents the dragged object during DND operation
**/
DNDIcon = 15,
/**
indicates an On Screen Display window (such as volume feedback)
@since 5.6
**/
OnScreenDisplay = 16, // NON STANDARD
/**
indicates a critical notification (such as battery is running out)
@since 5.58
**/
CriticalNotification = 17, // NON STANDARD
/**
* indicates that this window is an applet.
*/
AppletPopup = 18, // NON STANDARD
};
/**
Values for WindowType when they should be OR'ed together, e.g.
for the properties argument of the NETRootInfo constructor.
@see WindowTypes
**/
enum WindowTypeMask {
NormalMask = 1u << 0, ///< @see Normal
DesktopMask = 1u << 1, ///< @see Desktop
DockMask = 1u << 2, ///< @see Dock
ToolbarMask = 1u << 3, ///< @see Toolbar
MenuMask = 1u << 4, ///< @see Menu
DialogMask = 1u << 5, ///< @see Dialog
OverrideMask = 1u << 6, ///< @see Override
TopMenuMask = 1u << 7, ///< @see TopMenu
UtilityMask = 1u << 8, ///< @see Utility
SplashMask = 1u << 9, ///< @see Splash
DropdownMenuMask = 1u << 10, ///< @see DropdownMenu
PopupMenuMask = 1u << 11, ///< @see PopupMenu
TooltipMask = 1u << 12, ///< @see Tooltip
NotificationMask = 1u << 13, ///< @see Notification
ComboBoxMask = 1u << 14, ///< @see ComboBox
DNDIconMask = 1u << 15, ///< @see DNDIcon
OnScreenDisplayMask = 1u << 16, ///< NON STANDARD @see OnScreenDisplay @since 5.6
CriticalNotificationMask = 1u << 17, ///< NON STANDARD @see CriticalNotification @since 5.58
AppletPopupMask = 1u << 18, ///< NON STANDARD @see AppletPopup
AllTypesMask = 0U - 1, ///< All window types.
};
/**
* Stores a combination of #WindowTypeMask values.
*/
Q_DECLARE_FLAGS(WindowTypes, WindowTypeMask)
/**
* Returns true if the given window type matches the mask given
* using WindowTypeMask flags.
*/
static bool typeMatchesMask(WindowType type, WindowTypes mask);
/**
Window state.
To set the state of a window, you'll typically do something like:
\code
KX11Extras::setState( winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher );
\endcode
for example to not show the window on the taskbar, desktop pager, or window switcher.
winId() is a function of QWidget()
Note that KeepAbove (StaysOnTop) and KeepBelow are meant as user preference and
applications should avoid setting these states themselves.
@see States
**/
enum State {
/**
indicates that this is a modal dialog box. The WM_TRANSIENT_FOR hint
MUST be set to indicate which window the dialog is a modal for, or set to
the root window if the dialog is a modal for its window group.
**/
Modal = 1u << 0,
/**
indicates that the Window Manager SHOULD keep the window's position
fixed on the screen, even when the virtual desktop scrolls. Note that this is
different from being kept on all desktops.
**/
Sticky = 1u << 1,
/**
indicates that the window is vertically maximized.
**/
MaxVert = 1u << 2,
/**
indicates that the window is horizontally maximized.
**/
MaxHoriz = 1u << 3,
/**
convenience value. Equal to MaxVert | MaxHoriz.
**/
Max = MaxVert | MaxHoriz,
/**
indicates that the window is shaded (rolled-up).
**/
Shaded = 1u << 4,
/**
indicates that a window should not be included on a taskbar.
**/
SkipTaskbar = 1u << 5,
/**
indicates that a window should on top of most windows (but below fullscreen
windows).
**/
KeepAbove = 1u << 6,
/**
indicates that a window should not be included on a pager.
**/
SkipPager = 1u << 7,
/**
indicates that a window should not be visible on the screen (e.g. when minimised).
Only the window manager is allowed to change it.
**/
Hidden = 1u << 8,
/**
indicates that a window should fill the entire screen and have no window
decorations.
**/
FullScreen = 1u << 9,
/**
indicates that a window should be below most windows (but above any desktop windows).
**/
KeepBelow = 1u << 10,
/**
there was an attempt to activate this window, but the window manager prevented
this. E.g. taskbar should mark such window specially to bring user's attention to
this window. Only the window manager is allowed to change it.
**/
DemandsAttention = 1u << 11,
/**
indicates that a window should not be included on a switcher.
@since 5.45
**/
SkipSwitcher = 1u << 12,
/**
indicates that a client should render as though it has focus
Only the window manager is allowed to change it.
@since 5.58
**/
Focused = 1u << 13,
};
/**
* Stores a combination of #State values.
*/
Q_DECLARE_FLAGS(States, State)
/**
Direction for WMMoveResize.
When a client wants the Window Manager to start a WMMoveResize, it should
specify one of:
@li TopLeft
@li Top
@li TopRight
@li Right
@li BottomRight
@li Bottom
@li BottomLeft
@li Left
@li Move (for movement only)
@li KeyboardSize (resizing via keyboard)
@li KeyboardMove (movement via keyboard)
**/
enum Direction {
TopLeft = 0,
Top = 1,
TopRight = 2,
Right = 3,
BottomRight = 4,
Bottom = 5,
BottomLeft = 6,
Left = 7,
Move = 8, // movement only
KeyboardSize = 9, // size via keyboard
KeyboardMove = 10, // move via keyboard
MoveResizeCancel = 11, // to ask the WM to stop moving a window
};
/**
Client window mapping state. The class automatically watches the mapping
state of the client windows, and uses the mapping state to determine how
to set/change different properties. Note that this is very lowlevel
and you most probably don't want to use this state.
**/
enum MappingState {
/**
indicates the client window is visible to the user.
**/
Visible = 1, // NormalState,
/**
indicates that neither the client window nor its icon is visible.
**/
Withdrawn = 0, // WithdrawnState,
/**
indicates that the client window is not visible, but its icon is.
This can be when the window is minimized or when it's on a
different virtual desktop. See also NET::Hidden.
**/
Iconic = 3, // IconicState
};
/**
Actions that can be done with a window (_NET_WM_ALLOWED_ACTIONS).
@see Actions
**/
enum Action {
ActionMove = 1u << 0,
ActionResize = 1u << 1,
ActionMinimize = 1u << 2,
ActionShade = 1u << 3,
ActionStick = 1u << 4,
ActionMaxVert = 1u << 5,
ActionMaxHoriz = 1u << 6,
ActionMax = ActionMaxVert | ActionMaxHoriz,
ActionFullScreen = 1u << 7,
ActionChangeDesktop = 1u << 8,
ActionClose = 1u << 9,
};
/**
* Stores a combination of #Action values.
*/
Q_DECLARE_FLAGS(Actions, Action)
/**
Supported properties. Clients and Window Managers must define which
properties/protocols it wants to support.
Root/Desktop window properties and protocols:
@li Supported
@li ClientList
@li ClientListStacking
@li NumberOfDesktops
@li DesktopGeometry
@li DesktopViewport
@li CurrentDesktop
@li DesktopNames
@li ActiveWindow
@li WorkArea
@li SupportingWMCheck
@li VirtualRoots
@li CloseWindow
@li WMMoveResize
Client window properties and protocols:
@li WMName
@li WMVisibleName
@li WMDesktop
@li WMWindowType
@li WMState
@li WMStrut (obsoleted by WM2ExtendedStrut)
@li WMGeometry
@li WMFrameExtents
@li WMIconGeometry
@li WMIcon
@li WMIconName
@li WMVisibleIconName
@li WMHandledIcons
@li WMPid
@li WMPing
ICCCM properties (provided for convenience):
@li XAWMState
@see Properties
**/
enum Property {
// root
Supported = 1u << 0,
ClientList = 1u << 1,
ClientListStacking = 1u << 2,
NumberOfDesktops = 1u << 3,
DesktopGeometry = 1u << 4,
DesktopViewport = 1u << 5,
CurrentDesktop = 1u << 6,
DesktopNames = 1u << 7,
ActiveWindow = 1u << 8,
WorkArea = 1u << 9,
SupportingWMCheck = 1u << 10,
VirtualRoots = 1u << 11,
//
CloseWindow = 1u << 13,
WMMoveResize = 1u << 14,
// window
WMName = 1u << 15,
WMVisibleName = 1u << 16,
WMDesktop = 1u << 17,
WMWindowType = 1u << 18,
WMState = 1u << 19,
WMStrut = 1u << 20,
WMIconGeometry = 1u << 21,
WMIcon = 1u << 22,
WMPid = 1u << 23,
WMHandledIcons = 1u << 24,
WMPing = 1u << 25,
XAWMState = 1u << 27,
WMFrameExtents = 1u << 28,
// Need to be reordered
WMIconName = 1u << 29,
WMVisibleIconName = 1u << 30,
WMGeometry = 1u << 31,
WMAllProperties = ~0u,
};
/**
* Stores a combination of #Property values.
*/
Q_DECLARE_FLAGS(Properties, Property)
/**
Supported properties. This enum is an extension to NET::Property,
because them enum is limited only to 32 bits.
Client window properties and protocols:
@li WM2UserTime
@li WM2StartupId
@li WM2TransientFor mainwindow for the window (WM_TRANSIENT_FOR)
@li WM2GroupLeader group leader (window_group in WM_HINTS)
@li WM2AllowedActions
@li WM2RestackWindow
@li WM2MoveResizeWindow
@li WM2ExtendedStrut
@li WM2TemporaryRules internal, for kstart
@li WM2WindowClass WM_CLASS
@li WM2WindowRole WM_WINDOW_ROLE
@li WM2ClientMachine WM_CLIENT_MACHINE
@li WM2ShowingDesktop
@li WM2Opacity _NET_WM_WINDOW_OPACITY
@li WM2DesktopLayout _NET_DESKTOP_LAYOUT
@li WM2FullPlacement _NET_WM_FULL_PLACEMENT
@li WM2FullscreenMonitors _NET_WM_FULLSCREEN_MONITORS
@li WM2Urgency urgency hint in WM_HINTS (see ICCCM 4.1.2.4)
@li WM2Input input hint (input in WM_HINTS, see ICCCM 4.1.2.4)
@li WM2Protocols see NET::Protocol
@li WM2InitialMappingState initial state hint of WM_HINTS (see ICCCM 4.1.2.4)
@li WM2IconPixmap icon pixmap and mask in WM_HINTS (see ICCCM 4.1.2.4)
@li WM2OpaqueRegion
@li WM2DesktopFileName the base name of the desktop file name or the full path to the desktop file
@li WM2GTKFrameExtents extents of the shadow drawn by the client
@li WM2GTKApplicationId _GTK_APPLICATION_ID
@li WM2GTKShowWindowMenu _GTK_SHOW_WINDOW_MENU
@see Properties2
**/
enum Property2 {
WM2UserTime = 1u << 0,
WM2StartupId = 1u << 1,
WM2TransientFor = 1u << 2,
WM2GroupLeader = 1u << 3,
WM2AllowedActions = 1u << 4,
WM2RestackWindow = 1u << 5,
WM2MoveResizeWindow = 1u << 6,
WM2ExtendedStrut = 1u << 7,
WM2KDETemporaryRules = 1u << 8, // NOT STANDARD
WM2WindowClass = 1u << 9,
WM2WindowRole = 1u << 10,
WM2ClientMachine = 1u << 11,
WM2ShowingDesktop = 1u << 12,
WM2Opacity = 1u << 13,
WM2DesktopLayout = 1u << 14,
WM2FullPlacement = 1u << 15,
WM2FullscreenMonitors = 1u << 16,
WM2FrameOverlap = 1u << 17, // NOT STANDARD
WM2Activities = 1u << 18, // NOT STANDARD @since 4.6
WM2BlockCompositing = 1u << 19, // NOT STANDARD @since 4.7, STANDARD @since 5.17
WM2KDEShadow = 1u << 20, // NOT Standard @since 4.7
WM2Urgency = 1u << 21, // @since 5.3
WM2Input = 1u << 22, // @since 5.3
WM2Protocols = 1u << 23, // @since 5.3
WM2InitialMappingState = 1u << 24, // @since 5.5
WM2IconPixmap = 1u << 25, // @since 5.7
WM2OpaqueRegion = 1u << 25, // @since 5.7
WM2DesktopFileName = 1u << 26, // NOT STANDARD @since 5.28
WM2GTKFrameExtents = 1u << 27, // NOT STANDARD @since 5.65
WM2AppMenuServiceName = 1u << 28, // NOT STANDARD @since 5.69
WM2AppMenuObjectPath = 1u << 29, // NOT STANDARD @since 5.69
WM2GTKApplicationId = 1u << 30, // NOT STANDARD @since 5.91
WM2GTKShowWindowMenu = 1u << 31, // NOT STANDARD @since 5.96
WM2AllProperties = ~0u,
};
/**
* Stores a combination of #Property2 values.
*/
Q_DECLARE_FLAGS(Properties2, Property2)
/**
Sentinel value to indicate that the client wishes to be visible on
all desktops.
**/
enum {
OnAllDesktops = -1,
};
/**
Source of the request.
**/
// must match the values for data.l[0] field in _NET_ACTIVE_WINDOW message
enum RequestSource {
/**
@internal indicates that the source of the request is unknown
**/
FromUnknown = 0, // internal
/**
indicates that the request comes from a normal application
**/
FromApplication = 1,
/**
indicated that the request comes from pager or similar tool
**/
FromTool = 2,
};
/**
Orientation.
**/
enum Orientation {
OrientationHorizontal = 0,
OrientationVertical = 1,
};
/**
Starting corner for desktop layout.
**/
enum DesktopLayoutCorner {
DesktopLayoutCornerTopLeft = 0,
DesktopLayoutCornerTopRight = 1,
DesktopLayoutCornerBottomLeft = 2,
DesktopLayoutCornerBottomRight = 3,
};
/**
* Protocols supported by the client.
* See ICCCM 4.1.2.7.
*
* @see Protocols
* @since 5.3
**/
enum Protocol {
NoProtocol = 0,
TakeFocusProtocol = 1 << 0, ///< WM_TAKE_FOCUS
DeleteWindowProtocol = 1 << 1, ///< WM_DELETE_WINDOW
PingProtocol = 1 << 2, ///< _NET_WM_PING from EWMH
SyncRequestProtocol = 1 << 3, ///< _NET_WM_SYNC_REQUEST from EWMH
ContextHelpProtocol = 1 << 4, ///< _NET_WM_CONTEXT_HELP, NON STANDARD!
};
/**
* Stores a combination of #Protocol values.
*/
Q_DECLARE_FLAGS(Protocols, Protocol)
/**
Compares two X timestamps, taking into account wrapping and 64bit architectures.
Return value is like with strcmp(), 0 for equal, -1 for time1 < time2, 1 for time1 > time2.
*/
static int timestampCompare(unsigned long time1, unsigned long time2);
/**
Returns a difference of two X timestamps, time2 - time1, where time2 must be later than time1,
as returned by timestampCompare().
*/
static int timestampDiff(unsigned long time1, unsigned long time2);
};
Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Properties)
Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Properties2)
Q_DECLARE_OPERATORS_FOR_FLAGS(NET::WindowTypes)
Q_DECLARE_OPERATORS_FOR_FLAGS(NET::States)
Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Actions)
Q_DECLARE_OPERATORS_FOR_FLAGS(NET::Protocols)
#endif // netwm_def_h
@@ -0,0 +1,6 @@
if(KWINDOWSYSTEM_X11)
add_subdirectory(xcb)
endif()
if(KWINDOWSYSTEM_WAYLAND)
add_subdirectory(wayland)
endif()
@@ -0,0 +1,70 @@
add_library(KF6WindowSystemKWaylandPlugin MODULE)
set(wayland_plugin_SRCS
plugin.cpp
shm.cpp
windoweffects.cpp
windowshadow.cpp
windowsystem.cpp
waylandxdgactivationv1.cpp
waylandxdgforeignv2.cpp
plugin.h
windoweffects.h
windowshadow.h
windowsystem.h
waylandxdgactivationv1_p.h
)
if (Qt6_VERSION VERSION_GREATER_EQUAL "6.8.0")
set(private_code_option "PRIVATE_CODE")
endif()
qt6_generate_wayland_protocol_client_sources(KF6WindowSystemKWaylandPlugin
${private_code_option}
FILES
${WaylandProtocols_DATADIR}/staging/xdg-activation/xdg-activation-v1.xml
${WaylandProtocols_DATADIR}/unstable/xdg-foreign/xdg-foreign-unstable-v2.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/blur.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/contrast.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/slide.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/plasma-window-management.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/shadow.xml
${Wayland_DATADIR}/wayland.xml
)
ecm_qt_declare_logging_category(wayland_plugin_SRCS
HEADER logging.h
IDENTIFIER KWAYLAND_KWS
CATEGORY_NAME kf.windowsystem.wayland
OLD_CATEGORY_NAMES org.kde.kf5.kwindowsystem.kwayland
DEFAULT_SEVERITY Warning
DESCRIPTION "KWindowSystem Wayland Plugin"
EXPORT KWINDOWSYSTEM
)
target_sources(KF6WindowSystemKWaylandPlugin PRIVATE ${wayland_plugin_SRCS})
if(HAVE_MEMFD)
target_compile_definitions(KF6WindowSystemKWaylandPlugin PRIVATE -DHAVE_MEMFD)
endif()
target_compile_options(KF6WindowSystemKWaylandPlugin PRIVATE -UQT_NO_KEYWORDS)
pkg_check_modules(XKBCommon REQUIRED IMPORTED_TARGET xkbcommon)
target_link_libraries(KF6WindowSystemKWaylandPlugin
KF6WindowSystem
Wayland::Client
Qt::GuiPrivate
Qt::WaylandClient
PkgConfig::XKBCommon
)
install(
TARGETS
KF6WindowSystemKWaylandPlugin
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/kwindowsystem/
)
@@ -0,0 +1,40 @@
/*
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 "plugin.h"
#include "windoweffects.h"
#include "windowshadow.h"
#include "windowsystem.h"
KWaylandPlugin::KWaylandPlugin(QObject *parent)
: KWindowSystemPluginInterface(parent)
{
}
KWaylandPlugin::~KWaylandPlugin()
{
}
KWindowEffectsPrivate *KWaylandPlugin::createEffects()
{
return new WindowEffects();
}
KWindowSystemPrivate *KWaylandPlugin::createWindowSystem()
{
return new WindowSystem();
}
KWindowShadowTilePrivate *KWaylandPlugin::createWindowShadowTile()
{
return new WindowShadowTile();
}
KWindowShadowPrivate *KWaylandPlugin::createWindowShadow()
{
return new WindowShadow();
}
#include "moc_plugin.cpp"
@@ -0,0 +1,27 @@
/*
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
*/
#ifndef KWINDOWSYSTEM_KWAYLAND_PLUGIN_H
#define KWINDOWSYSTEM_KWAYLAND_PLUGIN_H
#include "kwindowsystemplugininterface_p.h"
class KWaylandPlugin : public KWindowSystemPluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.kde.kwindowsystem.KWindowSystemPluginInterface" FILE "wayland.json")
Q_INTERFACES(KWindowSystemPluginInterface)
public:
explicit KWaylandPlugin(QObject *parent = nullptr);
~KWaylandPlugin() override;
KWindowEffectsPrivate *createEffects() override;
KWindowSystemPrivate *createWindowSystem() override;
KWindowShadowTilePrivate *createWindowShadowTile() override;
KWindowShadowPrivate *createWindowShadow() override;
};
#endif
@@ -0,0 +1,137 @@
/*
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "shm.h"
#include <QGuiApplication>
#include <QImage>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <cstring>
static constexpr auto version = 1;
ShmBuffer::ShmBuffer(::wl_buffer *buffer)
: QtWayland::wl_buffer(buffer)
{
}
ShmBuffer::~ShmBuffer()
{
destroy();
}
Shm::Shm(QObject *parent)
: QWaylandClientExtensionTemplate(::version)
{
setParent(parent);
connect(this, &QWaylandClientExtension::activeChanged, this, [this] {
if (!isActive()) {
wl_shm_destroy(object());
}
});
initialize();
}
Shm *Shm::instance()
{
static Shm *instance = new Shm(qGuiApp);
return instance;
}
Shm::~Shm() noexcept
{
if (isActive()) {
wl_shm_destroy(object());
}
}
static wl_shm_format toWaylandFormat(QImage::Format format)
{
switch (format) {
case QImage::Format_ARGB32_Premultiplied:
return WL_SHM_FORMAT_ARGB8888;
case QImage::Format_RGB32:
return WL_SHM_FORMAT_XRGB8888;
case QImage::Format_ARGB32:
qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance. Use QImage::Format_ARGB32_Premultiplied";
return WL_SHM_FORMAT_ARGB8888;
default:
qCWarning(KWAYLAND_KWS()) << "Unsupported image format: " << format << ". expect slow performance.";
return WL_SHM_FORMAT_ARGB8888;
}
}
std::unique_ptr<ShmBuffer> Shm::createBuffer(const QImage &image)
{
if (image.isNull()) {
return {};
}
auto format = toWaylandFormat(image.format());
const int stride = image.bytesPerLine();
const int32_t byteCount = image.size().height() * stride;
int fd = -1;
#if defined HAVE_MEMFD
fd = memfd_create("kwayland-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd >= 0) {
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
} else
#endif
{
char templateName[] = "/tmp/kwayland-shared-XXXXXX";
fd = mkstemp(templateName);
if (fd >= 0) {
unlink(templateName);
int flags = fcntl(fd, F_GETFD);
if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
close(fd);
fd = -1;
}
}
}
if (fd == -1) {
qCDebug(KWAYLAND_KWS) << "Could not open temporary file for Shm pool";
return {};
}
if (ftruncate(fd, byteCount) < 0) {
qCDebug(KWAYLAND_KWS) << "Could not set size for Shm pool file";
close(fd);
return {};
}
auto data = mmap(nullptr, byteCount, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
qCDebug(KWAYLAND_KWS) << "Creating Shm pool failed";
close(fd);
return {};
}
auto pool = create_pool(fd, byteCount);
auto *buffer = wl_shm_pool_create_buffer(pool, 0, image.size().width(), image.size().height(), stride, format);
wl_shm_pool_destroy(pool);
const QImage &srcImage = [format, &image] {
if (format == WL_SHM_FORMAT_ARGB8888 && image.format() != QImage::Format_ARGB32_Premultiplied) {
return image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
} else {
return image;
}
}();
std::memcpy(static_cast<char *>(data), srcImage.bits(), byteCount);
munmap(data, byteCount);
close(fd);
return std::make_unique<ShmBuffer>(buffer);
}
@@ -0,0 +1,35 @@
/*
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2023 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 "logging.h"
#include <qwayland-wayland.h>
#include <QSize>
#include <QWaylandClientExtensionTemplate>
#include <memory>
class ShmBuffer : public QtWayland::wl_buffer
{
public:
ShmBuffer(::wl_buffer *buffer);
~ShmBuffer();
};
class Shm : public QWaylandClientExtensionTemplate<Shm>, public QtWayland::wl_shm
{
public:
static Shm *instance();
~Shm();
std::unique_ptr<ShmBuffer> createBuffer(const QImage &image);
private:
Shm(QObject *parent);
};
@@ -0,0 +1,28 @@
/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <QWindow>
#include <QGuiApplication>
#include <qpa/qplatformnativeinterface.h>
struct wl_surface;
inline wl_surface *surfaceForWindow(QWindow *window)
{
if (!window) {
return nullptr;
}
QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
if (!native) {
return nullptr;
}
window->create();
return reinterpret_cast<wl_surface *>(native->nativeResourceForWindow(QByteArrayLiteral("surface"), window));
}
@@ -0,0 +1,3 @@
{
"platforms": ["wayland", "wayland-egl"]
}
@@ -0,0 +1,48 @@
/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "waylandxdgactivationv1_p.h"
#include <QGuiApplication>
WaylandXdgActivationV1::WaylandXdgActivationV1()
: QWaylandClientExtensionTemplate<WaylandXdgActivationV1>(1)
{
initialize();
}
WaylandXdgActivationV1::~WaylandXdgActivationV1()
{
if (qGuiApp && isActive()) {
destroy();
}
}
WaylandXdgActivationV1 *WaylandXdgActivationV1::self()
{
static WaylandXdgActivationV1 *instance = new WaylandXdgActivationV1;
return instance;
}
WaylandXdgActivationTokenV1 *
WaylandXdgActivationV1::requestXdgActivationToken(wl_seat *seat, struct ::wl_surface *surface, uint32_t serial, const QString &app_id)
{
auto wl = get_activation_token();
auto provider = new WaylandXdgActivationTokenV1;
provider->init(wl);
if (surface)
provider->set_surface(surface);
if (!app_id.isEmpty())
provider->set_app_id(app_id);
if (seat)
provider->set_serial(serial, seat);
provider->commit();
return provider;
}
#include "moc_waylandxdgactivationv1_p.cpp"
@@ -0,0 +1,43 @@
/*
SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef WAYLANDXDGACTIVATIONV1_P_H
#define WAYLANDXDGACTIVATIONV1_P_H
#include "qwayland-xdg-activation-v1.h"
#include <QObject>
#include <QtWaylandClient/QWaylandClientExtension>
class QWaylandSurface;
class WaylandXdgActivationTokenV1 : public QObject, public QtWayland::xdg_activation_token_v1
{
Q_OBJECT
public:
void xdg_activation_token_v1_done(const QString &token) override
{
Q_EMIT done(token);
}
Q_SIGNALS:
void failed();
void done(const QString &token);
};
class WaylandXdgActivationV1 : public QWaylandClientExtensionTemplate<WaylandXdgActivationV1>, public QtWayland::xdg_activation_v1
{
public:
~WaylandXdgActivationV1() override;
static WaylandXdgActivationV1 *self();
WaylandXdgActivationTokenV1 *requestXdgActivationToken(wl_seat *seat, struct ::wl_surface *surface, uint32_t serial, const QString &app_id);
private:
WaylandXdgActivationV1();
};
#endif
@@ -0,0 +1,107 @@
/*
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "waylandxdgforeignv2_p.h"
#include <QGuiApplication>
WaylandXdgForeignExportedV2::WaylandXdgForeignExportedV2(::zxdg_exported_v2 *object)
: QObject()
, QtWayland::zxdg_exported_v2(object)
{
}
WaylandXdgForeignExportedV2::~WaylandXdgForeignExportedV2()
{
if (qGuiApp) {
destroy();
}
}
QString WaylandXdgForeignExportedV2::handle() const
{
return m_handle;
}
void WaylandXdgForeignExportedV2::zxdg_exported_v2_handle(const QString &handle)
{
m_handle = handle;
Q_EMIT handleReceived(handle);
}
WaylandXdgForeignExporterV2::WaylandXdgForeignExporterV2()
: QWaylandClientExtensionTemplate<WaylandXdgForeignExporterV2>(1)
{
initialize();
}
WaylandXdgForeignExporterV2::~WaylandXdgForeignExporterV2()
{
if (qGuiApp && isActive()) {
destroy();
}
}
WaylandXdgForeignExporterV2 &WaylandXdgForeignExporterV2::self()
{
static WaylandXdgForeignExporterV2 s_instance;
return s_instance;
}
WaylandXdgForeignExportedV2 *WaylandXdgForeignExporterV2::exportToplevel(wl_surface *surface)
{
return new WaylandXdgForeignExportedV2(export_toplevel(surface));
}
WaylandXdgForeignImportedV2::WaylandXdgForeignImportedV2(const QString &handle, ::zxdg_imported_v2 *object)
: QObject()
, QtWayland::zxdg_imported_v2(object)
, m_handle(handle)
{
}
WaylandXdgForeignImportedV2::~WaylandXdgForeignImportedV2()
{
if (qGuiApp) {
destroy();
}
}
void WaylandXdgForeignImportedV2::zxdg_imported_v2_destroyed()
{
delete this;
}
QString WaylandXdgForeignImportedV2::handle() const
{
return m_handle;
}
WaylandXdgForeignImporterV2::WaylandXdgForeignImporterV2()
: QWaylandClientExtensionTemplate<WaylandXdgForeignImporterV2>(1)
{
initialize();
}
WaylandXdgForeignImporterV2::~WaylandXdgForeignImporterV2()
{
if (qGuiApp && isActive()) {
destroy();
}
}
WaylandXdgForeignImporterV2 &WaylandXdgForeignImporterV2::self()
{
static WaylandXdgForeignImporterV2 s_instance;
return s_instance;
}
WaylandXdgForeignImportedV2 *WaylandXdgForeignImporterV2::importToplevel(const QString &handle)
{
return new WaylandXdgForeignImportedV2(handle, import_toplevel(handle));
}
#include "moc_waylandxdgforeignv2_p.cpp"
@@ -0,0 +1,76 @@
/*
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef WAYLANDXDGFOREIGNV2_P_H
#define WAYLANDXDGFOREIGNV2_P_H
#include "qwayland-xdg-foreign-unstable-v2.h"
#include <QObject>
#include <QtWaylandClient/QWaylandClientExtension>
class WaylandXdgForeignExportedV2 : public QObject, public QtWayland::zxdg_exported_v2
{
Q_OBJECT
public:
explicit WaylandXdgForeignExportedV2(::zxdg_exported_v2 *object);
~WaylandXdgForeignExportedV2() override;
QString handle() const;
Q_SIGNALS:
void handleReceived(const QString &handle);
protected:
void zxdg_exported_v2_handle(const QString &handle) override;
private:
QString m_handle;
};
class WaylandXdgForeignExporterV2 : public QWaylandClientExtensionTemplate<WaylandXdgForeignExporterV2>, public QtWayland::zxdg_exporter_v2
{
public:
~WaylandXdgForeignExporterV2() override;
static WaylandXdgForeignExporterV2 &self();
WaylandXdgForeignExportedV2 *exportToplevel(struct ::wl_surface *surface);
private:
WaylandXdgForeignExporterV2();
};
class WaylandXdgForeignImportedV2 : public QObject, public QtWayland::zxdg_imported_v2
{
public:
explicit WaylandXdgForeignImportedV2(const QString &handle, ::zxdg_imported_v2 *object);
~WaylandXdgForeignImportedV2() override;
QString handle() const;
protected:
void zxdg_imported_v2_destroyed() override;
private:
QString m_handle;
};
class WaylandXdgForeignImporterV2 : public QWaylandClientExtensionTemplate<WaylandXdgForeignImporterV2>, public QtWayland::zxdg_importer_v2
{
public:
~WaylandXdgForeignImporterV2() override;
static WaylandXdgForeignImporterV2 &self();
WaylandXdgForeignImportedV2 *importToplevel(const QString &handle);
private:
WaylandXdgForeignImporterV2();
};
#endif // WAYLANDXDGFOREIGNV2_P_H
@@ -0,0 +1,400 @@
/*
SPDX-FileCopyrightText: 2014 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 "windoweffects.h"
#include <QDebug>
#include <QExposeEvent>
#include <QGuiApplication>
#include <qpa/qplatformwindow_p.h>
#include <QWaylandClientExtensionTemplate>
#include <qwaylandclientextension.h>
#include "qwayland-blur.h"
#include "qwayland-contrast.h"
#include "qwayland-slide.h"
#include "surfacehelper.h"
#include <wayland-client-protocol.h>
static wl_region *createRegion(const QRegion &region)
{
QPlatformNativeInterface *native = qGuiApp->platformNativeInterface();
if (!native) {
return nullptr;
}
auto compositor = reinterpret_cast<wl_compositor *>(native->nativeResourceForIntegration(QByteArrayLiteral("compositor")));
if (!compositor) {
return nullptr;
}
auto wl_region = wl_compositor_create_region(compositor);
for (const auto &rect : region) {
wl_region_add(wl_region, rect.x(), rect.y(), rect.width(), rect.height());
}
return wl_region;
}
class BlurManager : public QWaylandClientExtensionTemplate<BlurManager>, public QtWayland::org_kde_kwin_blur_manager
{
public:
BlurManager()
: QWaylandClientExtensionTemplate<BlurManager>(1)
{
}
};
class Blur : public QObject, public QtWayland::org_kde_kwin_blur
{
public:
Blur(struct ::org_kde_kwin_blur *object, QObject *parent)
: QObject(parent)
, QtWayland::org_kde_kwin_blur(object)
{
}
~Blur() override
{
release();
}
};
class ContrastManager : public QWaylandClientExtensionTemplate<ContrastManager>, public QtWayland::org_kde_kwin_contrast_manager
{
public:
ContrastManager()
: QWaylandClientExtensionTemplate<ContrastManager>(2)
{
}
};
class Contrast : public QObject, public QtWayland::org_kde_kwin_contrast
{
public:
Contrast(struct ::org_kde_kwin_contrast *object, QObject *parent)
: QObject(parent)
, QtWayland::org_kde_kwin_contrast(object)
{
}
~Contrast() override
{
release();
}
};
class SlideManager : public QWaylandClientExtensionTemplate<SlideManager>, public QtWayland::org_kde_kwin_slide_manager
{
public:
SlideManager()
: QWaylandClientExtensionTemplate<SlideManager>(1)
{
}
};
class Slide : public QObject, public QtWayland::org_kde_kwin_slide
{
public:
Slide(struct ::org_kde_kwin_slide *object, QObject *parent)
: QObject(parent)
, QtWayland::org_kde_kwin_slide(object)
{
}
~Slide() override
{
release();
}
};
WindowEffects::WindowEffects()
: QObject()
, KWindowEffectsPrivate()
{
m_blurManager = new BlurManager();
m_contrastManager = new ContrastManager();
m_slideManager = new SlideManager();
// The KWindowEffects API doesn't provide any signals to notify that the particular
// effect has become unavailable. So we re-install effects when the corresponding globals
// are added.
connect(m_blurManager, &BlurManager::activeChanged, this, [this] {
for (auto it = m_blurRegions.constBegin(); it != m_blurRegions.constEnd(); ++it) {
installBlur(it.key(), m_blurManager->isActive(), *it);
}
});
connect(m_contrastManager, &ContrastManager::activeChanged, this, [this] {
for (auto it = m_backgroundConstrastRegions.constBegin(); it != m_backgroundConstrastRegions.constEnd(); ++it) {
if (m_contrastManager->isActive()) {
installContrast(it.key(), true, it->contrast, it->intensity, it->saturation, it->region);
} else {
installContrast(it.key(), false);
}
}
});
connect(m_slideManager, &SlideManager::activeChanged, this, [this] {
for (auto it = m_slideMap.constBegin(); it != m_slideMap.constEnd(); ++it) {
if (m_slideManager->isActive()) {
installSlide(it.key(), it->location, it->offset);
} else {
installSlide(it.key(), KWindowEffects::SlideFromLocation::NoEdge, 0);
}
}
});
}
WindowEffects::~WindowEffects()
{
delete m_blurManager;
delete m_contrastManager;
delete m_slideManager;
}
void WindowEffects::trackWindow(QWindow *window)
{
if (!m_windowWatchers.contains(window)) {
window->installEventFilter(this);
auto conn = connect(window, &QObject::destroyed, this, [this, window]() {
resetBlur(window);
m_blurRegions.remove(window);
resetContrast(window);
m_backgroundConstrastRegions.remove(window);
m_slideMap.remove(window);
m_windowWatchers.remove(window);
});
m_windowWatchers[window] << conn;
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
if (waylandWindow) {
auto conn = connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed, this, [this, window]() {
resetBlur(window);
resetContrast(window);
});
m_windowWatchers[window] << conn;
}
}
}
void WindowEffects::releaseWindow(QWindow *window)
{
if (!m_blurRegions.contains(window) && !m_backgroundConstrastRegions.contains(window) && !m_slideMap.contains(window)) {
for (const auto &conn : m_windowWatchers[window]) {
disconnect(conn);
}
window->removeEventFilter(this);
m_windowWatchers.remove(window);
}
}
// Helper function to replace a QObject value in the map and delete the old one.
template<typename MapType>
void replaceValue(MapType &map, typename MapType::key_type key, typename MapType::mapped_type value)
{
if (auto oldValue = map.take(key)) {
oldValue->deleteLater();
}
if (value) {
map[key] = value;
}
}
void WindowEffects::resetBlur(QWindow *window, Blur *blur)
{
replaceValue(m_blurs, window, blur);
}
void WindowEffects::resetContrast(QWindow *window, Contrast *contrast)
{
replaceValue(m_contrasts, window, contrast);
}
bool WindowEffects::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::Expose) {
auto window = qobject_cast<QWindow *>(watched);
if (!window || !window->isExposed()) {
return false;
}
{
auto it = m_blurRegions.constFind(window);
if (it != m_blurRegions.constEnd()) {
installBlur(window, true, *it);
}
}
{
auto it = m_backgroundConstrastRegions.constFind(window);
if (it != m_backgroundConstrastRegions.constEnd()) {
installContrast(window, true, it->contrast, it->intensity, it->saturation, it->region);
}
}
{
auto it = m_slideMap.constFind(window);
if (it != m_slideMap.constEnd()) {
installSlide(window, it->location, it->offset);
}
}
}
return false;
}
bool WindowEffects::isEffectAvailable(KWindowEffects::Effect effect)
{
switch (effect) {
case KWindowEffects::BackgroundContrast:
return m_contrastManager->isActive();
case KWindowEffects::BlurBehind:
return m_blurManager->isActive();
case KWindowEffects::Slide:
return m_slideManager->isActive();
default:
return false;
}
}
void WindowEffects::slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset)
{
if (location != KWindowEffects::SlideFromLocation::NoEdge) {
m_slideMap[window] = SlideData{
.location = location,
.offset = offset,
};
trackWindow(window);
} else {
m_slideMap.remove(window);
releaseWindow(window);
}
installSlide(window, location, offset);
}
void WindowEffects::installSlide(QWindow *window, KWindowEffects::SlideFromLocation location, int offset)
{
if (!m_slideManager->isActive()) {
return;
}
wl_surface *surface = surfaceForWindow(window);
if (surface) {
if (location != KWindowEffects::SlideFromLocation::NoEdge) {
auto slide = new Slide(m_slideManager->create(surface), window);
Slide::location convertedLoc;
switch (location) {
case KWindowEffects::SlideFromLocation::TopEdge:
convertedLoc = Slide::location::location_top;
break;
case KWindowEffects::SlideFromLocation::LeftEdge:
convertedLoc = Slide::location::location_left;
break;
case KWindowEffects::SlideFromLocation::RightEdge:
convertedLoc = Slide::location::location_right;
break;
case KWindowEffects::SlideFromLocation::BottomEdge:
default:
convertedLoc = Slide::location::location_bottom;
break;
}
slide->set_location(convertedLoc);
slide->set_offset(offset);
slide->commit();
} else {
m_slideManager->unset(surface);
}
}
}
void WindowEffects::enableBlurBehind(QWindow *window, bool enable, const QRegion &region)
{
if (enable) {
trackWindow(window);
m_blurRegions[window] = region;
} else {
resetBlur(window);
m_blurRegions.remove(window);
releaseWindow(window);
}
installBlur(window, enable, region);
}
void WindowEffects::installBlur(QWindow *window, bool enable, const QRegion &region)
{
if (!m_blurManager->isActive()) {
return;
}
wl_surface *surface = surfaceForWindow(window);
if (surface) {
if (enable) {
auto wl_region = createRegion(region);
if (!wl_region) {
return;
}
auto blur = new Blur(m_blurManager->create(surface), window);
blur->set_region(wl_region);
blur->commit();
wl_region_destroy(wl_region);
resetBlur(window, blur);
} else {
resetBlur(window);
m_blurManager->unset(surface);
}
}
}
void WindowEffects::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion &region)
{
if (enable) {
trackWindow(window);
m_backgroundConstrastRegions[window].contrast = contrast;
m_backgroundConstrastRegions[window].intensity = intensity;
m_backgroundConstrastRegions[window].saturation = saturation;
m_backgroundConstrastRegions[window].region = region;
} else {
resetContrast(window);
m_backgroundConstrastRegions.remove(window);
releaseWindow(window);
}
installContrast(window, enable, contrast, intensity, saturation, region);
}
void WindowEffects::installContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion &region)
{
if (!m_contrastManager->isActive()) {
return;
}
wl_surface *surface = surfaceForWindow(window);
if (surface) {
if (enable) {
auto wl_region = createRegion(region);
if (!wl_region) {
return;
}
auto backgroundContrast = new Contrast(m_contrastManager->create(surface), window);
backgroundContrast->set_region(wl_region);
backgroundContrast->set_contrast(wl_fixed_from_double(contrast));
backgroundContrast->set_intensity(wl_fixed_from_double(intensity));
backgroundContrast->set_saturation(wl_fixed_from_double(saturation));
backgroundContrast->commit();
wl_region_destroy(wl_region);
resetContrast(window, backgroundContrast);
} else {
resetContrast(window);
m_contrastManager->unset(surface);
}
}
}
#include "moc_windoweffects.cpp"
@@ -0,0 +1,72 @@
/*
SPDX-FileCopyrightText: 2014 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
*/
#ifndef WINDOWEFFECTS_H
#define WINDOWEFFECTS_H
#include "kwindoweffects_p.h"
#include <kwindowsystem_version.h>
#include <QHash>
#include <QObject>
#include <QPointer>
class BlurManager;
class Blur;
class ContrastManager;
class Contrast;
class SlideManager;
class WindowEffects : public QObject, public KWindowEffectsPrivate
{
Q_OBJECT
public:
WindowEffects();
~WindowEffects() override;
bool eventFilter(QObject *watched, QEvent *event) override;
void trackWindow(QWindow *window);
void releaseWindow(QWindow *window);
bool isEffectAvailable(KWindowEffects::Effect effect) override;
void slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) override;
void enableBlurBehind(QWindow *window, bool enable = true, const QRegion &region = QRegion()) override;
void enableBackgroundContrast(QWindow *window,
bool enable = true,
qreal contrast = 1,
qreal intensity = 1,
qreal saturation = 1,
const QRegion &region = QRegion()) override;
private:
void installContrast(QWindow *window, bool enable = true, qreal contrast = 1, qreal intensity = 1, qreal saturation = 1, const QRegion &region = QRegion());
void installBlur(QWindow *window, bool enable, const QRegion &region);
void installSlide(QWindow *window, KWindowEffects::SlideFromLocation location, int offset);
void resetBlur(QWindow *window, Blur *blur = nullptr);
void resetContrast(QWindow *window, Contrast *contrast = nullptr);
QHash<QWindow *, QList<QMetaObject::Connection>> m_windowWatchers;
QHash<QWindow *, QRegion> m_blurRegions;
struct BackgroundContrastData {
qreal contrast = 1;
qreal intensity = 1;
qreal saturation = 1;
QRegion region;
};
QHash<QWindow *, BackgroundContrastData> m_backgroundConstrastRegions;
QHash<QWindow *, QPointer<Blur>> m_blurs;
QHash<QWindow *, QPointer<Contrast>> m_contrasts;
struct SlideData {
KWindowEffects::SlideFromLocation location;
int offset;
};
QHash<QWindow *, SlideData> m_slideMap;
BlurManager *m_blurManager;
ContrastManager *m_contrastManager;
SlideManager *m_slideManager;
};
#endif
@@ -0,0 +1,214 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-FileCopyrightText: 2023 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "windowshadow.h"
#include "logging.h"
#include "shm.h"
#include "surfacehelper.h"
#include <qwayland-shadow.h>
#include <QDebug>
#include <QExposeEvent>
#include <QWaylandClientExtension>
#include <qpa/qplatformwindow_p.h>
class ShadowManager : public QWaylandClientExtensionTemplate<ShadowManager>, public QtWayland::org_kde_kwin_shadow_manager
{
Q_OBJECT
static constexpr int version = 2;
explicit ShadowManager(QObject *parent = nullptr)
: QWaylandClientExtensionTemplate(version)
{
setParent(parent);
initialize();
connect(this, &QWaylandClientExtension::activeChanged, this, [this] {
if (!isActive()) {
destroy();
}
});
}
public:
~ShadowManager()
{
if (isActive()) {
destroy();
}
}
static ShadowManager *instance()
{
static ShadowManager *instance = new ShadowManager(qGuiApp);
return instance;
}
};
class Shadow : public QtWayland::org_kde_kwin_shadow
{
public:
using QtWayland::org_kde_kwin_shadow::org_kde_kwin_shadow;
~Shadow()
{
destroy();
}
};
WindowShadowTile::WindowShadowTile()
{
connect(Shm::instance(), &Shm::activeChanged, this, [this] {
if (Shm::instance()->isActive()) {
buffer.reset();
}
});
}
WindowShadowTile::~WindowShadowTile()
{
}
bool WindowShadowTile::create()
{
if (!Shm::instance()->isActive()) {
return false;
}
buffer = Shm::instance()->createBuffer(image);
return true;
}
void WindowShadowTile::destroy()
{
buffer.reset();
}
WindowShadowTile *WindowShadowTile::get(const KWindowShadowTile *tile)
{
KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile);
return static_cast<WindowShadowTile *>(d);
}
static wl_buffer *bufferForTile(const KWindowShadowTile::Ptr &tile)
{
if (!tile) {
return nullptr;
}
WindowShadowTile *d = WindowShadowTile::get(tile.data());
// Our buffer has been deleted in the meantime, try to create it again
if (!d->buffer && d->isCreated) {
d->buffer = Shm::instance()->createBuffer(d->image);
}
return d->buffer ? d->buffer.get()->object() : nullptr;
}
WindowShadow::WindowShadow()
{
}
WindowShadow::~WindowShadow()
{
}
bool WindowShadow::eventFilter(QObject *watched, QEvent *event)
{
Q_UNUSED(watched)
if (event->type() == QEvent::Expose) {
if (auto window = qobject_cast<QWindow *>(watched); window && window->isExposed()) {
if (!internalCreate()) {
qCWarning(KWAYLAND_KWS) << "Failed to recreate shadow for" << window;
}
}
}
return false;
}
bool WindowShadow::internalCreate()
{
if (shadow) {
return true;
}
if (!ShadowManager::instance()->isActive()) {
return false;
}
auto surface = surfaceForWindow(window);
if (!surface) {
return false;
}
shadow = std::make_unique<Shadow>(ShadowManager::instance()->create(surface));
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
if (waylandWindow) {
connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed, this, &WindowShadow::internalDestroy, Qt::UniqueConnection);
}
auto attach = [](const std::unique_ptr<Shadow> &shadow, auto attach_func, const KWindowShadowTile::Ptr &tile) {
if (auto buffer = bufferForTile(tile)) {
(*shadow.*attach_func)(buffer);
}
};
attach(shadow, &Shadow::attach_left, leftTile);
attach(shadow, &Shadow::attach_top_left, topLeftTile);
attach(shadow, &Shadow::attach_top, topTile);
attach(shadow, &Shadow::attach_top_right, topRightTile);
attach(shadow, &Shadow::attach_right, rightTile);
attach(shadow, &Shadow::attach_bottom_right, bottomRightTile);
attach(shadow, &Shadow::attach_bottom, bottomTile);
attach(shadow, &Shadow::attach_bottom_left, bottomLeftTile);
shadow->set_left_offset(wl_fixed_from_double(padding.left()));
shadow->set_top_offset(wl_fixed_from_double(padding.top()));
shadow->set_right_offset(wl_fixed_from_double(padding.right()));
shadow->set_bottom_offset(wl_fixed_from_double(padding.bottom()));
shadow->commit();
// Commit wl_surface at the next available time.
window->requestUpdate();
return true;
}
bool WindowShadow::create()
{
if (!ShadowManager::instance()->isActive()) {
return false;
}
internalCreate();
window->installEventFilter(this);
return true;
}
void WindowShadow::internalDestroy()
{
if (!shadow) {
return;
}
// Only call surfaceForWindow and unset the surface if the native window is alive.
// Otherwise window->create() might be called when the window is being destroyed, leading to a crash.
if (window && window->nativeInterface<QNativeInterface::Private::QWaylandWindow>() && ShadowManager::instance()->isActive()) {
if (auto surface = surfaceForWindow(window)) {
ShadowManager::instance()->unset(surface);
}
}
shadow.reset();
if (window && window->isVisible()) {
window->requestUpdate();
}
}
void WindowShadow::destroy()
{
if (window) {
window->removeEventFilter(this);
}
internalDestroy();
}
#include "windowshadow.moc"
@@ -0,0 +1,50 @@
/*
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
*/
#ifndef WINDOWSHADOW_H
#define WINDOWSHADOW_H
#include "kwindowshadow_p.h"
#include <memory>
class Shadow;
class ShmBuffer;
class Shm;
class WindowShadowTile final : public QObject, public KWindowShadowTilePrivate
{
public:
WindowShadowTile();
~WindowShadowTile();
bool create() override;
void destroy() override;
static WindowShadowTile *get(const KWindowShadowTile *tile);
std::unique_ptr<ShmBuffer> buffer;
};
class WindowShadow final : public QObject, public KWindowShadowPrivate
{
public:
WindowShadow();
~WindowShadow() override;
bool create() override;
void destroy() override;
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
bool internalCreate();
void internalDestroy();
std::unique_ptr<Shadow> shadow;
};
#endif // WINDOWSHADOW_H
@@ -0,0 +1,289 @@
/*
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "windowsystem.h"
#include "logging.h"
#include "surfacehelper.h"
#include "waylandxdgactivationv1_p.h"
#include "waylandxdgforeignv2_p.h"
#include <KWaylandExtras>
#include <KWindowSystem>
#include "qwayland-plasma-window-management.h"
#include <QEvent>
#include <QGuiApplication>
#include <QPixmap>
#include <QPoint>
#include <QString>
#include <QTimer>
#include <QWaylandClientExtensionTemplate>
#include <QWindow>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformwindow_p.h>
constexpr const char *c_kdeXdgForeignExportedProperty("_kde_xdg_foreign_exported_v2");
constexpr const char *c_kdeXdgForeignImportedProperty("_kde_xdg_foreign_imported_v2");
constexpr const char *c_kdeXdgForeignPendingHandleProperty("_kde_xdg_foreign_pending_handle");
class WindowManagement : public QWaylandClientExtensionTemplate<WindowManagement>, public QtWayland::org_kde_plasma_window_management
{
public:
WindowManagement()
: QWaylandClientExtensionTemplate<WindowManagement>(17)
{
}
void org_kde_plasma_window_management_show_desktop_changed(uint32_t state) override
{
showingDesktop = state == show_desktop_enabled;
KWindowSystem::self()->showingDesktopChanged(showingDesktop);
}
bool showingDesktop = false;
};
WindowSystem::WindowSystem()
: QObject()
, KWindowSystemPrivateV2()
, m_lastToken(qEnvironmentVariable("XDG_ACTIVATION_TOKEN"))
{
m_windowManagement = new WindowManagement;
}
WindowSystem::~WindowSystem()
{
delete m_windowManagement;
}
void WindowSystem::activateWindow(QWindow *win, long int time)
{
Q_UNUSED(time);
auto s = surfaceForWindow(win);
if (!s) {
return;
}
WaylandXdgActivationV1 *activation = WaylandXdgActivationV1::self();
if (!activation->isActive()) {
return;
}
activation->activate(m_lastToken, s);
}
void WindowSystem::requestToken(QWindow *window, uint32_t serial, const QString &app_id)
{
if (window) {
window->create();
}
wl_surface *wlSurface = surfaceForWindow(window);
WaylandXdgActivationV1 *activation = WaylandXdgActivationV1::self();
if (!activation->isActive()) {
// Ensure that xdgActivationTokenArrived is always emitted asynchronously
QTimer::singleShot(0, [serial] {
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {});
});
return;
}
auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
auto seat = waylandApp ? waylandApp->lastInputSeat() : nullptr;
auto tokenReq = activation->requestXdgActivationToken(seat, wlSurface, serial, app_id);
connect(tokenReq, &WaylandXdgActivationTokenV1::failed, KWindowSystem::self(), [serial, app_id]() {
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, {});
});
connect(tokenReq, &WaylandXdgActivationTokenV1::done, KWindowSystem::self(), [serial](const QString &token) {
Q_EMIT KWaylandExtras::self()->xdgActivationTokenArrived(serial, token);
});
}
void WindowSystem::setCurrentToken(const QString &token)
{
m_lastToken = token;
}
quint32 WindowSystem::lastInputSerial(QWindow *window)
{
Q_UNUSED(window)
if (auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>()) {
return waylandApp->lastInputSerial();
}
return 0;
}
void WindowSystem::setShowingDesktop(bool showing)
{
if (!m_windowManagement->isActive()) {
return;
}
m_windowManagement->show_desktop(showing ? WindowManagement::show_desktop_enabled : WindowManagement::show_desktop_disabled);
}
bool WindowSystem::showingDesktop()
{
if (!m_windowManagement->isActive()) {
return false;
}
return m_windowManagement->showingDesktop;
}
void WindowSystem::exportWindow(QWindow *window)
{
auto emitHandle = [window](const QString &handle) {
// Ensure that windowExported is always emitted asynchronously.
QMetaObject::invokeMethod(
window,
[window, handle] {
Q_EMIT KWaylandExtras::self()->windowExported(window, handle);
},
Qt::QueuedConnection);
};
if (!window) {
return;
}
window->create();
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
if (!waylandWindow) {
emitHandle({});
return;
}
auto &exporter = WaylandXdgForeignExporterV2::self();
if (!exporter.isActive()) {
emitHandle({});
return;
}
// We want to use QObject::property(char*) and use dynamic properties on the object rather than
// call QWaylandWindow::property(QString) and send it around.
WaylandXdgForeignExportedV2 *exported = waylandWindow->property(c_kdeXdgForeignExportedProperty).value<WaylandXdgForeignExportedV2 *>();
if (!exported) {
exported = exporter.exportToplevel(surfaceForWindow(window));
exported->setParent(waylandWindow);
waylandWindow->setProperty(c_kdeXdgForeignExportedProperty, QVariant::fromValue(exported));
connect(exported, &QObject::destroyed, waylandWindow, [waylandWindow] {
waylandWindow->setProperty(c_kdeXdgForeignExportedProperty, QVariant());
});
connect(exported, &WaylandXdgForeignExportedV2::handleReceived, window, [window](const QString &handle) {
Q_EMIT KWaylandExtras::self()->windowExported(window, handle);
});
}
if (!exported->handle().isEmpty()) {
emitHandle(exported->handle());
}
}
void WindowSystem::unexportWindow(QWindow *window)
{
auto waylandWindow = window ? window->nativeInterface<QNativeInterface::Private::QWaylandWindow>() : nullptr;
if (!waylandWindow) {
return;
}
WaylandXdgForeignExportedV2 *exported = waylandWindow->property(c_kdeXdgForeignExportedProperty).value<WaylandXdgForeignExportedV2 *>();
delete exported;
Q_ASSERT(!waylandWindow->property(c_kdeXdgForeignExportedProperty).isValid());
}
void WindowSystem::setMainWindow(QWindow *window, const QString &handle)
{
if (!window) {
return;
}
window->create();
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
if (!waylandWindow) {
return;
}
// We want to use QObject::property(char*) and use dynamic properties on the object rather than
// call QWaylandWindow::property(QString) and send it around.
auto *imported = waylandWindow->property(c_kdeXdgForeignImportedProperty).value<WaylandXdgForeignImportedV2 *>();
// Window already parented with a different handle? Delete imported so we import the new one later.
if (imported && imported->handle() != handle) {
delete imported;
imported = nullptr;
Q_ASSERT(!waylandWindow->property(c_kdeXdgForeignImportedProperty).isValid());
}
// Don't bother.
if (handle.isEmpty()) {
return;
}
if (window->isExposed()) {
doSetMainWindow(window, handle);
} else {
// We can only import an XDG toplevel.
// QWaylandWindow::surfaceRoleCreated is only in Qt 6.8,
// in earlier versions wait for the window be exposed,
// since QWaylandWindow::wlSurfaceCreated is too early.
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
window->setProperty(c_kdeXdgForeignPendingHandleProperty, handle);
window->installEventFilter(this);
#else
connect(waylandWindow, &QNativeInterface::Private::QWaylandWindow::surfaceRoleCreated, window, [window, handle] {
doSetMainWindow(window, handle);
});
#endif
}
}
bool WindowSystem::eventFilter(QObject *watched, QEvent *event)
{
#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0)
if (event->type() == QEvent::Expose) {
auto *window = static_cast<QWindow *>(watched);
if (window->isExposed()) {
const QString handle = window->property(c_kdeXdgForeignPendingHandleProperty).toString();
if (!handle.isEmpty()) {
doSetMainWindow(window, handle);
window->setProperty(c_kdeXdgForeignPendingHandleProperty, QVariant());
}
window->removeEventFilter(this);
}
}
#endif
return QObject::eventFilter(watched, event);
}
void WindowSystem::doSetMainWindow(QWindow *window, const QString &handle)
{
Q_ASSERT(window);
Q_ASSERT(!handle.isEmpty());
auto waylandWindow = window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
if (!waylandWindow) {
return;
}
auto &importer = WaylandXdgForeignImporterV2::self();
if (!importer.isActive()) {
return;
}
Q_ASSERT(!waylandWindow->property(c_kdeXdgForeignImportedProperty).isValid());
WaylandXdgForeignImportedV2 *imported = importer.importToplevel(handle);
imported->set_parent_of(surfaceForWindow(window)); // foreign parent.
imported->setParent(waylandWindow); // memory owner.
waylandWindow->setProperty(c_kdeXdgForeignImportedProperty, QVariant::fromValue(imported));
connect(imported, &QObject::destroyed, waylandWindow, [waylandWindow] {
waylandWindow->setProperty(c_kdeXdgForeignImportedProperty, QVariant());
});
}
#include "moc_windowsystem.cpp"
@@ -0,0 +1,40 @@
/*
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
*/
#ifndef WINDOWSYSTEM_H
#define WINDOWSYSTEM_H
#include "kwindowsystem_p.h"
#include <QObject>
class WindowManagement;
class WindowSystem : public QObject, public KWindowSystemPrivateV2
{
Q_OBJECT
public:
WindowSystem();
~WindowSystem() override;
void activateWindow(QWindow *win, long time) override;
void requestToken(QWindow *win, uint32_t serial, const QString &app_id) override;
quint32 lastInputSerial(QWindow *window) override;
void setCurrentToken(const QString &token) override;
bool showingDesktop() override;
void setShowingDesktop(bool showing) override;
void exportWindow(QWindow *window) override;
void unexportWindow(QWindow *window) override;
void setMainWindow(QWindow *window, const QString &handle) override;
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
static void doSetMainWindow(QWindow *window, const QString &handle);
QString m_lastToken;
WindowManagement *m_windowManagement;
};
#endif
@@ -0,0 +1,52 @@
add_library(KF6WindowSystemX11Plugin MODULE)
target_sources(KF6WindowSystemX11Plugin PRIVATE
kwindoweffects.cpp
kwindowshadow.cpp
kwindowsystem.cpp
kxutils.cpp
plugin.cpp
)
target_link_libraries(KF6WindowSystemX11Plugin
PRIVATE
KF6WindowSystem
XCB::XCB
XCB::RES
${X11_LIBRARIES}
${X11_Xfixes_LIB}
Qt6::GuiPrivate
)
ecm_generate_headers(KWindowSystemX11_HEADERS
HEADER_NAMES
KSelectionOwner
KSelectionWatcher
KXMessages
NETWM # does not match the classnames in that file...
REQUIRED_HEADERS
KWindowSystemX11_HEADERS
)
install(
FILES
${KWindowSystemX11_HEADERS}
fixx11h.h
DESTINATION
${KDE_INSTALL_INCLUDEDIR_KF}/KWindowSystem
COMPONENT
Devel
)
set_target_properties(
KF6WindowSystemX11Plugin
PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/kf6/kwindowsystem"
)
install(
TARGETS
KF6WindowSystemX11Plugin
DESTINATION
${KDE_INSTALL_PLUGINDIR}/kf6/kwindowsystem/
)
@@ -0,0 +1,177 @@
/*
SPDX-FileCopyrightText: 2015 Thomas Lübking <thomas.luebking@gmail.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#if (!defined ATOMS_H) || (defined ENUM_CREATE_CHAR_ARRAY)
#undef ENUM_BEGIN
#undef ENUM
#undef ENUM_END
#undef ENUM_COUNT
// the following macros are set in a way so that
// the code below will either construct an enum for "<typ>"
// or a const *char array "<typ>Strings" containing all enum
// symbols as strings, depending on whether ENUM_CREATE_CHAR_ARRAY is
// defined
// The enum gets one extra item "<typ>Count", describing also the
// length of the array
// The header is safe for re-inclusion unless you define ENUM_CREATE_CHAR_ARRAY
// which is therefore undefined after usage
// => You *must* "#define ENUM_CREATE_CHAR_ARRAY 1" *every* time you want to create
// a string array!
// clang-format off
#ifndef ENUM_CREATE_CHAR_ARRAY
#define ATOMS_H
#define ENUM_BEGIN(typ) enum typ {
#define ENUM(nam) nam
#define ENUM_COUNT(typ) , typ##Count
#else
#define ENUM_BEGIN(typ) const char * typ##Strings [] = {
#define ENUM(nam) #nam
#define ENUM_COUNT(typ)
#undef ENUM_CREATE_CHAR_ARRAY
#endif
#define ENUM_END(typ) };
ENUM_BEGIN(KwsAtom)
ENUM(UTF8_STRING),
// root window properties
ENUM(_NET_SUPPORTED),
ENUM(_NET_SUPPORTING_WM_CHECK),
ENUM(_NET_CLIENT_LIST),
ENUM(_NET_CLIENT_LIST_STACKING),
ENUM(_NET_NUMBER_OF_DESKTOPS),
ENUM(_NET_DESKTOP_GEOMETRY),
ENUM(_NET_DESKTOP_VIEWPORT),
ENUM(_NET_CURRENT_DESKTOP),
ENUM(_NET_DESKTOP_NAMES),
ENUM(_NET_ACTIVE_WINDOW),
ENUM(_NET_WORKAREA),
ENUM(_NET_VIRTUAL_ROOTS),
ENUM(_NET_DESKTOP_LAYOUT),
ENUM(_NET_SHOWING_DESKTOP),
// root window messages
ENUM(_NET_CLOSE_WINDOW),
ENUM(_NET_RESTACK_WINDOW),
ENUM(_NET_WM_MOVERESIZE),
ENUM(_NET_MOVERESIZE_WINDOW),
// application window properties
ENUM(_NET_WM_NAME),
ENUM(_NET_WM_VISIBLE_NAME),
ENUM(_NET_WM_ICON_NAME),
ENUM(_NET_WM_VISIBLE_ICON_NAME),
ENUM(_NET_WM_DESKTOP),
ENUM(_NET_WM_WINDOW_TYPE),
ENUM(_NET_WM_STATE),
ENUM(_NET_WM_STRUT),
ENUM(_NET_WM_STRUT_PARTIAL),
ENUM(_NET_WM_ICON_GEOMETRY),
ENUM(_NET_WM_ICON),
ENUM(_NET_WM_PID),
ENUM(_NET_WM_USER_TIME),
ENUM(_NET_WM_HANDLED_ICONS),
ENUM(_NET_STARTUP_ID),
ENUM(_NET_WM_ALLOWED_ACTIONS),
ENUM(WM_WINDOW_ROLE),
ENUM(_NET_FRAME_EXTENTS),
ENUM(_NET_WM_WINDOW_OPACITY),
ENUM(_NET_WM_FULLSCREEN_MONITORS),
ENUM(_NET_WM_OPAQUE_REGION),
ENUM(_KDE_NET_WM_DESKTOP_FILE),
// used to determine whether application window is managed or not
ENUM(WM_STATE),
// application window types
ENUM(_NET_WM_WINDOW_TYPE_NORMAL),
ENUM(_NET_WM_WINDOW_TYPE_DESKTOP),
ENUM(_NET_WM_WINDOW_TYPE_DOCK),
ENUM(_NET_WM_WINDOW_TYPE_TOOLBAR),
ENUM(_NET_WM_WINDOW_TYPE_MENU),
ENUM(_NET_WM_WINDOW_TYPE_DIALOG),
ENUM(_NET_WM_WINDOW_TYPE_UTILITY),
ENUM(_NET_WM_WINDOW_TYPE_SPLASH),
ENUM(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU),
ENUM(_NET_WM_WINDOW_TYPE_POPUP_MENU),
ENUM(_NET_WM_WINDOW_TYPE_TOOLTIP),
ENUM(_NET_WM_WINDOW_TYPE_NOTIFICATION),
ENUM(_NET_WM_WINDOW_TYPE_COMBO),
ENUM(_NET_WM_WINDOW_TYPE_DND),
// application window state
ENUM(_NET_WM_STATE_MODAL),
ENUM(_NET_WM_STATE_STICKY),
ENUM(_NET_WM_STATE_MAXIMIZED_VERT),
ENUM(_NET_WM_STATE_MAXIMIZED_HORZ),
ENUM(_NET_WM_STATE_SHADED),
ENUM(_NET_WM_STATE_SKIP_TASKBAR),
ENUM(_NET_WM_STATE_SKIP_PAGER),
ENUM(_NET_WM_STATE_HIDDEN),
ENUM(_NET_WM_STATE_FULLSCREEN),
ENUM(_NET_WM_STATE_ABOVE),
ENUM(_NET_WM_STATE_BELOW),
ENUM(_NET_WM_STATE_DEMANDS_ATTENTION),
ENUM(_NET_WM_STATE_FOCUSED),
// KDE-specific atom
ENUM(_KDE_NET_WM_STATE_SKIP_SWITCHER),
// allowed actions
ENUM(_NET_WM_ACTION_MOVE),
ENUM(_NET_WM_ACTION_RESIZE),
ENUM(_NET_WM_ACTION_MINIMIZE),
ENUM(_NET_WM_ACTION_SHADE),
ENUM(_NET_WM_ACTION_STICK),
ENUM(_NET_WM_ACTION_MAXIMIZE_VERT),
ENUM(_NET_WM_ACTION_MAXIMIZE_HORZ),
ENUM(_NET_WM_ACTION_FULLSCREEN),
ENUM(_NET_WM_ACTION_CHANGE_DESKTOP),
ENUM(_NET_WM_ACTION_CLOSE),
// KDE extensions
ENUM(_KDE_NET_WM_FRAME_STRUT),
ENUM(_KDE_NET_WM_WINDOW_TYPE_OVERRIDE),
ENUM(_KDE_NET_WM_WINDOW_TYPE_TOPMENU),
ENUM(_KDE_NET_WM_WINDOW_TYPE_ON_SCREEN_DISPLAY),
ENUM(_KDE_NET_WM_WINDOW_TYPE_CRITICAL_NOTIFICATION),
ENUM(_KDE_NET_WM_WINDOW_TYPE_APPLET_POPUP),
ENUM(_KDE_NET_WM_TEMPORARY_RULES),
ENUM(_NET_WM_FRAME_OVERLAP),
ENUM(_KDE_NET_WM_APPMENU_SERVICE_NAME),
ENUM(_KDE_NET_WM_APPMENU_OBJECT_PATH),
// deprecated and naming convention violation
ENUM(_NET_WM_STATE_STAYS_ON_TOP),
// GTK extensions
ENUM(_GTK_FRAME_EXTENTS),
ENUM(_GTK_APPLICATION_ID),
ENUM(_GTK_SHOW_WINDOW_MENU),
// application protocols
ENUM(WM_PROTOCOLS),
ENUM(WM_TAKE_FOCUS),
ENUM(WM_DELETE_WINDOW),
ENUM(_NET_WM_PING),
ENUM(_NET_WM_SYNC_REQUEST),
ENUM(_NET_WM_CONTEXT_HELP),
// ability flags
ENUM(_NET_WM_FULL_PLACEMENT),
ENUM(_NET_WM_BYPASS_COMPOSITOR),
ENUM(_KDE_NET_WM_ACTIVITIES),
ENUM(_KDE_NET_WM_BLOCK_COMPOSITING),
ENUM(_KDE_NET_WM_SHADOW)
ENUM_COUNT(KwsAtom)
ENUM_END(KwsAtom)
#endif // ATOMS_H
@@ -0,0 +1,278 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
//#ifdef don't do this, this file is supposed to be included
//#define multiple times
#include <QtGlobal>
/* Usage:
If you get compile errors caused by X11 includes (the line
where first error appears contains word like None, Unsorted,
Below, etc.), put #include <fixx11h.h> in the .cpp file
(not .h file!) between the place where X11 headers are
included and the place where the file with compile
error is included (or the place where the compile error
in the .cpp file occurs).
This file remaps X11 #defines to const variables or
inline functions. The side effect may be that these
symbols may now refer to different variables
(e.g. if X11 #defined NoButton, after this file
is included NoButton would no longer be X11's
NoButton, but Qt::NoButton instead). At this time,
there's no conflict known that could cause problems.
The original X11 symbols are still accessible
(e.g. for None) as X::None, XNone, and also still
None, unless name lookup finds different None
first (in the current class, etc.)
Use 'Unsorted', 'Bool' and 'index' as templates.
*/
namespace X
{
// template --->
// Affects: Should be without side effects.
#ifdef Unsorted
#ifndef FIXX11H_Unsorted
#define FIXX11H_Unsorted
const int XUnsorted = Unsorted;
#undef Unsorted
const int Unsorted = XUnsorted;
#endif
#undef Unsorted
#endif
// template <---
// Affects: Should be without side effects.
#ifdef None
#ifndef FIXX11H_None
#define FIXX11H_None
const XID XNone = None;
#undef None
const XID None = XNone;
#endif
#undef None
#endif
// template --->
// Affects: Should be without side effects.
#ifdef Bool
#ifndef FIXX11H_Bool
#define FIXX11H_Bool
#ifdef _XTYPEDEF_BOOL /* Xdefs.h has typedef'ed Bool already */
#undef Bool
#else
typedef Bool XBool;
#undef Bool
typedef XBool Bool;
#endif
#endif
#undef Bool
#define _XTYPEDEF_BOOL
#endif
// template <---
// Affects: Should be without side effects.
#ifdef KeyPress
#ifndef FIXX11H_KeyPress
#define FIXX11H_KeyPress
const int XKeyPress = KeyPress;
#undef KeyPress
const int KeyPress = XKeyPress;
#endif
#undef KeyPress
#endif
// Affects: Should be without side effects.
#ifdef KeyRelease
#ifndef FIXX11H_KeyRelease
#define FIXX11H_KeyRelease
const int XKeyRelease = KeyRelease;
#undef KeyRelease
const int KeyRelease = XKeyRelease;
#endif
#undef KeyRelease
#endif
// Affects: Should be without side effects.
#ifdef Above
#ifndef FIXX11H_Above
#define FIXX11H_Above
const int XAbove = Above;
#undef Above
const int Above = XAbove;
#endif
#undef Above
#endif
// Affects: Should be without side effects.
#ifdef Below
#ifndef FIXX11H_Below
#define FIXX11H_Below
const int XBelow = Below;
#undef Below
const int Below = XBelow;
#endif
#undef Below
#endif
// Affects: Should be without side effects.
#ifdef FocusIn
#ifndef FIXX11H_FocusIn
#define FIXX11H_FocusIn
const int XFocusIn = FocusIn;
#undef FocusIn
const int FocusIn = XFocusIn;
#endif
#undef FocusIn
#endif
// Affects: Should be without side effects.
#ifdef FocusOut
#ifndef FIXX11H_FocusOut
#define FIXX11H_FocusOut
const int XFocusOut = FocusOut;
#undef FocusOut
const int FocusOut = XFocusOut;
#endif
#undef FocusOut
#endif
// Affects: Should be without side effects.
#ifdef Always
#ifndef FIXX11H_Always
#define FIXX11H_Always
const int XAlways = Always;
#undef Always
const int Always = XAlways;
#endif
#undef Always
#endif
// Affects: Should be without side effects.
#ifdef Expose
#ifndef FIXX11H_Expose
#define FIXX11H_Expose
const int XExpose = Expose;
#undef Expose
const int Expose = XExpose;
#endif
#undef Expose
#endif
// Affects: Should be without side effects.
#ifdef Success
#ifndef FIXX11H_Success
#define FIXX11H_Success
const int XSuccess = Success;
#undef Success
const int Success = XSuccess;
#endif
#undef Success
#endif
// Affects: Should be without side effects.
#ifdef GrayScale
#ifndef FIXX11H_GrayScale
#define FIXX11H_GrayScale
const int XGrayScale = GrayScale;
#undef GrayScale
const int GrayScale = XGrayScale;
#endif
#undef GrayScale
#endif
// Affects: Should be without side effects.
#ifdef Status
#ifndef FIXX11H_Status
#define FIXX11H_Status
typedef Status XStatus;
#undef Status
typedef XStatus Status;
#endif
#undef Status
#endif
// template --->
// Affects: Should be without side effects.
#ifdef CursorShape
#ifndef FIXX11H_CursorShape
#define FIXX11H_CursorShape
const int XCursorShape = CursorShape;
#undef CursorShape
const int CursorShape = XCursorShape;
#endif
#undef CursorShape
#endif
// template <---
// template --->
// Affects: Should be without side effects.
#ifdef FontChange
#ifndef FIXX11H_FontChange
#define FIXX11H_FontChange
const int XFontChange = FontChange;
#undef FontChange
const int FontChange = XFontChange;
#endif
#undef FontChange
#endif
// template <---
// Affects: Should be without side effects.
#ifdef NormalState
#ifndef FIXX11H_NormalState
#define FIXX11H_NormalState
const int XNormalState = NormalState;
#undef NormalState
const int NormalState = XNormalState;
#endif
#undef NormalState
#endif
// template --->
// Affects: Should be without side effects.
#ifdef index
#ifndef FIXX11H_index
#define FIXX11H_index
inline const char *Xindex(const char *s, int c)
{
return index(s, c);
}
#undef index
inline const char *index(const char *s, int c)
{
return Xindex(s, c);
}
#endif
#undef index
#endif
// template <---
#ifdef rindex
// Affects: Should be without side effects.
#ifndef FIXX11H_rindex
#define FIXX11H_rindex
inline const char *Xrindex(const char *s, int c)
{
return rindex(s, c);
}
#undef rindex
inline const char *rindex(const char *s, int c)
{
return Xrindex(s, c);
}
#endif
#undef rindex
#endif
}
using namespace X;
@@ -0,0 +1,615 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#include "kselectionowner.h"
#include "kwindowsystem.h"
#include "kxcbevent_p.h"
#include <config-kwindowsystem.h>
#include <QAbstractNativeEventFilter>
#include <QBasicTimer>
#include <QDebug>
#include <QGuiApplication>
#include <QTimerEvent>
#include <private/qtx11extras_p.h>
static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
{
xcb_window_t owner = XCB_NONE;
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr);
if (reply) {
owner = reply->owner;
free(reply);
}
return owner;
}
static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name)
{
xcb_atom_t atom = XCB_NONE;
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr);
if (reply) {
atom = reply->atom;
free(reply);
}
return atom;
}
class Q_DECL_HIDDEN KSelectionOwner::Private : public QAbstractNativeEventFilter
{
public:
enum State { Idle, WaitingForTimestamp, WaitingForPreviousOwner };
Private(KSelectionOwner *owner_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
: state(Idle)
, selection(selection_P)
, connection(c)
, root(root)
, window(XCB_NONE)
, prev_owner(XCB_NONE)
, timestamp(XCB_CURRENT_TIME)
, extra1(0)
, extra2(0)
, force_kill(false)
, owner(owner_P)
{
QCoreApplication::instance()->installNativeEventFilter(this);
}
void claimSucceeded();
void gotTimestamp();
void timeout();
State state;
const xcb_atom_t selection;
xcb_connection_t *connection;
xcb_window_t root;
xcb_window_t window;
xcb_window_t prev_owner;
xcb_timestamp_t timestamp;
uint32_t extra1, extra2;
QBasicTimer timer;
bool force_kill;
static xcb_atom_t manager_atom;
static xcb_atom_t xa_multiple;
static xcb_atom_t xa_targets;
static xcb_atom_t xa_timestamp;
static Private *create(KSelectionOwner *owner, xcb_atom_t selection_P, int screen_P);
static Private *create(KSelectionOwner *owner, const char *selection_P, int screen_P);
static Private *create(KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
static Private *create(KSelectionOwner *owner, const char *selection_P, xcb_connection_t *c, xcb_window_t root);
protected:
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
{
if (eventType != "xcb_generic_event_t") {
return false;
}
return owner->filterEvent(message);
}
private:
KSelectionOwner *owner;
};
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, xcb_atom_t selection_P, int screen_P)
{
if (KWindowSystem::isPlatformX11()) {
return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
}
qWarning() << "Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
return nullptr;
}
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
{
return new Private(owner, selection_P, c, root);
}
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, const char *selection_P, int screen_P)
{
if (KWindowSystem::isPlatformX11()) {
return create(owner, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
}
qWarning() << "Trying to use KSelectionOwner on a non-X11 platform! This is an application bug.";
return nullptr;
}
KSelectionOwner::Private *KSelectionOwner::Private::create(KSelectionOwner *owner, const char *selection_P, xcb_connection_t *c, xcb_window_t root)
{
return new Private(owner, intern_atom(c, selection_P), c, root);
}
KSelectionOwner::KSelectionOwner(xcb_atom_t selection_P, int screen_P, QObject *parent_P)
: QObject(parent_P)
, d(Private::create(this, selection_P, screen_P))
{
}
KSelectionOwner::KSelectionOwner(const char *selection_P, int screen_P, QObject *parent_P)
: QObject(parent_P)
, d(Private::create(this, selection_P, screen_P))
{
}
KSelectionOwner::KSelectionOwner(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
: QObject(parent)
, d(Private::create(this, selection, c, root))
{
}
KSelectionOwner::KSelectionOwner(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
: QObject(parent)
, d(Private::create(this, selection, c, root))
{
}
KSelectionOwner::~KSelectionOwner()
{
if (d) {
release();
if (d->window != XCB_WINDOW_NONE) {
xcb_destroy_window(d->connection, d->window); // also makes the selection not owned
}
delete d;
}
}
void KSelectionOwner::Private::claimSucceeded()
{
state = Idle;
KXcbEvent<xcb_client_message_event_t> ev;
ev.response_type = XCB_CLIENT_MESSAGE;
ev.format = 32;
ev.window = root;
ev.type = Private::manager_atom;
ev.data.data32[0] = timestamp;
ev.data.data32[1] = selection;
ev.data.data32[2] = window;
ev.data.data32[3] = extra1;
ev.data.data32[4] = extra2;
xcb_send_event(connection, false, root, XCB_EVENT_MASK_STRUCTURE_NOTIFY, ev.buffer());
// qDebug() << "Claimed selection";
Q_EMIT owner->claimedOwnership();
}
void KSelectionOwner::Private::gotTimestamp()
{
Q_ASSERT(state == WaitingForTimestamp);
state = Idle;
xcb_connection_t *c = connection;
// Set the selection owner and immediately verify that the claim was successful
xcb_set_selection_owner(c, window, selection, timestamp);
xcb_window_t new_owner = get_selection_owner(c, selection);
if (new_owner != window) {
// qDebug() << "Failed to claim selection : " << new_owner;
xcb_destroy_window(c, window);
timestamp = XCB_CURRENT_TIME;
window = XCB_NONE;
Q_EMIT owner->failedToClaimOwnership();
return;
}
if (prev_owner != XCB_NONE && force_kill) {
// qDebug() << "Waiting for previous owner to disown";
timer.start(1000, owner);
state = WaitingForPreviousOwner;
// Note: We've already selected for structure notify events
// on the previous owner window
} else {
// If there was no previous owner, we're done
claimSucceeded();
}
}
void KSelectionOwner::Private::timeout()
{
Q_ASSERT(state == WaitingForPreviousOwner);
state = Idle;
if (force_kill) {
// qDebug() << "Killing previous owner";
xcb_connection_t *c = connection;
// Ignore any errors from the kill request
xcb_generic_error_t *err = xcb_request_check(c, xcb_kill_client_checked(c, prev_owner));
free(err);
claimSucceeded();
} else {
Q_EMIT owner->failedToClaimOwnership();
}
}
void KSelectionOwner::claim(bool force_P, bool force_kill_P)
{
if (!d) {
return;
}
Q_ASSERT(d->state == Private::Idle);
if (Private::manager_atom == XCB_NONE) {
getAtoms();
}
if (d->timestamp != XCB_CURRENT_TIME) {
release();
}
xcb_connection_t *c = d->connection;
d->prev_owner = get_selection_owner(c, d->selection);
if (d->prev_owner != XCB_NONE) {
if (!force_P) {
// qDebug() << "Selection already owned, failing";
Q_EMIT failedToClaimOwnership();
return;
}
// Select structure notify events so get an event when the previous owner
// destroys the window
uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
xcb_change_window_attributes(c, d->prev_owner, XCB_CW_EVENT_MASK, &mask);
}
uint32_t values[] = {true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY};
d->window = xcb_generate_id(c);
xcb_create_window(c,
XCB_COPY_FROM_PARENT,
d->window,
d->root,
0,
0,
1,
1,
0,
XCB_WINDOW_CLASS_INPUT_ONLY,
XCB_COPY_FROM_PARENT,
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
values);
// Trigger a property change event so we get a timestamp
xcb_atom_t tmp = XCB_ATOM_ATOM;
xcb_change_property(c, XCB_PROP_MODE_REPLACE, d->window, XCB_ATOM_ATOM, XCB_ATOM_ATOM, 32, 1, (const void *)&tmp);
// Now we have to return to the event loop and wait for the property change event
d->force_kill = force_kill_P;
d->state = Private::WaitingForTimestamp;
}
// destroy resource first
void KSelectionOwner::release()
{
if (!d) {
return;
}
if (d->timestamp == XCB_CURRENT_TIME) {
return;
}
xcb_destroy_window(d->connection, d->window); // also makes the selection not owned
d->window = XCB_NONE;
// qDebug() << "Releasing selection";
d->timestamp = XCB_CURRENT_TIME;
}
xcb_window_t KSelectionOwner::ownerWindow() const
{
if (!d) {
return XCB_WINDOW_NONE;
}
if (d->timestamp == XCB_CURRENT_TIME) {
return XCB_NONE;
}
return d->window;
}
void KSelectionOwner::setData(uint32_t extra1_P, uint32_t extra2_P)
{
if (!d) {
return;
}
d->extra1 = extra1_P;
d->extra2 = extra2_P;
}
bool KSelectionOwner::filterEvent(void *ev_P)
{
if (!d) {
return false;
}
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P);
const uint response_type = event->response_type & ~0x80;
#if 0
// There's no generic way to get the window for an event in xcb, it depends on the type of event
// This handleMessage virtual doesn't seem used anyway.
if (d->timestamp != CurrentTime && ev_P->xany.window == d->window) {
if (handleMessage(ev_P)) {
return true;
}
}
#endif
switch (response_type) {
case XCB_SELECTION_CLEAR: {
xcb_selection_clear_event_t *ev = reinterpret_cast<xcb_selection_clear_event_t *>(event);
if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
return false;
}
d->timestamp = XCB_CURRENT_TIME;
// qDebug() << "Lost selection";
xcb_window_t window = d->window;
Q_EMIT lostOwnership();
// Unset the event mask before we destroy the window so we don't get a destroy event
uint32_t event_mask = XCB_NONE;
xcb_change_window_attributes(d->connection, window, XCB_CW_EVENT_MASK, &event_mask);
xcb_destroy_window(d->connection, window);
return true;
}
case XCB_DESTROY_NOTIFY: {
xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event);
if (ev->window == d->prev_owner) {
if (d->state == Private::WaitingForPreviousOwner) {
d->timer.stop();
d->claimSucceeded();
return true;
}
// It is possible for the previous owner to be destroyed
// while we're waiting for the timestamp
d->prev_owner = XCB_NONE;
}
if (d->timestamp == XCB_CURRENT_TIME || ev->window != d->window) {
return false;
}
d->timestamp = XCB_CURRENT_TIME;
// qDebug() << "Lost selection (destroyed)";
Q_EMIT lostOwnership();
return true;
}
case XCB_SELECTION_NOTIFY: {
xcb_selection_notify_event_t *ev = reinterpret_cast<xcb_selection_notify_event_t *>(event);
if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
return false;
}
// ignore?
return false;
}
case XCB_SELECTION_REQUEST:
filter_selection_request(event);
return false;
case XCB_PROPERTY_NOTIFY: {
xcb_property_notify_event_t *ev = reinterpret_cast<xcb_property_notify_event_t *>(event);
if (ev->window == d->window && d->state == Private::WaitingForTimestamp) {
d->timestamp = ev->time;
d->gotTimestamp();
return true;
}
return false;
}
default:
return false;
}
}
void KSelectionOwner::timerEvent(QTimerEvent *event)
{
if (!d) {
QObject::timerEvent(event);
return;
}
if (event->timerId() == d->timer.timerId()) {
d->timer.stop();
d->timeout();
return;
}
QObject::timerEvent(event);
}
#if 0
bool KSelectionOwner::handleMessage(XEvent *)
{
return false;
}
#endif
void KSelectionOwner::filter_selection_request(void *event)
{
if (!d) {
return;
}
xcb_selection_request_event_t *ev = reinterpret_cast<xcb_selection_request_event_t *>(event);
if (d->timestamp == XCB_CURRENT_TIME || ev->selection != d->selection) {
return;
}
if (ev->time != XCB_CURRENT_TIME && ev->time - d->timestamp > 1U << 31) {
return; // too old or too new request
}
// qDebug() << "Got selection request";
xcb_connection_t *c = d->connection;
bool handled = false;
if (ev->target == Private::xa_multiple) {
if (ev->property != XCB_NONE) {
const int MAX_ATOMS = 100;
xcb_get_property_cookie_t cookie = xcb_get_property(c, false, ev->requestor, ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, MAX_ATOMS);
xcb_get_property_reply_t *reply = xcb_get_property_reply(c, cookie, nullptr);
if (reply && reply->format == 32 && reply->value_len % 2 == 0) {
xcb_atom_t *atoms = reinterpret_cast<xcb_atom_t *>(xcb_get_property_value(reply));
bool handled_array[MAX_ATOMS];
for (uint i = 0; i < reply->value_len / 2; i++) {
handled_array[i] = handle_selection(atoms[i * 2], atoms[i * 2 + 1], ev->requestor);
}
bool all_handled = true;
for (uint i = 0; i < reply->value_len / 2; i++) {
if (!handled_array[i]) {
all_handled = false;
atoms[i * 2 + 1] = XCB_NONE;
}
}
if (!all_handled) {
xcb_change_property(c,
ev->requestor,
ev->property,
XCB_ATOM_ATOM,
32,
XCB_PROP_MODE_REPLACE,
reply->value_len,
reinterpret_cast<const void *>(atoms));
}
handled = true;
}
if (reply) {
free(reply);
}
}
} else {
if (ev->property == XCB_NONE) { // obsolete client
ev->property = ev->target;
}
handled = handle_selection(ev->target, ev->property, ev->requestor);
}
KXcbEvent<xcb_selection_notify_event_t> xev;
xev.response_type = XCB_SELECTION_NOTIFY;
xev.selection = ev->selection;
xev.requestor = ev->requestor;
xev.target = ev->target;
xev.property = handled ? ev->property : XCB_NONE;
xcb_send_event(c, false, ev->requestor, 0, xev.buffer());
}
bool KSelectionOwner::handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P)
{
if (!d) {
return false;
}
if (target_P == Private::xa_timestamp) {
// qDebug() << "Handling timestamp request";
xcb_change_property(d->connection,
requestor_P,
property_P,
XCB_ATOM_INTEGER,
32,
XCB_PROP_MODE_REPLACE,
1,
reinterpret_cast<const void *>(&d->timestamp));
} else if (target_P == Private::xa_targets) {
replyTargets(property_P, requestor_P);
} else if (genericReply(target_P, property_P, requestor_P)) {
// handled
} else {
return false; // unknown
}
return true;
}
void KSelectionOwner::replyTargets(xcb_atom_t property_P, xcb_window_t requestor_P)
{
if (!d) {
return;
}
xcb_atom_t atoms[3] = {Private::xa_multiple, Private::xa_timestamp, Private::xa_targets};
xcb_change_property(d->connection,
requestor_P,
property_P,
XCB_ATOM_ATOM,
32,
XCB_PROP_MODE_REPLACE,
sizeof(atoms) / sizeof(atoms[0]),
reinterpret_cast<const void *>(atoms));
// qDebug() << "Handling targets request";
}
bool KSelectionOwner::genericReply(xcb_atom_t, xcb_atom_t, xcb_window_t)
{
return false;
}
void KSelectionOwner::getAtoms()
{
if (!d) {
return;
}
if (Private::manager_atom != XCB_NONE) {
return;
}
xcb_connection_t *c = d->connection;
struct {
const char *name;
xcb_atom_t *atom;
} atoms[] = {{"MANAGER", &Private::manager_atom},
{"MULTIPLE", &Private::xa_multiple},
{"TARGETS", &Private::xa_targets},
{"TIMESTAMP", &Private::xa_timestamp}};
const int count = sizeof(atoms) / sizeof(atoms[0]);
xcb_intern_atom_cookie_t cookies[count];
for (int i = 0; i < count; i++) {
cookies[i] = xcb_intern_atom(c, false, strlen(atoms[i].name), atoms[i].name);
}
for (int i = 0; i < count; i++) {
if (xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, cookies[i], nullptr)) {
*atoms[i].atom = reply->atom;
free(reply);
}
}
}
xcb_atom_t KSelectionOwner::Private::manager_atom = XCB_NONE;
xcb_atom_t KSelectionOwner::Private::xa_multiple = XCB_NONE;
xcb_atom_t KSelectionOwner::Private::xa_targets = XCB_NONE;
xcb_atom_t KSelectionOwner::Private::xa_timestamp = XCB_NONE;
#include "moc_kselectionowner.cpp"
@@ -0,0 +1,188 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef KSELECTIONOWNER_H
#define KSELECTIONOWNER_H
#include <QObject>
#include <kwindowsystem_export.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
/**
This class implements claiming and owning manager selections, as described
in the ICCCM, section 2.8. The selection atom is passed to the constructor,
claim() attempts to claim ownership of the selection, release() gives up
the selection ownership. Signal lostOwnership() is emitted when the selection
is claimed by another owner.
@short ICCCM manager selection owner
This class is only useful on the xcb platform. On other platforms the code is only
functional if the constructor overloads taking an xcb_connection_t are used. In case
you inherit from this class ensure that you don't use xcb and/or XLib without verifying
the platform.
*/
class KWINDOWSYSTEM_EXPORT KSelectionOwner : public QObject
{
Q_OBJECT
public:
/**
* This constructor initializes the object, but doesn't perform any
* operation on the selection.
*
* @param selection atom representing the manager selection
* @param screen X screen, or -1 for default
* @param parent parent object, or nullptr if there is none
*/
explicit KSelectionOwner(xcb_atom_t selection, int screen = -1, QObject *parent = nullptr);
/**
* @overload
* This constructor accepts the selection name and creates the appropriate atom
* for it automatically.
*
* @param selection name of the manager selection
* @param screen X screen, or -1 for default
* @param parent parent object, or nullptr if there is none
*/
explicit KSelectionOwner(const char *selection, int screen = -1, QObject *parent = nullptr);
/**
* @overload
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
* running on the xcb platform. Otherwise this constructor behaves like the similar one
* without the xcb_connection_t.
*
* @param selection atom representing the manager selection
* @param c the xcb connection this KSelectionWatcher should use
* @param root the root window this KSelectionWatcher should use
* @param parent parent object, or nullptr if there is none
* @since 5.8
**/
explicit KSelectionOwner(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
/**
* @overload
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
* running on the xcb platform. Otherwise this constructor behaves like the similar one
* without the xcb_connection_t.
*
* @param selection name of the manager selection
* @param c the xcb connection this KSelectionWatcher should use
* @param root the root window this KSelectionWatcher should use
* @param parent parent object, or nullptr if there is none
* @since 5.8
**/
explicit KSelectionOwner(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
/**
* Destructor. Calls release().
*/
~KSelectionOwner() override;
/**
* Try to claim ownership of the manager selection using the current X timestamp.
*
* This function returns immediately, but it may take up to one second for the claim
* to succeed or fail, at which point either the claimedOwnership() or
* failedToClaimOwnership() signal is emitted. The claim will not be completed until
* the caller has returned to the event loop.
*
* If @p force is false, and the selection is already owned, the selection is not claimed,
* and failedToClaimOwnership() is emitted. If @p force is true and the selection is
* owned by another client, the client will be given one second to relinquish ownership
* of the selection. If @p force_kill is true, and the previous owner fails to disown
* the selection in time, it will be forcibly killed.
*/
void claim(bool force, bool force_kill = true);
/**
* If the selection is owned, the ownership is given up.
*/
void release();
/**
* If the selection is owned, returns the window used internally
* for owning the selection.
*/
xcb_window_t ownerWindow() const; // None if not owning the selection
/**
* @internal
*/
bool filterEvent(void *ev_P); // internal
/**
* @internal
*/
void timerEvent(QTimerEvent *event) override;
Q_SIGNALS:
/**
* This signal is emitted if the selection was owned and the ownership
* has been lost due to another client claiming it, this signal is emitted.
* IMPORTANT: It's not safe to delete the instance in a slot connected
* to this signal.
*/
void lostOwnership();
/**
* This signal is emitted when claim() was successful in claiming
* ownership of the selection.
*/
void claimedOwnership();
/**
* This signal is emitted when claim() failed to claim ownership
* of the selection.
*/
void failedToClaimOwnership();
protected:
/**
* Called for every X event received on the window used for owning
* the selection. If true is returned, the event is filtered out.
*/
// virtual bool handleMessage( XEvent* ev ); // removed for KF5, please shout if you need this
/**
* Called when a SelectionRequest event is received. A reply should
* be sent using the selection handling mechanism described in the ICCCM
* section 2.
*
* @param target requested target type
* @param property property to use for the reply data
* @param requestor requestor window
*/
virtual bool genericReply(xcb_atom_t target, xcb_atom_t property, xcb_window_t requestor);
/**
* Called to announce the supported targets, as described in the ICCCM
* section 2.6. The default implementation announces the required targets
* MULTIPLE, TIMESTAMP and TARGETS.
*/
virtual void replyTargets(xcb_atom_t property, xcb_window_t requestor);
/**
* Called to create atoms needed for claiming the selection and
* communication using the selection handling mechanism. The default
* implementation must be called if reimplemented. This method
* may be called repeatedly.
*/
virtual void getAtoms();
/**
* Sets extra data to be sent in the message sent to root window
* after successfully claiming a selection. These extra data
* are in data.l[3] and data.l[4] fields of the XClientMessage.
*/
void setData(uint32_t extra1, uint32_t extra2);
private:
void filter_selection_request(void *ev_P);
bool handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, xcb_window_t requestor_P);
class Private;
Private *const d;
};
#endif
@@ -0,0 +1,246 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#include "kselectionwatcher.h"
#include "kwindowsystem.h"
#include <config-kwindowsystem.h>
#include <QAbstractNativeEventFilter>
#include <QCoreApplication>
#include <private/qtx11extras_p.h>
static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t selection)
{
xcb_window_t owner = XCB_NONE;
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(c, xcb_get_selection_owner(c, selection), nullptr);
if (reply) {
owner = reply->owner;
free(reply);
}
return owner;
}
static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name)
{
xcb_atom_t atom = XCB_NONE;
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(c, xcb_intern_atom(c, false, strlen(name), name), nullptr);
if (reply) {
atom = reply->atom;
free(reply);
}
return atom;
}
//*******************************************
// KSelectionWatcher
//*******************************************
class Q_DECL_HIDDEN KSelectionWatcher::Private : public QAbstractNativeEventFilter
{
public:
Private(KSelectionWatcher *watcher_P, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
: connection(c)
, root(root)
, selection(selection_P)
, selection_owner(XCB_NONE)
, watcher(watcher_P)
{
QCoreApplication::instance()->installNativeEventFilter(this);
}
xcb_connection_t *connection;
xcb_window_t root;
const xcb_atom_t selection;
xcb_window_t selection_owner;
static xcb_atom_t manager_atom;
static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P);
static Private *create(KSelectionWatcher *watcher, const char *selection_P, int screen_P);
static Private *create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root);
static Private *create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root);
protected:
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
{
if (eventType != "xcb_generic_event_t") {
return false;
}
watcher->filterEvent(message);
return false;
}
private:
KSelectionWatcher *watcher;
};
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, int screen_P)
{
if (KWindowSystem::isPlatformX11()) {
return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
}
return nullptr;
}
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, xcb_atom_t selection_P, xcb_connection_t *c, xcb_window_t root)
{
return new Private(watcher, selection_P, c, root);
}
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, int screen_P)
{
if (KWindowSystem::isPlatformX11()) {
return create(watcher, selection_P, QX11Info::connection(), QX11Info::appRootWindow(screen_P));
}
return nullptr;
}
KSelectionWatcher::Private *KSelectionWatcher::Private::create(KSelectionWatcher *watcher, const char *selection_P, xcb_connection_t *c, xcb_window_t root)
{
return new Private(watcher, intern_atom(c, selection_P), c, root);
}
KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection_P, int screen_P, QObject *parent_P)
: QObject(parent_P)
, d(Private::create(this, selection_P, screen_P))
{
init();
}
KSelectionWatcher::KSelectionWatcher(const char *selection_P, int screen_P, QObject *parent_P)
: QObject(parent_P)
, d(Private::create(this, selection_P, screen_P))
{
init();
}
KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
: QObject(parent)
, d(Private::create(this, selection, c, root))
{
init();
}
KSelectionWatcher::KSelectionWatcher(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent)
: QObject(parent)
, d(Private::create(this, selection, c, root))
{
init();
}
KSelectionWatcher::~KSelectionWatcher()
{
delete d;
}
void KSelectionWatcher::init()
{
if (!d) {
return;
}
if (Private::manager_atom == XCB_NONE) {
xcb_connection_t *c = d->connection;
xcb_intern_atom_cookie_t atom_cookie = xcb_intern_atom(c, false, strlen("MANAGER"), "MANAGER");
xcb_get_window_attributes_cookie_t attr_cookie = xcb_get_window_attributes(c, d->root);
xcb_intern_atom_reply_t *atom_reply = xcb_intern_atom_reply(c, atom_cookie, nullptr);
Private::manager_atom = atom_reply->atom;
free(atom_reply);
xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(c, attr_cookie, nullptr);
uint32_t event_mask = attr->your_event_mask;
free(attr);
if (!(event_mask & XCB_EVENT_MASK_STRUCTURE_NOTIFY)) {
// We need XCB_EVENT_MASK_STRUCTURE_NORITY on the root window
event_mask |= XCB_EVENT_MASK_STRUCTURE_NOTIFY;
xcb_change_window_attributes(c, d->root, XCB_CW_EVENT_MASK, &event_mask);
}
}
owner(); // trigger reading of current selection status
}
xcb_window_t KSelectionWatcher::owner()
{
if (!d) {
return XCB_WINDOW_NONE;
}
xcb_connection_t *c = d->connection;
xcb_window_t current_owner = get_selection_owner(c, d->selection);
if (current_owner == XCB_NONE) {
return XCB_NONE;
}
if (current_owner == d->selection_owner) {
return d->selection_owner;
}
// We have a new selection owner - select for structure notify events
uint32_t mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
xcb_void_cookie_t cookie = xcb_change_window_attributes_checked(c, current_owner, XCB_CW_EVENT_MASK, &mask);
// Verify that the owner didn't change again while selecting for events
xcb_window_t new_owner = get_selection_owner(c, d->selection);
xcb_generic_error_t *err = xcb_request_check(c, cookie);
if (!err && current_owner == new_owner) {
d->selection_owner = current_owner;
Q_EMIT newOwner(d->selection_owner);
} else {
// ### This doesn't look right - the selection could have an owner
d->selection_owner = XCB_NONE;
}
if (err) {
free(err);
}
return d->selection_owner;
}
void KSelectionWatcher::filterEvent(void *ev_P)
{
if (!d) {
return;
}
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(ev_P);
const uint response_type = event->response_type & ~0x80;
if (response_type == XCB_CLIENT_MESSAGE) {
xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event);
if (cm_event->type != Private::manager_atom || cm_event->data.data32[1] != d->selection) {
return;
}
// owner() checks whether the owner changed and emits newOwner()
owner();
return;
}
if (response_type == XCB_DESTROY_NOTIFY) {
xcb_destroy_notify_event_t *ev = reinterpret_cast<xcb_destroy_notify_event_t *>(event);
if (d->selection_owner == XCB_NONE || ev->window != d->selection_owner) {
return;
}
d->selection_owner = XCB_NONE; // in case the exactly same ID gets reused as the owner
if (owner() == XCB_NONE) {
Q_EMIT lostOwner(); // it must be safe to delete 'this' in a slot
}
return;
}
}
xcb_atom_t KSelectionWatcher::Private::manager_atom = XCB_NONE;
#include "moc_kselectionwatcher.cpp"
@@ -0,0 +1,110 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef KSELECTIONWATCHER_H
#define KSELECTIONWATCHER_H
#include <QObject>
#include <kwindowsystem_export.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
/**
This class implements watching manager selections, as described in the ICCCM
section 2.8. It emits signal newOwner() when a new owner claim the selection,
and emits lostOwner() when the selection ownership is given up. To find
out current owner of the selection, owner() can be used.
@short ICCCM manager selection watching
This class is only useful on the xcb platform. On other platforms the code is only
functional if the constructor overloads taking an xcb_connection_t are used. In case
you inherit from this class ensure that you don't use xcb and/or XLib without verifying
the platform.
*/
class KWINDOWSYSTEM_EXPORT KSelectionWatcher : public QObject
{
Q_OBJECT
public:
/**
* This constructor initializes the object, but doesn't perform any
* operation on the selection.
*
* @param selection atom representing the manager selection
* @param screen X screen, or -1 for default
* @param parent parent object, or nullptr if there is none
*/
explicit KSelectionWatcher(xcb_atom_t selection, int screen = -1, QObject *parent = nullptr);
/**
* @overload
* This constructor accepts the selection name and creates the appropriate atom
* for it automatically.
*
* @param selection name of the manager selection
* @param screen X screen, or -1 for default
* @param parent parent object, or nullptr if there is none
*/
explicit KSelectionWatcher(const char *selection, int screen = -1, QObject *parent = nullptr);
/**
* @overload
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
* running on the xcb platform. Otherwise this constructor behaves like the similar one
* without the xcb_connection_t.
*
* @param selection atom representing the manager selection
* @param c the xcb connection this KSelectionWatcher should use
* @param root the root window this KSelectionWatcher should use
* @since 5.8
**/
explicit KSelectionWatcher(xcb_atom_t selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
/**
* @overload
* This constructor accepts the xcb_connection_t and root window and doesn't depend on
* running on the xcb platform. Otherwise this constructor behaves like the similar one
* without the xcb_connection_t.
*
* @param selection name of the manager selection
* @param c the xcb connection this KSelectionWatcher should use
* @param root the root window this KSelectionWatcher should use
* @since 5.8
**/
explicit KSelectionWatcher(const char *selection, xcb_connection_t *c, xcb_window_t root, QObject *parent = nullptr);
~KSelectionWatcher() override;
/**
* Return the current owner of the manager selection, if any. Note that if the event
* informing about the owner change is still in the input queue, newOwner() might
* have been emitted yet.
*/
xcb_window_t owner();
/**
* @internal
*/
void filterEvent(void *ev_P); // internal
Q_SIGNALS:
/**
* This signal is emitted when the selection is successfully claimed by a new
* owner.
* @param owner the new owner of the selection
*/
void newOwner(xcb_window_t owner);
/**
* This signal is emitted when the selection is given up, i.e. there's no
* owner. Note that the selection may be immediately claimed again,
* so the newOwner() signal may be emitted right after this one.
* It's safe to delete the instance in a slot connected to this signal.
*/
void lostOwner();
private:
void init();
class Private;
Private *const d;
};
Q_DECLARE_METATYPE(xcb_window_t)
#endif
@@ -0,0 +1,211 @@
/*
SPDX-FileCopyrightText: 2009 Marco Martin <notmart@gmail.com>
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwindoweffects_x11.h"
#include <QGuiApplication>
#include <QVarLengthArray>
#include "kx11extras.h"
#include <config-kwindowsystem.h>
#include <QMatrix4x4>
#include <QWindow>
#include <private/qtx11extras_p.h>
#include <xcb/xcb.h>
#include "cptr_p.h"
#include <cmath>
using namespace KWindowEffects;
KWindowEffectsPrivateX11::KWindowEffectsPrivateX11()
{
}
KWindowEffectsPrivateX11::~KWindowEffectsPrivateX11()
{
}
bool KWindowEffectsPrivateX11::isEffectAvailable(Effect effect)
{
if (!KX11Extras::self()->compositingActive()) {
return false;
}
QByteArray effectName;
switch (effect) {
case Slide:
effectName = QByteArrayLiteral("_KDE_SLIDE");
break;
case BlurBehind:
effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
break;
case BackgroundContrast:
effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
break;
default:
return false;
}
// hackish way to find out if KWin has the effect enabled,
// TODO provide proper support
xcb_connection_t *c = QX11Info::connection();
xcb_list_properties_cookie_t propsCookie = xcb_list_properties_unchecked(c, QX11Info::appRootWindow());
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
UniqueCPointer<xcb_list_properties_reply_t> props(xcb_list_properties_reply(c, propsCookie, nullptr));
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
if (!atom || !props) {
return false;
}
xcb_atom_t *atoms = xcb_list_properties_atoms(props.get());
for (int i = 0; i < props->atoms_len; ++i) {
if (atoms[i] == atom->atom) {
return true;
}
}
return false;
}
void KWindowEffectsPrivateX11::slideWindow(QWindow *window, SlideFromLocation location, int offset)
{
xcb_connection_t *c = QX11Info::connection();
if (!c) {
return;
}
const QByteArray effectName = QByteArrayLiteral("_KDE_SLIDE");
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
const int size = 2;
int32_t data[size];
data[0] = offset;
switch (location) {
case LeftEdge:
data[1] = 0;
break;
case TopEdge:
data[1] = 1;
break;
case RightEdge:
data[1] = 2;
break;
case BottomEdge:
data[1] = 3;
default:
break;
}
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
if (!atom) {
return;
}
if (location == NoEdge) {
xcb_delete_property(c, window->winId(), atom->atom);
} else {
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, atom->atom, 32, size, data);
}
}
void KWindowEffectsPrivateX11::enableBlurBehind(QWindow *window, bool enable, const QRegion &region)
{
xcb_connection_t *c = QX11Info::connection();
if (!c) {
return;
}
const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION");
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
if (!atom) {
return;
}
if (enable) {
QList<uint32_t> data;
data.reserve(region.rectCount() * 4);
for (const QRect &r : region) {
// kwin on X uses device pixels, convert from logical
auto dpr = qApp->devicePixelRatio();
data << std::floor(r.x() * dpr) << std::floor(r.y() * dpr) << std::ceil(r.width() * dpr) << std::ceil(r.height() * dpr);
}
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
} else {
xcb_delete_property(c, window->winId(), atom->atom);
}
}
void KWindowEffectsPrivateX11::enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion &region)
{
xcb_connection_t *c = QX11Info::connection();
const QByteArray effectName = QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION");
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, effectName.length(), effectName.constData());
UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr));
if (!atom) {
return;
}
if (enable) {
QList<uint32_t> data;
data.reserve(region.rectCount() * 4 + 16);
for (const QRect &r : region) {
auto dpr = qApp->devicePixelRatio();
data << std::floor(r.x() * dpr) << std::floor(r.y() * dpr) << std::ceil(r.width() * dpr) << std::ceil(r.height() * dpr);
}
QMatrix4x4 satMatrix; // saturation
QMatrix4x4 intMatrix; // intensity
QMatrix4x4 contMatrix; // contrast
// clang-format off
//Saturation matrix
if (!qFuzzyCompare(saturation, 1.0)) {
const qreal rval = (1.0 - saturation) * .2126;
const qreal gval = (1.0 - saturation) * .7152;
const qreal bval = (1.0 - saturation) * .0722;
satMatrix = QMatrix4x4(rval + saturation, rval, rval, 0.0,
gval, gval + saturation, gval, 0.0,
bval, bval, bval + saturation, 0.0,
0, 0, 0, 1.0);
}
//IntensityMatrix
if (!qFuzzyCompare(intensity, 1.0)) {
intMatrix.scale(intensity, intensity, intensity);
}
//Contrast Matrix
if (!qFuzzyCompare(contrast, 1.0)) {
const float transl = (1.0 - contrast) / 2.0;
contMatrix = QMatrix4x4(contrast, 0, 0, 0.0,
0, contrast, 0, 0.0,
0, 0, contrast, 0.0,
transl, transl, transl, 1.0);
}
// clang-format on
QMatrix4x4 colorMatrix = contMatrix * satMatrix * intMatrix;
colorMatrix = colorMatrix.transposed();
uint32_t *rawData = reinterpret_cast<uint32_t *>(colorMatrix.data());
for (int i = 0; i < 16; ++i) {
data << rawData[i];
}
xcb_change_property(c, XCB_PROP_MODE_REPLACE, window->winId(), atom->atom, atom->atom, 32, data.size(), data.constData());
} else {
xcb_delete_property(c, window->winId(), atom->atom);
}
}
@@ -0,0 +1,27 @@
/*
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
*/
#ifndef KWINDOWEFFECTS_X11_H
#define KWINDOWEFFECTS_X11_H
#include "kwindoweffects_p.h"
class KWindowEffectsPrivateX11 : public KWindowEffectsPrivate
{
public:
KWindowEffectsPrivateX11();
~KWindowEffectsPrivateX11() override;
bool isEffectAvailable(KWindowEffects::Effect effect) override;
void slideWindow(QWindow *window, KWindowEffects::SlideFromLocation location, int offset) override;
void enableBlurBehind(QWindow *window, bool enable = true, const QRegion &region = QRegion()) override;
void enableBackgroundContrast(QWindow *window,
bool enable = true,
qreal contrast = 1,
qreal intensity = 1,
qreal saturation = 1,
const QRegion &region = QRegion()) override;
};
#endif
@@ -0,0 +1,218 @@
/*
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwindowshadow_p_x11.h"
#include <private/qtx11extras_p.h>
static const QByteArray s_atomName = QByteArrayLiteral("_KDE_NET_WM_SHADOW");
bool KWindowShadowTilePrivateX11::create()
{
xcb_connection_t *connection = QX11Info::connection();
xcb_window_t rootWindow = QX11Info::appRootWindow();
const uint16_t width = uint16_t(image.width());
const uint16_t height = uint16_t(image.height());
const uint8_t depth = uint8_t(image.depth());
pixmap = xcb_generate_id(connection);
gc = xcb_generate_id(connection);
xcb_create_pixmap(connection, depth, pixmap, rootWindow, width, height);
xcb_create_gc(connection, gc, pixmap, 0, nullptr);
xcb_put_image(connection, //
XCB_IMAGE_FORMAT_Z_PIXMAP,
pixmap,
gc,
width,
height,
0,
0,
0,
depth,
image.sizeInBytes(),
image.constBits());
return true;
}
void KWindowShadowTilePrivateX11::destroy()
{
xcb_connection_t *connection = QX11Info::connection();
if (connection) {
xcb_free_pixmap(connection, pixmap);
xcb_free_gc(connection, gc);
}
pixmap = XCB_PIXMAP_NONE;
gc = XCB_NONE;
}
KWindowShadowTilePrivateX11 *KWindowShadowTilePrivateX11::get(const KWindowShadowTile *tile)
{
KWindowShadowTilePrivate *d = KWindowShadowTilePrivate::get(tile);
return static_cast<KWindowShadowTilePrivateX11 *>(d);
}
static xcb_atom_t lookupAtom(const QByteArray &atomName)
{
xcb_connection_t *connection = QX11Info::connection();
if (!connection) {
return XCB_ATOM_NONE;
}
xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(connection, //
false,
atomName.size(),
atomName.constData());
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, atomCookie, nullptr);
if (!reply) {
return XCB_ATOM_NONE;
}
xcb_atom_t atom = reply->atom;
free(reply);
return atom;
}
static xcb_pixmap_t nativeHandleForTile(const KWindowShadowTile::Ptr &tile)
{
const auto d = KWindowShadowTilePrivateX11::get(tile.data());
return d->pixmap;
}
bool KWindowShadowPrivateX11::create()
{
xcb_connection_t *connection = QX11Info::connection();
const xcb_atom_t atom = lookupAtom(s_atomName);
if (atom == XCB_ATOM_NONE) {
return false;
}
QList<quint32> data(12);
int i = 0;
// Unfortunately we cannot use handle of XCB_PIXMAP_NONE for missing shadow tiles because
// KWin expects **all** shadow tile handles to be valid. Maybe we could address this small
// inconvenience and then remove the empty tile stuff.
if (topTile) {
data[i++] = nativeHandleForTile(topTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (topRightTile) {
data[i++] = nativeHandleForTile(topRightTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (rightTile) {
data[i++] = nativeHandleForTile(rightTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (bottomRightTile) {
data[i++] = nativeHandleForTile(bottomRightTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (bottomTile) {
data[i++] = nativeHandleForTile(bottomTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (bottomLeftTile) {
data[i++] = nativeHandleForTile(bottomLeftTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (leftTile) {
data[i++] = nativeHandleForTile(leftTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (topLeftTile) {
data[i++] = nativeHandleForTile(topLeftTile);
} else {
data[i++] = nativeHandleForTile(getOrCreateEmptyTile());
}
if (topLeftTile || topTile || topRightTile) {
data[i++] = uint32_t(padding.top());
} else {
data[i++] = 1;
}
if (topRightTile || rightTile || bottomRightTile) {
data[i++] = uint32_t(padding.right());
} else {
data[i++] = 1;
}
if (bottomRightTile || bottomTile || bottomLeftTile) {
data[i++] = uint32_t(padding.bottom());
} else {
data[i++] = 1;
}
if (bottomLeftTile || leftTile || topLeftTile) {
data[i++] = uint32_t(padding.left());
} else {
data[i++] = 1;
}
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window->winId(), atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData());
xcb_flush(connection);
return true;
}
void KWindowShadowPrivateX11::destroy()
{
emptyTile = nullptr;
// For some reason, QWindow changes visibility of QSurface::surfaceHandle().
const QSurface *surface = window;
// Attempting to uninstall the shadow after the platform window had been destroyed.
if (!(surface && surface->surfaceHandle())) {
return;
}
xcb_connection_t *connection = QX11Info::connection();
const xcb_atom_t atom = lookupAtom(s_atomName);
if (atom == XCB_ATOM_NONE) {
return;
}
xcb_delete_property(connection, window->winId(), atom);
}
KWindowShadowTile::Ptr KWindowShadowPrivateX11::getOrCreateEmptyTile()
{
if (!emptyTile) {
QImage image(QSize(1, 1), QImage::Format_ARGB32);
image.fill(Qt::transparent);
emptyTile = KWindowShadowTile::Ptr::create();
emptyTile->setImage(image);
emptyTile->create();
}
return emptyTile;
}
@@ -0,0 +1,37 @@
/*
SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWSHADOW_P_X11_H
#define KWINDOWSHADOW_P_X11_H
#include "kwindowshadow_p.h"
#include <xcb/xcb.h>
class KWindowShadowTilePrivateX11 final : public KWindowShadowTilePrivate
{
public:
bool create() override;
void destroy() override;
static KWindowShadowTilePrivateX11 *get(const KWindowShadowTile *tile);
xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
xcb_gcontext_t gc = XCB_NONE;
};
class KWindowShadowPrivateX11 final : public KWindowShadowPrivate
{
public:
bool create() override;
void destroy() override;
KWindowShadowTile::Ptr getOrCreateEmptyTile();
KWindowShadowTile::Ptr emptyTile;
};
#endif // KWINDOWSHADOW_P_X11_H
@@ -0,0 +1,27 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 1999 Matthias Ettrich <ettrich@kde.org>
SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "kwindowsystem_p_x11.h"
#include "kx11extras.h"
void KWindowSystemPrivateX11::activateWindow(QWindow *win, long time)
{
KX11Extras::activateWindow(win->winId(), time);
}
bool KWindowSystemPrivateX11::showingDesktop()
{
return KX11Extras::showingDesktop();
}
void KWindowSystemPrivateX11::setShowingDesktop(bool showing)
{
KX11Extras::setShowingDesktop(showing);
}
@@ -0,0 +1,20 @@
/*
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
*/
#ifndef KWINDOWSYSTEM_P_X11_H
#define KWINDOWSYSTEM_P_X11_H
#include "kwindowsystem_p.h"
class KWindowSystemPrivateX11 : public KWindowSystemPrivate
{
public:
void activateWindow(QWindow *win, long time) override;
bool showingDesktop() override;
void setShowingDesktop(bool showing) override;
};
#endif
@@ -0,0 +1,94 @@
/*
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef KXERRORHANDLER_H
#define KXERRORHANDLER_H
#include <config-kwindowsystem.h>
#include <QtGlobal>
#include <private/qtx11extras_p.h>
#include <X11/Xlib.h>
class KXErrorHandlerPrivate;
/**
* This class simplifies handling of X errors. It shouldn't be necessary to use
* with Qt classes, as the toolkit should handle X errors itself, so this
* class will be mainly used with direct Xlib usage, and some lowlevel classes
* like NETWinInfo.
*
* The usual usage is to create a KXErrorHandler instance right before starting
* operations that might cause X errors, and checking if there was an error
* by calling error() after the operations are finished. The handlers
* may be nested, but must be destroyed in reverse order they were created.
*
* There's no need to do X sync before creating an instance, every instance
* will handle only errors for request issued after the instance was created.
* Errors for older requests will be passed to previous error handler.
* When checking for error by calling error() at the end, it is necessary
* to sync with X, to catch all errors that were caused by requests issued
* before the call to error(). This can be done by passing true to error()
* to cause explicit XSync(), however, if the last X request needed a roundtrip
* (e.g. XGetWindowAttributes(), XGetGeometry(), etc.), it is not required
* to do an explicit sync.
*
* @author Lubos Lunak <l.lunak@kde.org>
* @short Handler for X errors
*/
class KXErrorHandler
{
public:
/**
* Creates error handler that will set error flag after encountering
* any X error.
*/
explicit KXErrorHandler(Display *dpy = QX11Info::display());
/**
* This constructor takes pointer to a function whose prototype matches
* the one that's used with the XSetErrorHandler() Xlib function.
* NOTE: For the error flag to be set, the function must return a non-zero
* value.
*/
explicit KXErrorHandler(int (*handler)(Display *, XErrorEvent *), Display *dpy = QX11Info::display());
/**
* This function returns true if the error flag is set (i.e. no custom handler
* function was used and there was any error, or the custom handler indicated
* an error by its return value).
*
* @param sync if true, an explicit XSync() will be done. Not necessary
* when the last X request required a roundtrip.
*/
bool error(bool sync) const;
/**
* This function returns the error event for the first X error that occurred.
* The return value is useful only if error() returned true.
* @since 4.0.1
*/
XErrorEvent errorEvent() const;
/**
* Returns error message for the given error. The error message is not translated,
* as it is meant for debugging.
* @since 4.0.1
*/
static QByteArray errorMessage(const XErrorEvent &e, Display *dpy = QX11Info::display());
~KXErrorHandler();
private:
void addHandler();
int handle(Display *dpy, XErrorEvent *e);
bool (*user_handler1)(int request, int error_code, unsigned long resource_id);
int (*user_handler2)(Display *, XErrorEvent *);
int (*old_handler)(Display *, XErrorEvent *);
static int handler_wrapper(Display *, XErrorEvent *);
static KXErrorHandler **handlers;
static int pos;
static int size;
Q_DISABLE_COPY(KXErrorHandler)
KXErrorHandlerPrivate *const d;
};
#endif
@@ -0,0 +1,267 @@
/*
SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
SPDX-License-Identifier: MIT
*/
#include "kxmessages.h"
#include "cptr_p.h"
#include "kxutils_p.h"
#include "kxcbevent_p.h"
#if KWINDOWSYSTEM_HAVE_X11
#include <QAbstractNativeEventFilter>
#include <QCoreApplication>
#include <QDebug>
#include <QWindow> // WId
#include <X11/Xlib.h>
#include <private/qtx11extras_p.h>
class XcbAtom
{
public:
explicit XcbAtom(const QByteArray &name, bool onlyIfExists = false)
: m_name(name)
, m_atom(XCB_ATOM_NONE)
, m_connection(nullptr)
, m_retrieved(false)
, m_onlyIfExists(onlyIfExists)
{
m_cookie.sequence = 0;
}
explicit XcbAtom(xcb_connection_t *c, const QByteArray &name, bool onlyIfExists = false)
: m_name(name)
, m_atom(XCB_ATOM_NONE)
, m_cookie(xcb_intern_atom_unchecked(c, onlyIfExists, name.length(), name.constData()))
, m_connection(c)
, m_retrieved(false)
, m_onlyIfExists(onlyIfExists)
{
}
~XcbAtom()
{
if (!m_retrieved && m_cookie.sequence && m_connection) {
xcb_discard_reply(m_connection, m_cookie.sequence);
}
}
operator xcb_atom_t()
{
getReply();
return m_atom;
}
inline const QByteArray &name() const
{
return m_name;
}
inline void setConnection(xcb_connection_t *c)
{
m_connection = c;
}
inline void fetch()
{
if (!m_connection || m_name.isEmpty()) {
return;
}
m_cookie = xcb_intern_atom_unchecked(m_connection, m_onlyIfExists, m_name.length(), m_name.constData());
}
private:
void getReply()
{
if (m_retrieved || !m_cookie.sequence || !m_connection) {
return;
}
UniqueCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(m_connection, m_cookie, nullptr));
if (reply) {
m_atom = reply->atom;
}
m_retrieved = true;
}
QByteArray m_name;
xcb_atom_t m_atom;
xcb_intern_atom_cookie_t m_cookie;
xcb_connection_t *m_connection;
bool m_retrieved;
bool m_onlyIfExists;
};
class KXMessagesPrivate : public QAbstractNativeEventFilter
{
public:
KXMessagesPrivate(KXMessages *parent, const char *acceptBroadcast, xcb_connection_t *c, xcb_window_t root)
: accept_atom1(acceptBroadcast ? QByteArray(acceptBroadcast) + QByteArrayLiteral("_BEGIN") : QByteArray())
, accept_atom2(acceptBroadcast ? QByteArray(acceptBroadcast) : QByteArray())
, handle(new QWindow)
, q(parent)
, valid(c)
, connection(c)
, rootWindow(root)
{
if (acceptBroadcast) {
accept_atom1.setConnection(c);
accept_atom1.fetch();
accept_atom2.setConnection(c);
accept_atom2.fetch();
QCoreApplication::instance()->installNativeEventFilter(this);
}
}
XcbAtom accept_atom1;
XcbAtom accept_atom2;
QMap<WId, QByteArray> incoming_messages;
std::unique_ptr<QWindow> handle;
KXMessages *q;
bool valid;
xcb_connection_t *connection;
xcb_window_t rootWindow;
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *) override
{
// A faster comparison than eventType != "xcb_generic_event_t"
if (eventType[0] != 'x') {
return false;
}
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(message);
uint response_type = event->response_type & ~0x80;
if (response_type != XCB_CLIENT_MESSAGE) {
return false;
}
xcb_client_message_event_t *cm_event = reinterpret_cast<xcb_client_message_event_t *>(event);
if (cm_event->format != 8) {
return false;
}
if (cm_event->type != accept_atom1 && cm_event->type != accept_atom2) {
return false;
}
char buf[21]; // can't be longer
// Copy the data in order to null-terminate it
qstrncpy(buf, reinterpret_cast<char *>(cm_event->data.data8), 21);
// qDebug() << cm_event->window << "buf=\"" << buf << "\" atom=" << (cm_event->type == accept_atom1 ? "atom1" : "atom2");
if (incoming_messages.contains(cm_event->window)) {
if (cm_event->type == accept_atom1)
// two different messages on the same window at the same time shouldn't happen anyway
{
incoming_messages[cm_event->window] = QByteArray();
}
incoming_messages[cm_event->window] += buf;
} else {
if (cm_event->type == accept_atom2) {
return false; // middle of message, but we don't have the beginning
}
incoming_messages[cm_event->window] = buf;
}
if (strlen(buf) < 20) { // last message fragment
Q_EMIT q->gotMessage(QString::fromUtf8(incoming_messages[cm_event->window].constData()));
incoming_messages.remove(cm_event->window);
}
return false; // lets other KXMessages instances get the event too
}
};
static void
send_message_internal(xcb_window_t w, const QString &msg, xcb_connection_t *c, xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle);
KXMessages::KXMessages(const char *accept_broadcast_P, QObject *parent_P)
: QObject(parent_P)
, d(new KXMessagesPrivate(this,
accept_broadcast_P,
QX11Info::isPlatformX11() ? QX11Info::connection() : nullptr,
QX11Info::isPlatformX11() ? QX11Info::appRootWindow() : 0))
{
}
KXMessages::KXMessages(xcb_connection_t *connection, xcb_window_t rootWindow, const char *accept_broadcast, QObject *parent)
: QObject(parent)
, d(new KXMessagesPrivate(this, accept_broadcast, connection, rootWindow))
{
}
KXMessages::~KXMessages()
{
delete d;
}
static xcb_screen_t *defaultScreen(xcb_connection_t *c, int screen)
{
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(c)); it.rem; --screen, xcb_screen_next(&it)) {
if (screen == 0) {
return it.data;
}
}
return nullptr;
}
void KXMessages::broadcastMessage(const char *msg_type_P, const QString &message_P, int screen_P)
{
if (!d->valid) {
qWarning() << "KXMessages used on non-X11 platform! This is an application bug.";
return;
}
const QByteArray msg(msg_type_P);
XcbAtom a2(d->connection, msg);
XcbAtom a1(d->connection, msg + QByteArrayLiteral("_BEGIN"));
xcb_window_t root = screen_P == -1 ? d->rootWindow : defaultScreen(d->connection, screen_P)->root;
send_message_internal(root, message_P, d->connection, a1, a2, d->handle->winId());
}
bool KXMessages::broadcastMessageX(xcb_connection_t *c, const char *msg_type_P, const QString &message, int screenNumber)
{
if (!c) {
return false;
}
const QByteArray msg(msg_type_P);
XcbAtom a2(c, msg);
XcbAtom a1(c, msg + QByteArrayLiteral("_BEGIN"));
const xcb_screen_t *screen = defaultScreen(c, screenNumber);
if (!screen) {
return false;
}
const xcb_window_t root = screen->root;
const xcb_window_t win = xcb_generate_id(c);
xcb_create_window(c, XCB_COPY_FROM_PARENT, win, root, 0, 0, 1, 1, 0, XCB_COPY_FROM_PARENT, XCB_COPY_FROM_PARENT, 0, nullptr);
send_message_internal(root, message, c, a1, a2, win);
xcb_destroy_window(c, win);
return true;
}
static void
send_message_internal(xcb_window_t w, const QString &msg_P, xcb_connection_t *c, xcb_atom_t leadingMessage, xcb_atom_t followingMessage, xcb_window_t handle)
{
unsigned int pos = 0;
QByteArray msg = msg_P.toUtf8();
const size_t len = msg.size();
KXcbEvent<xcb_client_message_event_t> event;
event.response_type = XCB_CLIENT_MESSAGE;
event.format = 8;
event.sequence = 0;
event.window = handle;
event.type = leadingMessage;
do {
unsigned int i;
for (i = 0; i < 20 && i + pos < len; ++i) {
event.data.data8[i] = msg[i + pos];
}
for (; i < 20; ++i) {
event.data.data8[i] = 0;
}
xcb_send_event(c, false, w, XCB_EVENT_MASK_PROPERTY_CHANGE, event.buffer());
event.type = followingMessage;
pos += i;
} while (pos <= len);
xcb_flush(c);
}
#endif
#include "moc_kxmessages.cpp"
@@ -0,0 +1,92 @@
/*
SPDX-FileCopyrightText: 2001-2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef KXMESSAGES_H
#define KXMESSAGES_H
#include <QObject>
#include <kwindowsystem_export.h>
#include <config-kwindowsystem.h> // KWINDOWSYSTEM_HAVE_X11
#if KWINDOWSYSTEM_HAVE_X11
#include <xcb/xcb.h>
typedef struct _XDisplay Display;
class QString;
class KXMessagesPrivate;
/**
* Sending string messages to other applications using the X Client Messages.
*
* Used internally by KStartupInfo and kstart.
* You usually don't want to use this, use D-Bus instead.
*
* @internal
*
* @author Lubos Lunak <l.lunak@kde.org>
*/
class KWINDOWSYSTEM_EXPORT KXMessages : public QObject
{
Q_OBJECT
public:
/**
* Creates an instance which will receive X messages.
*
* @param accept_broadcast if non-nullptr, all broadcast messages with
* this message type will be received.
* @param parent the parent of this widget
*/
explicit KXMessages(const char *accept_broadcast = nullptr, QObject *parent = nullptr);
/**
* @overload
* Overload passing in the xcb_connection_t to use instead relying on platform xcb.
*
* @param connection The xcb connection
* @param rootWindow The rootWindow to use
* @param accept_broadcast if non-nullptr, all broadcast messages with
* this message type will be received.
* @param parent the parent of this object
* @since 5.8
**/
explicit KXMessages(xcb_connection_t *connection, xcb_window_t rootWindow, const char *accept_broadcast = nullptr, QObject *parent = nullptr);
~KXMessages() override;
/**
* Broadcasts the given message with the given message type.
* @param msg_type the type of the message
* @param message the message itself
* @param screen X11 screen to use, -1 for the default
*/
void broadcastMessage(const char *msg_type, const QString &message, int screen = -1);
/**
* Broadcasts the given message with the given message type.
*
* @param c X11 connection which will be used instead of
* QX11Info::connection()
* @param msg_type the type of the message
* @param message the message itself
* @param screenNumber X11 screen to use
* @return false when an error occurred, true otherwise
*/
static bool broadcastMessageX(xcb_connection_t *c, const char *msg_type, const QString &message, int screenNumber);
Q_SIGNALS:
/**
* Emitted when a message was received.
* @param message the message that has been received
*/
void gotMessage(const QString &message);
private:
friend class KXMessagesPrivate;
KXMessagesPrivate *const d;
};
#endif
#endif
@@ -0,0 +1,146 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@kde.org>
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "cptr_p.h"
#include "kxutils_p.h"
#include <QBitmap>
#include <QDebug>
#include <private/qtx11extras_p.h>
#include <xcb/xcb.h>
namespace KXUtils
{
template<typename T>
T fromNative(xcb_pixmap_t pixmap, xcb_connection_t *c)
{
const xcb_get_geometry_cookie_t geoCookie = xcb_get_geometry_unchecked(c, pixmap);
UniqueCPointer<xcb_get_geometry_reply_t> geo(xcb_get_geometry_reply(c, geoCookie, nullptr));
if (!geo) {
// getting geometry for the pixmap failed
return T();
}
const xcb_get_image_cookie_t imageCookie = xcb_get_image_unchecked(c, XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, 0, 0, geo->width, geo->height, ~0);
UniqueCPointer<xcb_get_image_reply_t> xImage(xcb_get_image_reply(c, imageCookie, nullptr));
if (!xImage) {
// request for image data failed
return T();
}
QImage::Format format = QImage::Format_Invalid;
switch (xImage->depth) {
case 1:
format = QImage::Format_MonoLSB;
break;
case 16:
format = QImage::Format_RGB16;
break;
case 24:
format = QImage::Format_RGB32;
break;
case 30: {
// Qt doesn't have a matching image format. We need to convert manually
uint32_t *pixels = reinterpret_cast<uint32_t *>(xcb_get_image_data(xImage.get()));
for (uint i = 0; i < xImage.get()->length; ++i) {
int r = (pixels[i] >> 22) & 0xff;
int g = (pixels[i] >> 12) & 0xff;
int b = (pixels[i] >> 2) & 0xff;
pixels[i] = qRgba(r, g, b, 0xff);
}
// fall through, Qt format is still Format_ARGB32_Premultiplied
Q_FALLTHROUGH();
}
case 32:
format = QImage::Format_ARGB32_Premultiplied;
break;
default:
return T(); // we don't know
}
QImage image(xcb_get_image_data(xImage.get()), geo->width, geo->height, xcb_get_image_data_length(xImage.get()) / geo->height, format, free, xImage.get());
xImage.release();
if (image.isNull()) {
return T();
}
if (image.format() == QImage::Format_MonoLSB) {
// work around an abort in QImage::color
image.setColorCount(2);
image.setColor(0, QColor(Qt::white).rgb());
image.setColor(1, QColor(Qt::black).rgb());
}
return T::fromImage(image);
}
// Create QPixmap from X pixmap. Take care of different depths if needed.
QPixmap createPixmapFromHandle(WId pixmap, WId pixmap_mask)
{
return createPixmapFromHandle(QX11Info::connection(), pixmap, pixmap_mask);
}
QPixmap createPixmapFromHandle(xcb_connection_t *c, WId pixmap, WId pixmap_mask)
{
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
qDebug() << "Byte order not supported";
return QPixmap();
#endif
const xcb_setup_t *setup = xcb_get_setup(c);
if (setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST) {
qDebug() << "Byte order not supported";
return QPixmap();
}
QPixmap pix = fromNative<QPixmap>(pixmap, c);
if (pixmap_mask != XCB_PIXMAP_NONE) {
QBitmap mask = fromNative<QBitmap>(pixmap_mask, c);
if (mask.size() != pix.size()) {
return QPixmap();
}
pix.setMask(mask);
}
return pix;
}
// Functions for X timestamp comparing. For Time being 32bit they're fairly simple
// (the #if 0 part), but on 64bit architectures Time is 64bit unsigned long,
// so there special care needs to be taken to always use only the lower 32bits.
#if 0
int timestampCompare(Time time1, Time time2) // like strcmp()
{
if (time1 == time2) {
return 0;
}
return (time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
}
Time timestampDiff(Time time1, Time time2) // returns time2 - time1
{
// no need to handle wrapping?
return time2 - time1;
}
#else
int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
{
quint32 time1 = time1_;
quint32 time2 = time2_;
if (time1 == time2) {
return 0;
}
return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
}
int timestampDiff(unsigned long time1_, unsigned long time2_) // returns time2 - time1
{
// no need to handle wrapping?
quint32 time1 = time1_;
quint32 time2 = time2_;
return quint32(time2 - time1);
}
#endif
} // namespace
@@ -0,0 +1,52 @@
/*
This file is part of the KDE libraries
SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KXUTILS_H
#define KXUTILS_H
#include <QPixmap>
#include <config-kwindowsystem.h>
#if KWINDOWSYSTEM_HAVE_X11
#include <kwindowsystem_export.h>
struct xcb_connection_t;
/**
* Namespace with various generic X11-related functionality.
*/
namespace KXUtils
{
/**
* Creates a QPixmap that contains a copy of the pixmap given by the X handle @p pixmap
* and optionally also mask given as another X handle @mask. This function tries to
* also handle the case when the depth of the pixmap differs from the native QPixmap depth.
* @since 4.0.2
*/
QPixmap createPixmapFromHandle(WId pixmap, WId mask = 0);
QPixmap createPixmapFromHandle(xcb_connection_t *c, WId pixmap, WId mask = 0);
/**
* Compares two X timestamps, taking into account wrapping and 64bit architectures.
* Return value is like with strcmp(), 0 for equal, -1 for time1 < time2, 1 for time1 > time2.
* @since 4.1.0
*/
int timestampCompare(unsigned long time1, unsigned long time2);
/**
* Returns a difference of two X timestamps, time2 - time1, where time2 must be later than time1,
* as returned by timestampCompare().
* @since 4.1.0
*/
int timestampDiff(unsigned long time1, unsigned long time2);
} // namespace
#endif // KWINDOWSYSTEM_HAVE_X11
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,195 @@
/*
SPDX-FileCopyrightText: 2000 Troll Tech AS
SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: MIT
*/
#ifndef netwm_p_h
#define netwm_p_h
#include <QSharedData>
#include <QSharedDataPointer>
#include "atoms_p.h"
class Atoms : public QSharedData
{
public:
explicit Atoms(xcb_connection_t *c);
xcb_atom_t atom(KwsAtom atom) const
{
return m_atoms[atom];
}
private:
void init();
xcb_atom_t m_atoms[KwsAtomCount];
xcb_connection_t *m_connection;
};
/**
Resizable array class.
This resizable array is used to simplify the implementation. The existence of
this class is to keep the implementation from depending on a separate
framework/library.
@internal
**/
template<class Z>
class NETRArray
{
public:
/**
Constructs an empty (size == 0) array.
**/
NETRArray();
/**
Resizable array destructor.
**/
~NETRArray();
/**
The [] operator does the work. If the index is larger than the current
size of the array, it is resized.
**/
Z &operator[](int);
/**
Returns the size of the array.
**/
int size() const
{
return sz;
}
/**
Resets the array (size == 0).
**/
void reset();
private:
int sz;
int capacity;
Z *d;
};
/**
Private data for the NETRootInfo class.
@internal
**/
struct NETRootInfoPrivate {
NET::Role role;
// information about the X server
xcb_connection_t *conn;
NETSize rootSize;
xcb_window_t root;
xcb_window_t supportwindow;
const char *name;
uint32_t *temp_buf;
size_t temp_buf_size;
// data that changes (either by the window manager or by a client)
// and requires updates
NETRArray<NETPoint> viewport;
NETRArray<NETRect> workarea;
NETSize geometry;
xcb_window_t active;
xcb_window_t *clients, *stacking, *virtual_roots;
NETRArray<const char *> desktop_names;
int number_of_desktops;
int current_desktop;
unsigned long clients_count, stacking_count, virtual_roots_count;
bool showing_desktop;
NET::Orientation desktop_layout_orientation;
NET::DesktopLayoutCorner desktop_layout_corner;
int desktop_layout_columns, desktop_layout_rows;
NET::Properties properties;
NET::Properties2 properties2;
NET::WindowTypes windowTypes;
NET::States states;
NET::Actions actions;
NET::Properties clientProperties;
NET::Properties2 clientProperties2;
int ref;
QSharedDataPointer<Atoms> atoms;
xcb_atom_t atom(KwsAtom atom) const
{
return atoms->atom(atom);
}
};
/**
Private data for the NETWinInfo class.
@internal
**/
struct NETWinInfoPrivate {
NET::Role role;
xcb_connection_t *conn;
xcb_window_t window, root;
NET::MappingState mapping_state;
bool mapping_state_dirty;
NETRArray<NETIcon> icons;
int icon_count;
int *icon_sizes; // for iconSizes() only
NETRect icon_geom, win_geom;
NET::States state;
NETExtendedStrut extended_strut;
NETStrut strut;
NETStrut frame_strut; // strut?
NETStrut frame_overlap;
NETStrut gtk_frame_extents;
NETRArray<NET::WindowType> types;
char *name, *visible_name, *icon_name, *visible_icon_name;
int desktop;
int pid;
bool handled_icons;
xcb_timestamp_t user_time;
char *startup_id;
unsigned long opacity;
xcb_window_t transient_for, window_group;
xcb_pixmap_t icon_pixmap, icon_mask;
NET::Actions allowed_actions;
char *class_class, *class_name, *window_role, *client_machine, *desktop_file, *appmenu_object_path, *appmenu_service_name, *gtk_application_id;
NET::Properties properties;
NET::Properties2 properties2;
NETFullscreenMonitors fullscreen_monitors;
bool has_net_support;
const char *activities;
bool blockCompositing;
bool urgency;
bool input;
NET::MappingState initialMappingState;
NET::Protocols protocols;
std::vector<NETRect> opaqueRegion;
int ref;
QSharedDataPointer<Atoms> atoms;
xcb_atom_t atom(KwsAtom atom) const
{
return atoms->atom(atom);
}
};
#endif // netwm_p_h
@@ -0,0 +1,41 @@
/*
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 "plugin.h"
#include "kwindoweffects_x11.h"
#include "kwindowshadow_p_x11.h"
#include "kwindowsystem_p_x11.h"
X11Plugin::X11Plugin(QObject *parent)
: KWindowSystemPluginInterface(parent)
{
}
X11Plugin::~X11Plugin()
{
}
KWindowEffectsPrivate *X11Plugin::createEffects()
{
return new KWindowEffectsPrivateX11();
}
KWindowSystemPrivate *X11Plugin::createWindowSystem()
{
return new KWindowSystemPrivateX11();
}
KWindowShadowPrivate *X11Plugin::createWindowShadow()
{
return new KWindowShadowPrivateX11();
}
KWindowShadowTilePrivate *X11Plugin::createWindowShadowTile()
{
return new KWindowShadowTilePrivateX11();
}
#include "moc_plugin.cpp"
@@ -0,0 +1,27 @@
/*
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
*/
#ifndef KWINDOWSYSTEM_X11_PLUGIN_H
#define KWINDOWSYSTEM_X11_PLUGIN_H
#include "kwindowsystemplugininterface_p.h"
class X11Plugin : public KWindowSystemPluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.kde.kwindowsystem.KWindowSystemPluginInterface" FILE "xcb.json")
Q_INTERFACES(KWindowSystemPluginInterface)
public:
explicit X11Plugin(QObject *parent = nullptr);
~X11Plugin() override;
KWindowEffectsPrivate *createEffects() override;
KWindowSystemPrivate *createWindowSystem() override;
KWindowShadowPrivate *createWindowShadow() override final;
KWindowShadowTilePrivate *createWindowShadowTile() override final;
};
#endif
@@ -0,0 +1,3 @@
{
"platforms": ["xcb"]
}
@@ -0,0 +1,162 @@
/*
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 "kwindoweffects_dummy_p.h"
#include "kwindowshadow_dummy_p.h"
#include "kwindowsystem_debug.h"
#include "kwindowsystem_dummy_p.h"
#include "kwindowsystemplugininterface_p.h"
#include "pluginwrapper_p.h"
#include <QDir>
#include <QGuiApplication>
#include <QJsonArray>
#include <QLibrary>
#include <QPluginLoader>
Q_GLOBAL_STATIC(KWindowSystemPluginWrapper, s_pluginWrapper)
static QStringList pluginCandidates()
{
QStringList ret;
const auto paths = QCoreApplication::libraryPaths();
for (const QString &path : paths) {
static const QStringList searchFolders{
QStringLiteral("/kf6/org.kde.kwindowsystem.platforms"),
QStringLiteral("/kf6/kwindowsystem"),
};
for (const QString &searchFolder : searchFolders) {
QDir pluginDir(path + searchFolder);
if (!pluginDir.exists()) {
continue;
}
const auto entries = pluginDir.entryList(QDir::Files | QDir::NoDotAndDotDot);
for (const QString &entry : entries) {
ret << pluginDir.absoluteFilePath(entry);
}
}
}
return ret;
}
static bool checkPlatform(const QJsonObject &metadata, const QString &platformName)
{
const QJsonArray platforms = metadata.value(QStringLiteral("MetaData")).toObject().value(QStringLiteral("platforms")).toArray();
return std::any_of(platforms.begin(), platforms.end(), [&platformName](const QJsonValue &value) {
return QString::compare(platformName, value.toString(), Qt::CaseInsensitive) == 0;
});
}
static KWindowSystemPluginInterface *loadPlugin()
{
if (!qobject_cast<QGuiApplication *>(QCoreApplication::instance())) {
qCWarning(LOG_KWINDOWSYSTEM) << "Cannot use KWindowSystem without a QGuiApplication";
return nullptr;
}
QString platformName = QGuiApplication::platformName();
if (platformName == QLatin1String("flatpak")) {
// here we cannot know what is the actual windowing system, let's try it's env variable
const auto flatpakPlatform = QString::fromLocal8Bit(qgetenv("QT_QPA_FLATPAK_PLATFORM"));
if (!flatpakPlatform.isEmpty()) {
platformName = flatpakPlatform;
}
}
const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
for (const QStaticPlugin &staticPlugin : staticPlugins) {
const QJsonObject metadata = staticPlugin.metaData();
if (metadata.value(QLatin1String("IID")) != QLatin1String(KWindowSystemPluginInterface_iid)) {
continue;
}
if (checkPlatform(metadata, platformName)) {
KWindowSystemPluginInterface *interface = qobject_cast<KWindowSystemPluginInterface *>(staticPlugin.instance());
if (interface) {
qCDebug(LOG_KWINDOWSYSTEM) << "Loaded a static plugin for platform" << platformName;
return interface;
}
}
}
const auto candidates = pluginCandidates();
for (const QString &candidate : candidates) {
if (!QLibrary::isLibrary(candidate)) {
continue;
}
QPluginLoader loader(candidate);
if (checkPlatform(loader.metaData(), platformName)) {
KWindowSystemPluginInterface *interface = qobject_cast<KWindowSystemPluginInterface *>(loader.instance());
if (interface) {
qCDebug(LOG_KWINDOWSYSTEM) << "Loaded plugin" << candidate << "for platform" << platformName;
return interface;
}
}
}
qCWarning(LOG_KWINDOWSYSTEM) << "Could not find any platform plugin";
return nullptr;
}
KWindowSystemPluginWrapper::KWindowSystemPluginWrapper()
: m_plugin(loadPlugin())
, m_effects()
{
if (m_plugin) {
m_effects.reset(m_plugin->createEffects());
}
if (!m_effects) {
m_effects.reset(new KWindowEffectsPrivateDummy());
}
}
KWindowSystemPluginWrapper::~KWindowSystemPluginWrapper()
{
}
KWindowEffectsPrivate *KWindowSystemPluginWrapper::effects() const
{
return m_effects.get();
}
KWindowSystemPrivate *KWindowSystemPluginWrapper::createWindowSystem() const
{
KWindowSystemPrivate *p = nullptr;
if (m_plugin) {
p = m_plugin->createWindowSystem();
}
if (!p) {
p = new KWindowSystemPrivateDummy();
}
return p;
}
KWindowShadowPrivate *KWindowSystemPluginWrapper::createWindowShadow() const
{
KWindowShadowPrivate *p = nullptr;
if (m_plugin) {
p = m_plugin->createWindowShadow();
}
if (!p) {
p = new KWindowShadowPrivateDummy();
}
return p;
}
KWindowShadowTilePrivate *KWindowSystemPluginWrapper::createWindowShadowTile() const
{
KWindowShadowTilePrivate *p = nullptr;
if (m_plugin) {
p = m_plugin->createWindowShadowTile();
}
if (!p) {
p = new KWindowShadowTilePrivateDummy();
}
return p;
}
const KWindowSystemPluginWrapper &KWindowSystemPluginWrapper::self()
{
return *s_pluginWrapper;
}
@@ -0,0 +1,35 @@
/*
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
*/
#ifndef PLUGINWRAPPER_P_H
#define PLUGINWRAPPER_P_H
#include <QWidgetList> //For WId
#include <memory>
class KWindowEffectsPrivate;
class KWindowShadowPrivate;
class KWindowShadowTilePrivate;
class KWindowSystemPluginInterface;
class KWindowSystemPrivate;
class KWindowSystemPluginWrapper
{
public:
KWindowSystemPluginWrapper();
~KWindowSystemPluginWrapper();
static const KWindowSystemPluginWrapper &self();
KWindowEffectsPrivate *effects() const;
KWindowSystemPrivate *createWindowSystem() const;
KWindowShadowPrivate *createWindowShadow() const;
KWindowShadowTilePrivate *createWindowShadowTile() const;
private:
std::unique_ptr<KWindowSystemPluginInterface> m_plugin;
std::unique_ptr<KWindowEffectsPrivate> m_effects;
};
#endif
@@ -0,0 +1,6 @@
ecm_add_qml_module(KWindowSystem URI "org.kde.kwindowsystem" VERSION 1.0 GENERATE_PLUGIN_SOURCE)
target_sources(KWindowSystem PRIVATE types.h)
target_link_libraries(KWindowSystem PRIVATE Qt::Qml KF6::WindowSystem)
ecm_finalize_qml_module(KWindowSystem DESTINATION ${KDE_INSTALL_QMLDIR})
@@ -0,0 +1,18 @@
/*
SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kwindowsystem 1.0
Item {
QQC2.Label {
anchors.centerIn: parent
text: "Compositing active: " + KX11Extras.compositingActive
}
}
@@ -0,0 +1,21 @@
/*
SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kwindowsystem 1.0
Item {
QQC2.Button {
anchors.centerIn: parent
text: "Force"
// Forcing the current window to be active it a bit silly, but it illustrates how to use the API
onClicked: KX11Extras.forceActiveWindow(Window.window)
}
}
@@ -0,0 +1,26 @@
/*
SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kwindowsystem 1.0
Item {
Column {
anchors.centerIn: parent
QQC2.Label {
text: "Is X11: " + KWindowSystem.isPlatformX11
}
QQC2.Label {
text: "Is Wayland: " + KWindowSystem.isPlatformWayland
}
}
}
@@ -0,0 +1,35 @@
/*
SPDX-FileCopyrightText: 2023 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15 as QQC2
import org.kde.kwindowsystem 1.0
Item {
Connections {
target: KWindowSystem
function onShowingDesktopChanged() {
console.log("Showing desktop changed to " + KWindowSystem.showingDesktop)
}
}
Column {
anchors.centerIn: parent
QQC2.Label {
text: "Showing desktop: " + KWindowSystem.showingDesktop
}
QQC2.Button {
text: "Show"
onClicked: KWindowSystem.showingDesktop = true
}
}
}
@@ -0,0 +1,49 @@
/*
SPDX-FileCopyrightText: 2024 Nicolas Fella <nicolas.fella@gmx.de>
SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifndef KWINDOWSYSTEM_QML_TYPES_H
#define KWINDOWSYSTEM_QML_TYPES_H
#include <QQmlEngine>
#include <KWindowSystem>
#include <config-kwindowsystem.h>
#if KWINDOWSYSTEM_HAVE_X11
#include <KX11Extras>
#endif
struct KWindowSystemForeign {
Q_GADGET
QML_NAMED_ELEMENT(KWindowSystem)
QML_SINGLETON
QML_FOREIGN(KWindowSystem)
public:
static KWindowSystem *create(QQmlEngine *, QJSEngine *)
{
QQmlEngine::setObjectOwnership(KWindowSystem::self(), QQmlEngine::CppOwnership);
return KWindowSystem::self();
}
};
#if KWINDOWSYSTEM_HAVE_X11
struct KX11ExtrasForeign {
Q_GADGET
QML_NAMED_ELEMENT(KX11Extras)
QML_SINGLETON
QML_FOREIGN(KX11Extras)
public:
static KX11Extras *create(QQmlEngine *, QJSEngine *)
{
QQmlEngine::setObjectOwnership(KX11Extras::self(), QQmlEngine::CppOwnership);
return KX11Extras::self();
}
};
#endif
#endif