1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qxcbcursor.h"
5#include "qxcbconnection.h"
6#include "qxcbwindow.h"
7#include "qxcbimage.h"
8#include "qxcbxsettings.h"
9
10#include <QtGui/QWindow>
11#include <QtGui/QBitmap>
12#include <QtGui/private/qguiapplication_p.h>
13#include <qpa/qplatformtheme.h>
14
15#if QT_CONFIG(xcb_xlib)
16#include <X11/cursorfont.h>
17#else
18#include "qxcbcursorfont.h"
19#endif
20
21#include <xcb/xfixes.h>
22#include <xcb/xcb_image.h>
23
24QT_BEGIN_NAMESPACE
25
26using namespace Qt::StringLiterals;
27
28static xcb_font_t cursorFont = 0;
29static int cursorCount = 0;
30
31#ifndef QT_NO_CURSOR
32
33static uint8_t cur_blank_bits[] = {
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
37
38static const uint8_t cur_ver_bits[] = {
39 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, 0xf0, 0x0f,
40 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0xf0, 0x0f,
41 0xe0, 0x07, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x00 };
42static const uint8_t mcur_ver_bits[] = {
43 0x00, 0x00, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f,
44 0xfc, 0x7f, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xfc, 0x7f, 0xf8, 0x3f,
45 0xf0, 0x1f, 0xe0, 0x0f, 0xc0, 0x07, 0x80, 0x03 };
46static const uint8_t cur_hor_bits[] = {
47 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x08, 0x30, 0x18,
48 0x38, 0x38, 0xfc, 0x7f, 0xfc, 0x7f, 0x38, 0x38, 0x30, 0x18, 0x20, 0x08,
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
50static const uint8_t mcur_hor_bits[] = {
51 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x60, 0x0c, 0x70, 0x1c, 0x78, 0x3c,
52 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0x78, 0x3c,
53 0x70, 0x1c, 0x60, 0x0c, 0x40, 0x04, 0x00, 0x00 };
54static const uint8_t cur_bdiag_bits[] = {
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x3e,
56 0x00, 0x37, 0x88, 0x23, 0xd8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0xf8, 0x00,
57 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
58static const uint8_t mcur_bdiag_bits[] = {
59 0x00, 0x00, 0xc0, 0x7f, 0x80, 0x7f, 0x00, 0x7f, 0x00, 0x7e, 0x04, 0x7f,
60 0x8c, 0x7f, 0xdc, 0x77, 0xfc, 0x63, 0xfc, 0x41, 0xfc, 0x00, 0xfc, 0x01,
61 0xfc, 0x03, 0xfc, 0x07, 0x00, 0x00, 0x00, 0x00 };
62static const uint8_t cur_fdiag_bits[] = {
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00,
64 0xf8, 0x00, 0xd8, 0x01, 0x88, 0x23, 0x00, 0x37, 0x00, 0x3e, 0x00, 0x3c,
65 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00 };
66static const uint8_t mcur_fdiag_bits[] = {
67 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xfc, 0x03, 0xfc, 0x01, 0xfc, 0x00,
68 0xfc, 0x41, 0xfc, 0x63, 0xdc, 0x77, 0x8c, 0x7f, 0x04, 0x7f, 0x00, 0x7e,
69 0x00, 0x7f, 0x80, 0x7f, 0xc0, 0x7f, 0x00, 0x00 };
70static const uint8_t *cursor_bits16[] = {
71 cur_ver_bits, mcur_ver_bits, cur_hor_bits, mcur_hor_bits,
72 cur_bdiag_bits, mcur_bdiag_bits, cur_fdiag_bits, mcur_fdiag_bits,
73 nullptr, nullptr, cur_blank_bits, cur_blank_bits };
74
75static const uint8_t vsplit_bits[] = {
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00,
79 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
80 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0x00,
82 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
83 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
84 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
87static const uint8_t vsplitm_bits[] = {
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
90 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x07, 0x00,
91 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
92 0x00, 0xc0, 0x01, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
93 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00, 0x80, 0xff, 0xff, 0x00,
94 0x80, 0xff, 0xff, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xc0, 0x01, 0x00,
95 0x00, 0xc0, 0x01, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0x00, 0xf0, 0x07, 0x00,
96 0x00, 0xe0, 0x03, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
99static const uint8_t hsplit_bits[] = {
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
103 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
104 0x00, 0x41, 0x82, 0x00, 0x80, 0x41, 0x82, 0x01, 0xc0, 0x7f, 0xfe, 0x03,
105 0x80, 0x41, 0x82, 0x01, 0x00, 0x41, 0x82, 0x00, 0x00, 0x40, 0x02, 0x00,
106 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00,
107 0x00, 0x40, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
111static const uint8_t hsplitm_bits[] = {
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
115 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe3, 0xc7, 0x00,
116 0x80, 0xe3, 0xc7, 0x01, 0xc0, 0xff, 0xff, 0x03, 0xe0, 0xff, 0xff, 0x07,
117 0xc0, 0xff, 0xff, 0x03, 0x80, 0xe3, 0xc7, 0x01, 0x00, 0xe3, 0xc7, 0x00,
118 0x00, 0xe2, 0x47, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00,
119 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
123static const uint8_t whatsthis_bits[] = {
124 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0xf0, 0x07, 0x00,
125 0x09, 0x18, 0x0e, 0x00, 0x11, 0x1c, 0x0e, 0x00, 0x21, 0x1c, 0x0e, 0x00,
126 0x41, 0x1c, 0x0e, 0x00, 0x81, 0x1c, 0x0e, 0x00, 0x01, 0x01, 0x07, 0x00,
127 0x01, 0x82, 0x03, 0x00, 0xc1, 0xc7, 0x01, 0x00, 0x49, 0xc0, 0x01, 0x00,
128 0x95, 0xc0, 0x01, 0x00, 0x93, 0xc0, 0x01, 0x00, 0x21, 0x01, 0x00, 0x00,
129 0x20, 0xc1, 0x01, 0x00, 0x40, 0xc2, 0x01, 0x00, 0x40, 0x02, 0x00, 0x00,
130 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
135static const uint8_t whatsthism_bits[] = {
136 0x01, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x07, 0x00, 0x07, 0xf8, 0x0f, 0x00,
137 0x0f, 0xfc, 0x1f, 0x00, 0x1f, 0x3e, 0x1f, 0x00, 0x3f, 0x3e, 0x1f, 0x00,
138 0x7f, 0x3e, 0x1f, 0x00, 0xff, 0x3e, 0x1f, 0x00, 0xff, 0x9d, 0x0f, 0x00,
139 0xff, 0xc3, 0x07, 0x00, 0xff, 0xe7, 0x03, 0x00, 0x7f, 0xe0, 0x03, 0x00,
140 0xf7, 0xe0, 0x03, 0x00, 0xf3, 0xe0, 0x03, 0x00, 0xe1, 0xe1, 0x03, 0x00,
141 0xe0, 0xe1, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00, 0xc0, 0xe3, 0x03, 0x00,
142 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
143 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
146 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
147static const uint8_t busy_bits[] = {
148 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
149 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
150 0x41, 0xe0, 0xff, 0x00, 0x81, 0x20, 0x80, 0x00, 0x01, 0xe1, 0xff, 0x00,
151 0x01, 0x42, 0x40, 0x00, 0xc1, 0x47, 0x40, 0x00, 0x49, 0x40, 0x55, 0x00,
152 0x95, 0x80, 0x2a, 0x00, 0x93, 0x00, 0x15, 0x00, 0x21, 0x01, 0x0a, 0x00,
153 0x20, 0x01, 0x11, 0x00, 0x40, 0x82, 0x20, 0x00, 0x40, 0x42, 0x44, 0x00,
154 0x80, 0x41, 0x4a, 0x00, 0x00, 0x40, 0x55, 0x00, 0x00, 0xe0, 0xff, 0x00,
155 0x00, 0x20, 0x80, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
156 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
159static const uint8_t busym_bits[] = {
160 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
161 0x0f, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
162 0x7f, 0xe0, 0xff, 0x00, 0xff, 0xe0, 0xff, 0x00, 0xff, 0xe1, 0xff, 0x00,
163 0xff, 0xc3, 0x7f, 0x00, 0xff, 0xc7, 0x7f, 0x00, 0x7f, 0xc0, 0x7f, 0x00,
164 0xf7, 0x80, 0x3f, 0x00, 0xf3, 0x00, 0x1f, 0x00, 0xe1, 0x01, 0x0e, 0x00,
165 0xe0, 0x01, 0x1f, 0x00, 0xc0, 0x83, 0x3f, 0x00, 0xc0, 0xc3, 0x7f, 0x00,
166 0x80, 0xc1, 0x7f, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, 0xe0, 0xff, 0x00,
167 0x00, 0xe0, 0xff, 0x00, 0x00, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
171
172static const uint8_t * const cursor_bits32[] = {
173 vsplit_bits, vsplitm_bits, hsplit_bits, hsplitm_bits,
174 nullptr, nullptr, nullptr, nullptr, whatsthis_bits, whatsthism_bits, busy_bits, busym_bits
175};
176
177static const uint8_t forbidden_bits[] = {
178 0x00,0x00,0x00,0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xf0,0x00,0x38,0xc0,0x01,
179 0x7c,0x80,0x03,0xec,0x00,0x03,0xce,0x01,0x07,0x86,0x03,0x06,0x06,0x07,0x06,
180 0x06,0x0e,0x06,0x06,0x1c,0x06,0x0e,0x38,0x07,0x0c,0x70,0x03,0x1c,0xe0,0x03,
181 0x38,0xc0,0x01,0xf0,0xe0,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00,0x00,0x00,0x00 };
182
183static const uint8_t forbiddenm_bits[] = {
184 0x80,0x1f,0x00,0xe0,0x7f,0x00,0xf0,0xff,0x00,0xf8,0xff,0x01,0xfc,0xf0,0x03,
185 0xfe,0xc0,0x07,0xfe,0x81,0x07,0xff,0x83,0x0f,0xcf,0x07,0x0f,0x8f,0x0f,0x0f,
186 0x0f,0x1f,0x0f,0x0f,0x3e,0x0f,0x1f,0xfc,0x0f,0x1e,0xf8,0x07,0x3e,0xf0,0x07,
187 0xfc,0xe0,0x03,0xf8,0xff,0x01,0xf0,0xff,0x00,0xe0,0x7f,0x00,0x80,0x1f,0x00};
188
189static const uint8_t openhand_bits[] = {
190 0x80,0x01,0x58,0x0e,0x64,0x12,0x64,0x52,0x48,0xb2,0x48,0x92,
191 0x16,0x90,0x19,0x80,0x11,0x40,0x02,0x40,0x04,0x40,0x04,0x20,
192 0x08,0x20,0x10,0x10,0x20,0x10,0x00,0x00};
193static const uint8_t openhandm_bits[] = {
194 0x80,0x01,0xd8,0x0f,0xfc,0x1f,0xfc,0x5f,0xf8,0xff,0xf8,0xff,
195 0xf6,0xff,0xff,0xff,0xff,0x7f,0xfe,0x7f,0xfc,0x7f,0xfc,0x3f,
196 0xf8,0x3f,0xf0,0x1f,0xe0,0x1f,0x00,0x00};
197static const uint8_t closedhand_bits[] = {
198 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0x48,0x32,0x08,0x50,
199 0x10,0x40,0x18,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,
200 0x20,0x10,0x20,0x10,0x00,0x00,0x00,0x00};
201static const uint8_t closedhandm_bits[] = {
202 0x00,0x00,0x00,0x00,0x00,0x00,0xb0,0x0d,0xf8,0x3f,0xf8,0x7f,
203 0xf0,0x7f,0xf8,0x7f,0xfc,0x7f,0xfc,0x3f,0xf8,0x3f,0xf0,0x1f,
204 0xe0,0x1f,0xe0,0x1f,0x00,0x00,0x00,0x00};
205
206static const uint8_t * const cursor_bits20[] = {
207 forbidden_bits, forbiddenm_bits
208};
209
210// ### FIXME This mapping is incomplete - QTBUG-71423
211static const std::vector<const char *> cursorNames[] = {
212 { "left_ptr", "default", "top_left_arrow", "left_arrow" },
213 { "up_arrow" },
214 { "cross" },
215 { "wait", "watch", "0426c94ea35c87780ff01dc239897213" },
216 { "ibeam", "text", "xterm" },
217 { "size_ver", "ns-resize", "v_double_arrow", "00008160000006810000408080010102" },
218 { "size_hor", "ew-resize", "h_double_arrow", "028006030e0e7ebffc7f7070c0600140" },
219 { "size_bdiag", "nesw-resize", "50585d75b494802d0151028115016902", "fcf1c3c7cd4491d801f1e1c78f100000" },
220 { "size_fdiag", "nwse-resize", "38c5dff7c7b8962045400281044508d2", "c7088f0f3e6c8088236ef8e1e3e70000" },
221 { "size_all" },
222 { "blank" },
223 { "split_v", "row-resize", "sb_v_double_arrow", "2870a09082c103050810ffdffffe0204", "c07385c7190e701020ff7ffffd08103c" },
224 { "split_h", "col-resize", "sb_h_double_arrow", "043a9f68147c53184671403ffa811cc5", "14fef782d02440884392942c11205230" },
225 { "pointing_hand", "pointer", "hand1", "e29285e634086352946a0e7090d73106" },
226 { "forbidden", "not-allowed", "crossed_circle", "circle", "03b6e0fcb3499374a867c041f52298f0" },
227 { "whats_this", "help", "question_arrow", "5c6cd98b3f3ebcb1f9c7f1c204630408", "d9ce0ab605698f320427677b458ad60b" },
228 { "left_ptr_watch", "half-busy", "progress", "00000000000000020006000e7e9ffc3f", "08e8e1c95fe2fc01f976f1e063a24ccd" },
229 { "openhand", "grab", "fleur", "5aca4d189052212118709018842178c0", "9d800788f1b08800ae810202380a0822" },
230 { "closedhand", "grabbing", "208530c400c041818281048008011002" },
231 { "dnd-copy", "copy" },
232 { "dnd-move", "move" },
233 { "dnd-link", "link" }
234};
235
236QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c)
237 : shape(c.shape()), bitmapCacheKey(0), maskCacheKey(0)
238{
239 if (shape == Qt::BitmapCursor) {
240 const qint64 pixmapCacheKey = c.pixmap().cacheKey();
241 if (pixmapCacheKey) {
242 bitmapCacheKey = pixmapCacheKey;
243 } else {
244 Q_ASSERT(!c.bitmap().isNull());
245 Q_ASSERT(!c.mask().isNull());
246 bitmapCacheKey = c.bitmap().cacheKey();
247 maskCacheKey = c.mask().cacheKey();
248 }
249 }
250}
251
252#endif // !QT_NO_CURSOR
253
254QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen)
255 : QXcbObject(conn), m_screen(screen), m_cursorContext(nullptr), m_callbackForPropertyRegistered(false)
256{
257#if QT_CONFIG(cursor)
258 // see NUM_BITMAPS in libXcursor/src/xcursorint.h
259 m_bitmapCache.setMaxCost(8);
260#endif
261
262 updateContext();
263
264 if (cursorCount++)
265 return;
266
267 cursorFont = xcb_generate_id(c: xcb_connection());
268 const char *cursorStr = "cursor";
269 xcb_open_font(c: xcb_connection(), fid: cursorFont, name_len: strlen(s: cursorStr), name: cursorStr);
270}
271
272QXcbCursor::~QXcbCursor()
273{
274 xcb_connection_t *conn = xcb_connection();
275
276 if (m_callbackForPropertyRegistered) {
277 m_screen->xSettings()->removeCallbackForHandle(handle: this);
278 }
279
280 if (!--cursorCount)
281 xcb_close_font(c: conn, font: cursorFont);
282
283#ifndef QT_NO_CURSOR
284 for (xcb_cursor_t cursor : std::as_const(t&: m_cursorHash))
285 xcb_free_cursor(c: conn, cursor);
286#endif
287
288 if (m_cursorContext)
289 xcb_cursor_context_free(ctx: m_cursorContext);
290}
291
292QSize QXcbCursor::size() const
293{
294 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
295 return theme->themeHint(hint: QPlatformTheme::MouseCursorSize).toSize();
296 return QSize(24, 24);
297}
298
299void QXcbCursor::updateContext()
300{
301 if (m_cursorContext)
302 xcb_cursor_context_free(ctx: m_cursorContext);
303
304 m_cursorContext = nullptr;
305
306 xcb_connection_t *conn = xcb_connection();
307 if (xcb_cursor_context_new(conn, screen: m_screen->screen(), ctx: &m_cursorContext) < 0) {
308 qWarning() << "xcb: Could not initialize xcb-cursor";
309 m_cursorContext = nullptr;
310 }
311}
312
313#ifndef QT_NO_CURSOR
314void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window)
315{
316 if (!window || !window->handle())
317 return;
318
319 xcb_cursor_t c = XCB_CURSOR_NONE;
320 if (cursor) {
321 const QXcbCursorCacheKey key(*cursor);
322 const Qt::CursorShape shape = cursor->shape();
323
324 if (shape == Qt::BitmapCursor) {
325 auto *bitmap = m_bitmapCache.object(key);
326 if (bitmap) {
327 c = bitmap->cursor;
328 } else {
329 c = createBitmapCursor(cursor);
330 m_bitmapCache.insert(key, object: new CachedCursor(xcb_connection(), c));
331 }
332 } else {
333 auto it = m_cursorHash.find(key);
334 if (it == m_cursorHash.end()) {
335 c = createFontCursor(cshape: shape);
336 m_cursorHash.insert(key, value: c);
337 } else {
338 c = it.value();
339 }
340 }
341 }
342
343 auto *w = static_cast<QXcbWindow *>(window->handle());
344 xcb_change_window_attributes(c: xcb_connection(), window: w->xcb_window(), value_mask: XCB_CW_CURSOR, value_list: &c);
345 xcb_flush(c: xcb_connection());
346}
347
348static int cursorIdForShape(int cshape)
349{
350 int cursorId = 0;
351 switch (cshape) {
352 case Qt::ArrowCursor:
353 cursorId = XC_left_ptr;
354 break;
355 case Qt::UpArrowCursor:
356 cursorId = XC_center_ptr;
357 break;
358 case Qt::CrossCursor:
359 cursorId = XC_crosshair;
360 break;
361 case Qt::WaitCursor:
362 cursorId = XC_watch;
363 break;
364 case Qt::IBeamCursor:
365 cursorId = XC_xterm;
366 break;
367 case Qt::SizeAllCursor:
368 cursorId = XC_fleur;
369 break;
370 case Qt::PointingHandCursor:
371 cursorId = XC_hand2;
372 break;
373 case Qt::SizeBDiagCursor:
374 cursorId = XC_top_right_corner;
375 break;
376 case Qt::SizeFDiagCursor:
377 cursorId = XC_bottom_right_corner;
378 break;
379 case Qt::SizeVerCursor:
380 case Qt::SplitVCursor:
381 cursorId = XC_sb_v_double_arrow;
382 break;
383 case Qt::SizeHorCursor:
384 case Qt::SplitHCursor:
385 cursorId = XC_sb_h_double_arrow;
386 break;
387 case Qt::WhatsThisCursor:
388 cursorId = XC_question_arrow;
389 break;
390 case Qt::ForbiddenCursor:
391 cursorId = XC_circle;
392 break;
393 case Qt::BusyCursor:
394 cursorId = XC_watch;
395 break;
396 default:
397 break;
398 }
399 return cursorId;
400}
401
402xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape)
403{
404 xcb_cursor_t cursor = 0;
405 xcb_connection_t *conn = xcb_connection();
406
407 if (cshape == Qt::BlankCursor) {
408 xcb_pixmap_t cp = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(), data: cur_blank_bits, width: 16, height: 16,
409 depth: 1, fg: 0, bg: 0, gcp: nullptr);
410 xcb_pixmap_t mp = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(), data: cur_blank_bits, width: 16, height: 16,
411 depth: 1, fg: 0, bg: 0, gcp: nullptr);
412 cursor = xcb_generate_id(c: conn);
413 xcb_create_cursor(c: conn, cid: cursor, source: cp, mask: mp, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF, x: 8, y: 8);
414 } else if (cshape >= Qt::SizeVerCursor && cshape < Qt::SizeAllCursor) {
415 int i = (cshape - Qt::SizeVerCursor) * 2;
416 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
417 data: const_cast<uint8_t*>(cursor_bits16[i]),
418 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
419 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
420 data: const_cast<uint8_t*>(cursor_bits16[i + 1]),
421 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
422 cursor = xcb_generate_id(c: conn);
423 xcb_create_cursor(c: conn, cid: cursor, source: pm, mask: pmm, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF, x: 8, y: 8);
424 } else if ((cshape >= Qt::SplitVCursor && cshape <= Qt::SplitHCursor)
425 || cshape == Qt::WhatsThisCursor || cshape == Qt::BusyCursor) {
426 int i = (cshape - Qt::SplitVCursor) * 2;
427 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
428 data: const_cast<uint8_t*>(cursor_bits32[i]),
429 width: 32, height: 32, depth: 1, fg: 0, bg: 0, gcp: nullptr);
430 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
431 data: const_cast<uint8_t*>(cursor_bits32[i + 1]),
432 width: 32, height: 32, depth: 1, fg: 0, bg: 0, gcp: nullptr);
433 int hs = (cshape == Qt::PointingHandCursor || cshape == Qt::WhatsThisCursor
434 || cshape == Qt::BusyCursor) ? 0 : 16;
435 cursor = xcb_generate_id(c: conn);
436 xcb_create_cursor(c: conn, cid: cursor, source: pm, mask: pmm, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF, x: hs, y: hs);
437 } else if (cshape == Qt::ForbiddenCursor) {
438 int i = (cshape - Qt::ForbiddenCursor) * 2;
439 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
440 data: const_cast<uint8_t*>(cursor_bits20[i]),
441 width: 20, height: 20, depth: 1, fg: 0, bg: 0, gcp: nullptr);
442 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
443 data: const_cast<uint8_t*>(cursor_bits20[i + 1]),
444 width: 20, height: 20, depth: 1, fg: 0, bg: 0, gcp: nullptr);
445 cursor = xcb_generate_id(c: conn);
446 xcb_create_cursor(c: conn, cid: cursor, source: pm, mask: pmm, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF, x: 10, y: 10);
447 } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) {
448 bool open = cshape == Qt::OpenHandCursor;
449 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
450 data: const_cast<uint8_t*>(open ? openhand_bits : closedhand_bits),
451 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
452 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
453 data: const_cast<uint8_t*>(open ? openhandm_bits : closedhandm_bits),
454 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
455 cursor = xcb_generate_id(c: conn);
456 xcb_create_cursor(c: conn, cid: cursor, source: pm, mask: pmm, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF, x: 8, y: 8);
457 } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor
458 || cshape == Qt::DragLinkCursor) {
459 QImage image = QGuiApplicationPrivate::instance()->getPixmapCursor(cshape: static_cast<Qt::CursorShape>(cshape)).toImage();
460 if (!image.isNull()) {
461 xcb_pixmap_t pm = qt_xcb_XPixmapFromBitmap(screen: m_screen, image);
462 xcb_pixmap_t pmm = qt_xcb_XPixmapFromBitmap(screen: m_screen, image: image.createAlphaMask());
463 cursor = xcb_generate_id(c: conn);
464 xcb_create_cursor(c: conn, cid: cursor, source: pm, mask: pmm, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF, x: 8, y: 8);
465 xcb_free_pixmap(c: conn, pixmap: pm);
466 xcb_free_pixmap(c: conn, pixmap: pmm);
467 }
468 }
469
470 return cursor;
471}
472
473void QXcbCursor::cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, void *handle)
474{
475 Q_UNUSED(screen);
476 Q_UNUSED(name);
477 Q_UNUSED(property);
478 QXcbCursor *self = static_cast<QXcbCursor *>(handle);
479 self->m_cursorHash.clear();
480 self->updateContext();
481}
482
483xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
484{
485 if (!m_cursorContext)
486 return XCB_NONE;
487
488 xcb_connection_t *conn = xcb_connection();
489 int cursorId = cursorIdForShape(cshape);
490 xcb_cursor_t cursor = XCB_NONE;
491
492 if (!m_callbackForPropertyRegistered && m_screen->xSettings()->initialized()) {
493 m_screen->xSettings()->registerCallbackForProperty(property: "Gtk/CursorThemeName", func: cursorThemePropertyChanged, handle: this);
494
495 m_callbackForPropertyRegistered = true;
496 }
497
498 // Try xcb-cursor first
499 if (cshape >= 0 && cshape <= Qt::LastCursor) {
500 for (const char *cursorName : cursorNames[cshape]) {
501 cursor = xcb_cursor_load_cursor(ctx: m_cursorContext, name: cursorName);
502 if (cursor != XCB_NONE)
503 return cursor;
504 }
505 }
506
507 // Non-standard X11 cursors are created from bitmaps
508 cursor = createNonStandardCursor(cshape);
509
510 // Create a glyph cursor if everything else failed
511 if (!cursor && cursorId) {
512 cursor = xcb_generate_id(c: conn);
513 xcb_create_glyph_cursor(c: conn, cid: cursor, source_font: cursorFont, mask_font: cursorFont,
514 source_char: cursorId, mask_char: cursorId + 1,
515 fore_red: 0xFFFF, fore_green: 0xFFFF, fore_blue: 0xFFFF, back_red: 0, back_green: 0, back_blue: 0);
516 }
517
518 if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
519 const char *name = cursorNames[cshape].front();
520 xcb_xfixes_set_cursor_name(c: conn, cursor, nbytes: strlen(s: name), name);
521 }
522
523 return cursor;
524}
525
526xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
527{
528 QPoint spot = cursor->hotSpot();
529 xcb_cursor_t c = XCB_NONE;
530 if (cursor->pixmap().depth() > 1) {
531 if (connection()->hasXRender(major: 0, minor: 5))
532 c = qt_xcb_createCursorXRender(screen: m_screen, image: cursor->pixmap().toImage(), spot);
533 else
534 qCWarning(lcQpaXcb, "xrender >= 0.5 required to create pixmap cursors");
535 } else {
536 xcb_connection_t *conn = xcb_connection();
537 xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(screen: m_screen, image: cursor->bitmap().toImage());
538 xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(screen: m_screen, image: cursor->mask().toImage());
539 c = xcb_generate_id(c: conn);
540 xcb_create_cursor(c: conn, cid: c, source: cp, mask: mp, fore_red: 0, fore_green: 0, fore_blue: 0, back_red: 0xFFFF, back_green: 0xFFFF, back_blue: 0xFFFF,
541 x: spot.x(), y: spot.y());
542 xcb_free_pixmap(c: conn, pixmap: cp);
543 xcb_free_pixmap(c: conn, pixmap: mp);
544 }
545 return c;
546}
547#endif
548
549/*! \internal
550
551 Note that the logical state of a device (as seen by means of the protocol) may
552 lag the physical state if device event processing is frozen. See QueryPointer
553 in X11 protocol specification.
554*/
555void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask)
556{
557 if (pos)
558 *pos = QPoint();
559
560 xcb_window_t root = c->primaryVirtualDesktop()->root();
561
562 auto reply = Q_XCB_REPLY(xcb_query_pointer, c->xcb_connection(), root);
563 if (reply) {
564 if (virtualDesktop) {
565 const auto virtualDesktops = c->virtualDesktops();
566 for (QXcbVirtualDesktop *vd : virtualDesktops) {
567 if (vd->root() == reply->root) {
568 *virtualDesktop = vd;
569 break;
570 }
571 }
572 }
573 if (pos)
574 *pos = QPoint(reply->root_x, reply->root_y);
575 if (keybMask)
576 *keybMask = reply->mask;
577 return;
578 }
579}
580
581QPoint QXcbCursor::pos() const
582{
583 QPoint p;
584 queryPointer(c: connection(), virtualDesktop: nullptr, pos: &p);
585 return p;
586}
587
588void QXcbCursor::setPos(const QPoint &pos)
589{
590 QXcbVirtualDesktop *virtualDesktop = nullptr;
591 queryPointer(c: connection(), virtualDesktop: &virtualDesktop, pos: nullptr);
592 if (virtualDesktop)
593 xcb_warp_pointer(c: xcb_connection(), XCB_NONE, dst_window: virtualDesktop->root(), src_x: 0, src_y: 0, src_width: 0, src_height: 0, dst_x: pos.x(), dst_y: pos.y());
594 xcb_flush(c: xcb_connection());
595}
596
597QT_END_NAMESPACE
598

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/plugins/platforms/xcb/qxcbcursor.cpp