1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@kde.org>
4     SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8 
9 #include "kwindowsystem_xcb_debug.h"
10 #include "kxutils_p.h"
11 #include <QBitmap>
12 #include <QX11Info>
13 
14 #include <xcb/xcb.h>
15 
16 namespace KXUtils
17 {
18 template<typename T>
fromNative(xcb_pixmap_t pixmap,xcb_connection_t * c)19 T fromNative(xcb_pixmap_t pixmap, xcb_connection_t *c)
20 {
21     const xcb_get_geometry_cookie_t geoCookie = xcb_get_geometry_unchecked(c, pixmap);
22     ScopedCPointer<xcb_get_geometry_reply_t> geo(xcb_get_geometry_reply(c, geoCookie, nullptr));
23     if (geo.isNull()) {
24         // getting geometry for the pixmap failed
25         return T();
26     }
27 
28     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);
29     ScopedCPointer<xcb_get_image_reply_t> xImage(xcb_get_image_reply(c, imageCookie, nullptr));
30     if (xImage.isNull()) {
31         // request for image data failed
32         return T();
33     }
34     QImage::Format format = QImage::Format_Invalid;
35     switch (xImage->depth) {
36     case 1:
37         format = QImage::Format_MonoLSB;
38         break;
39     case 16:
40         format = QImage::Format_RGB16;
41         break;
42     case 24:
43         format = QImage::Format_RGB32;
44         break;
45     case 30: {
46         // Qt doesn't have a matching image format. We need to convert manually
47         uint32_t *pixels = reinterpret_cast<uint32_t *>(xcb_get_image_data(xImage.data()));
48         for (uint i = 0; i < xImage.data()->length; ++i) {
49             int r = (pixels[i] >> 22) & 0xff;
50             int g = (pixels[i] >> 12) & 0xff;
51             int b = (pixels[i] >> 2) & 0xff;
52 
53             pixels[i] = qRgba(r, g, b, 0xff);
54         }
55         // fall through, Qt format is still Format_ARGB32_Premultiplied
56         Q_FALLTHROUGH();
57     }
58     case 32:
59         format = QImage::Format_ARGB32_Premultiplied;
60         break;
61     default:
62         return T(); // we don't know
63     }
64     QImage
65         image(xcb_get_image_data(xImage.data()), geo->width, geo->height, xcb_get_image_data_length(xImage.data()) / geo->height, format, free, xImage.data());
66     xImage.take();
67     if (image.isNull()) {
68         return T();
69     }
70     if (image.format() == QImage::Format_MonoLSB) {
71         // work around an abort in QImage::color
72         image.setColorCount(2);
73         image.setColor(0, QColor(Qt::white).rgb());
74         image.setColor(1, QColor(Qt::black).rgb());
75     }
76     return T::fromImage(image);
77 }
78 
79 // Create QPixmap from X pixmap. Take care of different depths if needed.
createPixmapFromHandle(WId pixmap,WId pixmap_mask)80 QPixmap createPixmapFromHandle(WId pixmap, WId pixmap_mask)
81 {
82     return createPixmapFromHandle(QX11Info::connection(), pixmap, pixmap_mask);
83 }
84 
createPixmapFromHandle(xcb_connection_t * c,WId pixmap,WId pixmap_mask)85 QPixmap createPixmapFromHandle(xcb_connection_t *c, WId pixmap, WId pixmap_mask)
86 {
87 #if Q_BYTE_ORDER == Q_BIG_ENDIAN
88     qCDebug(LOG_KKEYSERVER_X11) << "Byte order not supported";
89     return QPixmap();
90 #endif
91     const xcb_setup_t *setup = xcb_get_setup(c);
92     if (setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST) {
93         qCDebug(LOG_KKEYSERVER_X11) << "Byte order not supported";
94         return QPixmap();
95     }
96 
97     QPixmap pix = fromNative<QPixmap>(pixmap, c);
98     if (pixmap_mask != XCB_PIXMAP_NONE) {
99         QBitmap mask = fromNative<QBitmap>(pixmap_mask, c);
100         if (mask.size() != pix.size()) {
101             return QPixmap();
102         }
103         pix.setMask(mask);
104     }
105     return pix;
106 }
107 
108 // Functions for X timestamp comparing. For Time being 32bit they're fairly simple
109 // (the #if 0 part), but on 64bit architectures Time is 64bit unsigned long,
110 // so there special care needs to be taken to always use only the lower 32bits.
111 #if 0
112 int timestampCompare(Time time1, Time time2)   // like strcmp()
113 {
114     if (time1 == time2) {
115         return 0;
116     }
117     return (time1 - time2) < 0x7fffffffU ? 1 : -1;   // time1 > time2 -> 1, handle wrapping
118 }
119 
120 Time timestampDiff(Time time1, Time time2)   // returns time2 - time1
121 {
122     // no need to handle wrapping?
123     return time2 - time1;
124 }
125 #else
timestampCompare(unsigned long time1_,unsigned long time2_)126 int timestampCompare(unsigned long time1_, unsigned long time2_) // like strcmp()
127 {
128     quint32 time1 = time1_;
129     quint32 time2 = time2_;
130     if (time1 == time2) {
131         return 0;
132     }
133     return quint32(time1 - time2) < 0x7fffffffU ? 1 : -1; // time1 > time2 -> 1, handle wrapping
134 }
135 
timestampDiff(unsigned long time1_,unsigned long time2_)136 int timestampDiff(unsigned long time1_, unsigned long time2_) // returns time2 - time1
137 {
138     // no need to handle wrapping?
139     quint32 time1 = time1_;
140     quint32 time2 = time2_;
141     return quint32(time2 - time1);
142 }
143 #endif
144 
145 } // namespace
146