Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,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 ®ion)
|
||||
{
|
||||
KWindowSystemPluginWrapper::self().effects()->enableBlurBehind(window, enable, region);
|
||||
}
|
||||
|
||||
void enableBackgroundContrast(QWindow *window, bool enable, qreal contrast, qreal intensity, qreal saturation, const QRegion ®ion)
|
||||
{
|
||||
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 ®ion = 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 ®ion = 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 ®ion)
|
||||
{
|
||||
Q_UNUSED(window)
|
||||
Q_UNUSED(enable)
|
||||
Q_UNUSED(region)
|
||||
}
|
||||
|
||||
void KWindowEffectsPrivateDummy::enableBackgroundContrast(QWindow *window,
|
||||
bool enable,
|
||||
qreal contrast,
|
||||
qreal intensity,
|
||||
qreal saturation,
|
||||
const QRegion ®ion)
|
||||
{
|
||||
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 ®ion = QRegion()) override;
|
||||
void enableBackgroundContrast(QWindow *window,
|
||||
bool enable = true,
|
||||
qreal contrast = 1,
|
||||
qreal intensity = 1,
|
||||
qreal saturation = 1,
|
||||
const QRegion ®ion = 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 ®ion = QRegion()) = 0;
|
||||
virtual void enableBackgroundContrast(QWindow *window,
|
||||
bool enable = true,
|
||||
qreal contrast = 1,
|
||||
qreal intensity = 1,
|
||||
qreal saturation = 1,
|
||||
const QRegion ®ion = 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"]
|
||||
}
|
||||
+48
@@ -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"
|
||||
+43
@@ -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
|
||||
+107
@@ -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"
|
||||
+76
@@ -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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion = QRegion()) override;
|
||||
void enableBackgroundContrast(QWindow *window,
|
||||
bool enable = true,
|
||||
qreal contrast = 1,
|
||||
qreal intensity = 1,
|
||||
qreal saturation = 1,
|
||||
const QRegion ®ion = QRegion()) override;
|
||||
|
||||
private:
|
||||
void installContrast(QWindow *window, bool enable = true, qreal contrast = 1, qreal intensity = 1, qreal saturation = 1, const QRegion ®ion = QRegion());
|
||||
void installBlur(QWindow *window, bool enable, const QRegion ®ion);
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion = QRegion()) override;
|
||||
void enableBackgroundContrast(QWindow *window,
|
||||
bool enable = true,
|
||||
qreal contrast = 1,
|
||||
qreal intensity = 1,
|
||||
qreal saturation = 1,
|
||||
const QRegion ®ion = 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
|
||||
Reference in New Issue
Block a user