kdeui: new KPixmap class

very much written for use case, not to make sense. while porting to it I
already noticed there are leaks for X11 pixmaps and may have to write
glue code to keep track of the pixmaps and release them before
application quits, that will be a huge performance penalty tho so may
have to be ifdef'ed for debug builds only

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-11-09 02:05:35 +02:00
parent 43cf97b00d
commit a5734faf0f
6 changed files with 281 additions and 54 deletions

View file

@ -219,6 +219,7 @@ install(
KPassivePopupMessageHandler KPassivePopupMessageHandler
KPasswordDialog KPasswordDialog
KPasteTextAction KPasteTextAction
KPixmap
KPixmapRegionSelectorDialog KPixmapRegionSelectorDialog
KPixmapRegionSelectorWidget KPixmapRegionSelectorWidget
KPluginFactory KPluginFactory

1
includes/KPixmap Normal file
View file

@ -0,0 +1 @@
#include "../kpixmap.h"

View file

@ -189,6 +189,7 @@ set(kdeui_LIB_SRCS
util/kxerrorhandler.cpp util/kxerrorhandler.cpp
util/kxmessages.cpp util/kxmessages.cpp
util/kundostack.cpp util/kundostack.cpp
util/kpixmap.cpp
util/kpixmapsequence.cpp util/kpixmapsequence.cpp
util/kpixmapsequenceoverlaypainter.cpp util/kpixmapsequenceoverlaypainter.cpp
util/kpixmapsequencewidget.cpp util/kpixmapsequencewidget.cpp
@ -508,6 +509,7 @@ install(
util/kxerrorhandler.h util/kxerrorhandler.h
util/kxmessages.h util/kxmessages.h
util/kundostack.h util/kundostack.h
util/kpixmap.h
util/kpixmapsequence.h util/kpixmapsequence.h
util/kpixmapsequenceoverlaypainter.h util/kpixmapsequenceoverlaypainter.h
util/kpixmapsequencewidget.h util/kpixmapsequencewidget.h

169
kdeui/util/kpixmap.cpp Normal file
View file

@ -0,0 +1,169 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
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 <X11/Xlib.h>
#include <fixx11h.h>
#include "kpixmap.h"
#include "kxerrorhandler.h"
#include "kdebug.h"
#include <QX11Info>
#include <QPainter>
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;
}

78
kdeui/util/kpixmap.h Normal file
View file

@ -0,0 +1,78 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
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 <kdeui_export.h>
#include <QImage>
#include <QPixmap>
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

View file

@ -17,6 +17,8 @@
*/ */
#include "dialogshadows.h" #include "dialogshadows.h"
#include "kpixmap.h"
#include "kdebug.h"
#include <QWidget> #include <QWidget>
#include <QPainter> #include <QPainter>
@ -28,8 +30,6 @@
#include <fixx11h.h> #include <fixx11h.h>
#endif #endif
#include <kdebug.h>
namespace Plasma namespace Plasma
{ {
@ -43,8 +43,6 @@ public:
~DialogShadowsPrivate() ~DialogShadowsPrivate()
{ {
// Do not call clearPixmaps() from here: it creates new QPixmap(),
// which causes a crash when application is stopping.
freeX11Pixmaps(); freeX11Pixmaps();
} }
@ -52,7 +50,7 @@ public:
void clearPixmaps(); void clearPixmaps();
void setupPixmaps(); void setupPixmaps();
void initPixmap(const QString &element); void initPixmap(const QString &element);
QPixmap initEmptyPixmap(const QSize &size); KPixmap initEmptyPixmap(const QSize &size);
void updateShadow(const QWidget *window, Plasma::FrameSvg::EnabledBorders); void updateShadow(const QWidget *window, Plasma::FrameSvg::EnabledBorders);
void clearShadow(const QWidget *window); void clearShadow(const QWidget *window);
void updateShadows(); void updateShadows();
@ -60,15 +58,15 @@ public:
void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders); void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders);
DialogShadows *q; DialogShadows *q;
QList<QPixmap> m_shadowPixmaps; QList<KPixmap> m_shadowPixmaps;
QPixmap m_emptyCornerPix; KPixmap m_emptyCornerPix;
QPixmap m_emptyCornerLeftPix; KPixmap m_emptyCornerLeftPix;
QPixmap m_emptyCornerTopPix; KPixmap m_emptyCornerTopPix;
QPixmap m_emptyCornerRightPix; KPixmap m_emptyCornerRightPix;
QPixmap m_emptyCornerBottomPix; KPixmap m_emptyCornerBottomPix;
QPixmap m_emptyVerticalPix; KPixmap m_emptyVerticalPix;
QPixmap m_emptyHorizontalPix; KPixmap m_emptyHorizontalPix;
QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data; QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data;
QHash<const QWidget *, Plasma::FrameSvg::EnabledBorders> m_windows; QHash<const QWidget *, Plasma::FrameSvg::EnabledBorders> m_windows;
@ -135,34 +133,12 @@ void DialogShadowsPrivate::updateShadows()
void DialogShadowsPrivate::initPixmap(const QString &element) void DialogShadowsPrivate::initPixmap(const QString &element)
{ {
#ifdef Q_WS_X11 m_shadowPixmaps << KPixmap(q->pixmap(element));
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
} }
QPixmap DialogShadowsPrivate::initEmptyPixmap(const QSize &size) KPixmap DialogShadowsPrivate::initEmptyPixmap(const QSize &size)
{ {
#ifdef Q_WS_X11 return KPixmap(size);
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
} }
void DialogShadowsPrivate::setupPixmaps() void DialogShadowsPrivate::setupPixmaps()
@ -321,35 +297,35 @@ void DialogShadowsPrivate::setupData(Plasma::FrameSvg::EnabledBorders enabledBor
void DialogShadowsPrivate::freeX11Pixmaps() void DialogShadowsPrivate::freeX11Pixmaps()
{ {
#ifdef Q_WS_X11 #ifdef Q_WS_X11
foreach (const QPixmap &pixmap, m_shadowPixmaps) { foreach (KPixmap &pixmap, m_shadowPixmaps) {
if (!QX11Info::display()) { if (!QX11Info::display()) {
return; return;
} }
if (!pixmap.isNull()) { if (!pixmap.isNull()) {
XFreePixmap(QX11Info::display(), pixmap.handle()); pixmap.release();
} }
} }
if (!m_emptyCornerPix.isNull()) { if (!m_emptyCornerPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyCornerPix.handle()); m_emptyCornerPix.release();
} }
if (!m_emptyCornerBottomPix.isNull()) { if (!m_emptyCornerBottomPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyCornerBottomPix.handle()); m_emptyCornerBottomPix.release();
} }
if (!m_emptyCornerLeftPix.isNull()) { if (!m_emptyCornerLeftPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyCornerLeftPix.handle()); m_emptyCornerLeftPix.release();
} }
if (!m_emptyCornerRightPix.isNull()) { if (!m_emptyCornerRightPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyCornerRightPix.handle()); m_emptyCornerRightPix.release();
} }
if (!m_emptyCornerTopPix.isNull()) { if (!m_emptyCornerTopPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyCornerTopPix.handle()); m_emptyCornerTopPix.release();
} }
if (!m_emptyVerticalPix.isNull()) { if (!m_emptyVerticalPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyVerticalPix.handle()); m_emptyVerticalPix.release();
} }
if (!m_emptyHorizontalPix.isNull()) { if (!m_emptyHorizontalPix.isNull()) {
XFreePixmap(QX11Info::display(), m_emptyHorizontalPix.handle()); m_emptyHorizontalPix.release();
} }
#endif #endif
} }
@ -359,13 +335,13 @@ void DialogShadowsPrivate::clearPixmaps()
#ifdef Q_WS_X11 #ifdef Q_WS_X11
freeX11Pixmaps(); freeX11Pixmaps();
m_emptyCornerPix = QPixmap(); m_emptyCornerPix = KPixmap();
m_emptyCornerBottomPix = QPixmap(); m_emptyCornerBottomPix = KPixmap();
m_emptyCornerLeftPix = QPixmap(); m_emptyCornerLeftPix = KPixmap();
m_emptyCornerRightPix = QPixmap(); m_emptyCornerRightPix = KPixmap();
m_emptyCornerTopPix = QPixmap(); m_emptyCornerTopPix = KPixmap();
m_emptyVerticalPix = QPixmap(); m_emptyVerticalPix = KPixmap();
m_emptyHorizontalPix = QPixmap(); m_emptyHorizontalPix = KPixmap();
#endif #endif
m_shadowPixmaps.clear(); m_shadowPixmaps.clear();
data.clear(); data.clear();