diff --git a/includes/CMakeLists.txt b/includes/CMakeLists.txt index fafaea40..9d13614e 100644 --- a/includes/CMakeLists.txt +++ b/includes/CMakeLists.txt @@ -219,6 +219,7 @@ install( KPassivePopupMessageHandler KPasswordDialog KPasteTextAction + KPixmap KPixmapRegionSelectorDialog KPixmapRegionSelectorWidget KPluginFactory diff --git a/includes/KPixmap b/includes/KPixmap new file mode 100644 index 00000000..371914dd --- /dev/null +++ b/includes/KPixmap @@ -0,0 +1 @@ +#include "../kpixmap.h" diff --git a/kdeui/CMakeLists.txt b/kdeui/CMakeLists.txt index cd6c6f40..90c8db7c 100644 --- a/kdeui/CMakeLists.txt +++ b/kdeui/CMakeLists.txt @@ -189,6 +189,7 @@ set(kdeui_LIB_SRCS util/kxerrorhandler.cpp util/kxmessages.cpp util/kundostack.cpp + util/kpixmap.cpp util/kpixmapsequence.cpp util/kpixmapsequenceoverlaypainter.cpp util/kpixmapsequencewidget.cpp @@ -508,6 +509,7 @@ install( util/kxerrorhandler.h util/kxmessages.h util/kundostack.h + util/kpixmap.h util/kpixmapsequence.h util/kpixmapsequenceoverlaypainter.h util/kpixmapsequencewidget.h diff --git a/kdeui/util/kpixmap.cpp b/kdeui/util/kpixmap.cpp new file mode 100644 index 00000000..10b9b8b0 --- /dev/null +++ b/kdeui/util/kpixmap.cpp @@ -0,0 +1,169 @@ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include +#include + +#include "kpixmap.h" +#include "kxerrorhandler.h" +#include "kdebug.h" + +#include +#include + +class KPixmapPrivate +{ +public: + KPixmapPrivate(); + + QSize size; + Qt::HANDLE handle; +}; + +KPixmapPrivate::KPixmapPrivate() + : handle(XNone) +{ +} + +KPixmap::KPixmap() + : d(new KPixmapPrivate()) +{ +} + +KPixmap::KPixmap(const Qt::HANDLE pixmap) + : d(new KPixmapPrivate()) +{ + d->handle = pixmap; + if (pixmap) { + Window x11window; + int x11x = 0; + int x11y = 0; + uint x11width = 0; + uint x11height = 0; + uint x11borderwidth = 0; + uint x11depth = 0; + KXErrorHandler kx11errorhandler; + XGetGeometry( + QX11Info::display(), pixmap, + &x11window, &x11x, &x11y, + &x11width, &x11height, + &x11borderwidth, &x11depth + ); + d->size = QSize(x11width, x11height); + if (kx11errorhandler.error(true)) { + kWarning() << KXErrorHandler::errorMessage(kx11errorhandler.errorEvent()); + // in the worst case the pixmap will be leaked + d->handle = XNone; + d->size = QSize(); + } + } +} + +KPixmap::KPixmap(const QPixmap &pixmap) + : d(new KPixmapPrivate()) +{ + if (!pixmap.isNull()) { + KXErrorHandler kx11errorhandler; + d->handle = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), pixmap.width(), pixmap.height(), 32); + if (kx11errorhandler.error(true)) { + kWarning() << KXErrorHandler::errorMessage(kx11errorhandler.errorEvent()); + d->handle = XNone; + return; + } + // TODO: optimize + QPixmap tempPix = QPixmap::fromX11Pixmap(d->handle, QPixmap::ExplicitlyShared); + tempPix.fill(Qt::transparent); + QPainter p(&tempPix); + p.drawPixmap(QPoint(0, 0), pixmap); + p.end(); + d->size = pixmap.size(); + } +} + +KPixmap::KPixmap(const KPixmap &pixmap) + : d(new KPixmapPrivate(*pixmap.d)) +{ +} + +KPixmap::KPixmap(const QSize &size) + : d(new KPixmapPrivate()) +{ + if (!size.isEmpty()) { + KXErrorHandler kx11errorhandler; + d->handle = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), size.width(), size.height(), 32); + if (kx11errorhandler.error(true)) { + kWarning() << KXErrorHandler::errorMessage(kx11errorhandler.errorEvent()); + d->handle = XNone; + return; + } + d->size = size; + GC x11gc = XCreateGC(QX11Info::display(), d->handle, 0, 0); + XSetForeground(QX11Info::display(), x11gc, XBlackPixel(QX11Info::display(), QX11Info::appScreen())); + XFillRectangle(QX11Info::display(), d->handle, x11gc, 0, 0, size.width(), size.height()); + XFreeGC(QX11Info::display(), x11gc); + } +} + +KPixmap::~KPixmap() +{ + delete d; +} + +bool KPixmap::isNull() const +{ + return (d->handle == XNone); +} + +QSize KPixmap::size() const +{ + return d->size; +} + +Qt::HANDLE KPixmap::handle() const +{ + return d->handle; +} + +void KPixmap::release() +{ + if (d->handle == XNone) { + kDebug() << "No handle"; + return; + } + // NOTE: catching errors here is done to not get fatal I/O + KXErrorHandler kx11errorhandler; + XFreePixmap(QX11Info::display(), d->handle); + if (kx11errorhandler.error(true)) { + kWarning() << KXErrorHandler::errorMessage(kx11errorhandler.errorEvent()); + } + d->handle = XNone; + d->size = QSize(); +} + +QImage KPixmap::toImage() const +{ + // TODO: optimize + return QPixmap::fromX11Pixmap(d->handle).toImage(); +} + +KPixmap& KPixmap::operator=(const KPixmap &other) +{ + delete d; + d = new KPixmapPrivate(*other.d); + return *this; +} \ No newline at end of file diff --git a/kdeui/util/kpixmap.h b/kdeui/util/kpixmap.h new file mode 100644 index 00000000..24e27272 --- /dev/null +++ b/kdeui/util/kpixmap.h @@ -0,0 +1,78 @@ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2, as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIXMAP_H +#define KPIXMAP_H + +#include + +#include +#include + +class KPixmapPrivate; + +/*! + Class to deal with X11 pixmaps. +*/ +class KDEUI_EXPORT KPixmap +{ +public: + /*! + @brief Constructs null object. + */ + KPixmap(); + /*! + @brief Constructs object from X11 pixmap @p pixmap, the @p pixmap is not deep-copied. + */ + KPixmap(const Qt::HANDLE pixmap); + /*! + @brief Constructs object from QPixmap @p pixmap, the @p pixmap is deep-copied and must be released. + */ + KPixmap(const QPixmap &pixmap); + /*! + @brief Constructs object from other KPixmap @p pixmap, the @p pixmap is not deep-copied. + */ + KPixmap(const KPixmap &pixmap); + /*! + @brief Constructs object for X11 pixmap with size @p size, the @p pixmap must be released. + */ + KPixmap(const QSize &size); + ~KPixmap(); + + bool isNull() const; + QSize size() const; + int width() const { return size().width(); } + int height() const { return size().height(); } + Qt::HANDLE handle() const; + + /*! + @brief Releases the X11 pixmap if not null. + */ + void release(); + /*! + @brief Returns QImage copy of the X11 pixmap. + */ + QImage toImage() const; + + KPixmap &operator=(const KPixmap &other); + +private: + KPixmapPrivate* d; +}; + +#endif // KPIXMAP_H diff --git a/plasma/dialogshadows.cpp b/plasma/dialogshadows.cpp index d8cf841a..976a3f74 100644 --- a/plasma/dialogshadows.cpp +++ b/plasma/dialogshadows.cpp @@ -17,6 +17,8 @@ */ #include "dialogshadows.h" +#include "kpixmap.h" +#include "kdebug.h" #include #include @@ -28,8 +30,6 @@ #include #endif -#include - namespace Plasma { @@ -43,8 +43,6 @@ public: ~DialogShadowsPrivate() { - // Do not call clearPixmaps() from here: it creates new QPixmap(), - // which causes a crash when application is stopping. freeX11Pixmaps(); } @@ -52,7 +50,7 @@ public: void clearPixmaps(); void setupPixmaps(); void initPixmap(const QString &element); - QPixmap initEmptyPixmap(const QSize &size); + KPixmap initEmptyPixmap(const QSize &size); void updateShadow(const QWidget *window, Plasma::FrameSvg::EnabledBorders); void clearShadow(const QWidget *window); void updateShadows(); @@ -60,15 +58,15 @@ public: void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders); DialogShadows *q; - QList m_shadowPixmaps; + QList m_shadowPixmaps; - QPixmap m_emptyCornerPix; - QPixmap m_emptyCornerLeftPix; - QPixmap m_emptyCornerTopPix; - QPixmap m_emptyCornerRightPix; - QPixmap m_emptyCornerBottomPix; - QPixmap m_emptyVerticalPix; - QPixmap m_emptyHorizontalPix; + KPixmap m_emptyCornerPix; + KPixmap m_emptyCornerLeftPix; + KPixmap m_emptyCornerTopPix; + KPixmap m_emptyCornerRightPix; + KPixmap m_emptyCornerBottomPix; + KPixmap m_emptyVerticalPix; + KPixmap m_emptyHorizontalPix; QHash > data; QHash m_windows; @@ -135,34 +133,12 @@ void DialogShadowsPrivate::updateShadows() void DialogShadowsPrivate::initPixmap(const QString &element) { -#ifdef Q_WS_X11 - QPixmap pix = q->pixmap(element); - QPixmap tempPix; - if (!pix.isNull()) { - Pixmap xPix = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), pix.width(), pix.height(), 32); - tempPix = QPixmap::fromX11Pixmap(xPix, QPixmap::ExplicitlyShared); - tempPix.fill(Qt::transparent); - QPainter p(&tempPix); - p.drawPixmap(QPoint(0, 0), pix); - p.end(); - } - m_shadowPixmaps << tempPix; -#endif + m_shadowPixmaps << KPixmap(q->pixmap(element)); } -QPixmap DialogShadowsPrivate::initEmptyPixmap(const QSize &size) +KPixmap DialogShadowsPrivate::initEmptyPixmap(const QSize &size) { -#ifdef Q_WS_X11 - QPixmap tempEmptyPix; - if (!size.isEmpty()) { - Pixmap emptyXPix = XCreatePixmap(QX11Info::display(), QX11Info::appRootWindow(), size.width(), size.height(), 32); - tempEmptyPix = QPixmap::fromX11Pixmap(emptyXPix, QPixmap::ExplicitlyShared); - tempEmptyPix.fill(Qt::transparent); - } - return tempEmptyPix; -#else - return QPixmap(); -#endif + return KPixmap(size); } void DialogShadowsPrivate::setupPixmaps() @@ -321,35 +297,35 @@ void DialogShadowsPrivate::setupData(Plasma::FrameSvg::EnabledBorders enabledBor void DialogShadowsPrivate::freeX11Pixmaps() { #ifdef Q_WS_X11 - foreach (const QPixmap &pixmap, m_shadowPixmaps) { + foreach (KPixmap &pixmap, m_shadowPixmaps) { if (!QX11Info::display()) { return; } if (!pixmap.isNull()) { - XFreePixmap(QX11Info::display(), pixmap.handle()); + pixmap.release(); } } if (!m_emptyCornerPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyCornerPix.handle()); + m_emptyCornerPix.release(); } if (!m_emptyCornerBottomPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyCornerBottomPix.handle()); + m_emptyCornerBottomPix.release(); } if (!m_emptyCornerLeftPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyCornerLeftPix.handle()); + m_emptyCornerLeftPix.release(); } if (!m_emptyCornerRightPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyCornerRightPix.handle()); + m_emptyCornerRightPix.release(); } if (!m_emptyCornerTopPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyCornerTopPix.handle()); + m_emptyCornerTopPix.release(); } if (!m_emptyVerticalPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyVerticalPix.handle()); + m_emptyVerticalPix.release(); } if (!m_emptyHorizontalPix.isNull()) { - XFreePixmap(QX11Info::display(), m_emptyHorizontalPix.handle()); + m_emptyHorizontalPix.release(); } #endif } @@ -359,13 +335,13 @@ void DialogShadowsPrivate::clearPixmaps() #ifdef Q_WS_X11 freeX11Pixmaps(); - m_emptyCornerPix = QPixmap(); - m_emptyCornerBottomPix = QPixmap(); - m_emptyCornerLeftPix = QPixmap(); - m_emptyCornerRightPix = QPixmap(); - m_emptyCornerTopPix = QPixmap(); - m_emptyVerticalPix = QPixmap(); - m_emptyHorizontalPix = QPixmap(); + m_emptyCornerPix = KPixmap(); + m_emptyCornerBottomPix = KPixmap(); + m_emptyCornerLeftPix = KPixmap(); + m_emptyCornerRightPix = KPixmap(); + m_emptyCornerTopPix = KPixmap(); + m_emptyVerticalPix = KPixmap(); + m_emptyHorizontalPix = KPixmap(); #endif m_shadowPixmaps.clear(); data.clear();