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 hotspotCacheKey.x = c.hotSpot().x();
251 hotspotCacheKey.y = c.hotSpot().y();
252}
253
254#endif // !QT_NO_CURSOR
255
256QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen)
257 : QXcbObject(conn), m_screen(screen), m_cursorContext(nullptr), m_callbackForPropertyRegistered(false)
258{
259#if QT_CONFIG(cursor)
260 // see NUM_BITMAPS in libXcursor/src/xcursorint.h
261 m_bitmapCache.setMaxCost(8);
262#endif
263
264 updateContext();
265
266 if (cursorCount++)
267 return;
268
269 cursorFont = xcb_generate_id(c: xcb_connection());
270 const char *cursorStr = "cursor";
271 xcb_open_font(c: xcb_connection(), fid: cursorFont, name_len: strlen(s: cursorStr), name: cursorStr);
272}
273
274QXcbCursor::~QXcbCursor()
275{
276 xcb_connection_t *conn = xcb_connection();
277
278 if (m_callbackForPropertyRegistered) {
279 m_screen->xSettings()->removeCallbackForHandle(handle: this);
280 }
281
282 if (!--cursorCount)
283 xcb_close_font(c: conn, font: cursorFont);
284
285#ifndef QT_NO_CURSOR
286 for (xcb_cursor_t cursor : std::as_const(t&: m_cursorHash))
287 xcb_free_cursor(c: conn, cursor);
288#endif
289
290 if (m_cursorContext)
291 xcb_cursor_context_free(ctx: m_cursorContext);
292}
293
294QSize QXcbCursor::size() const
295{
296 if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
297 return theme->themeHint(hint: QPlatformTheme::MouseCursorSize).toSize();
298 return QSize(24, 24);
299}
300
301void QXcbCursor::updateContext()
302{
303 if (m_cursorContext)
304 xcb_cursor_context_free(ctx: m_cursorContext);
305
306 m_cursorContext = nullptr;
307
308 xcb_connection_t *conn = xcb_connection();
309 if (xcb_cursor_context_new(conn, screen: m_screen->screen(), ctx: &m_cursorContext) < 0) {
310 qWarning() << "xcb: Could not initialize xcb-cursor";
311 m_cursorContext = nullptr;
312 }
313}
314
315#ifndef QT_NO_CURSOR
316void QXcbCursor::changeCursor(QCursor *cursor, QWindow *window)
317{
318 if (!window || !window->handle())
319 return;
320
321 xcb_cursor_t c = XCB_CURSOR_NONE;
322 if (cursor) {
323 const QXcbCursorCacheKey key(*cursor);
324 const Qt::CursorShape shape = cursor->shape();
325
326 if (shape == Qt::BitmapCursor) {
327 auto *bitmap = m_bitmapCache.object(key);
328 if (bitmap) {
329 c = bitmap->cursor;
330 } else {
331 c = createBitmapCursor(cursor);
332 m_bitmapCache.insert(key, object: new CachedCursor(xcb_connection(), c));
333 }
334 } else {
335 auto it = m_cursorHash.find(key);
336 if (it == m_cursorHash.end()) {
337 c = createFontCursor(cshape: shape);
338 m_cursorHash.insert(key, value: c);
339 } else {
340 c = it.value();
341 }
342 }
343 }
344
345 auto *w = static_cast<QXcbWindow *>(window->handle());
346 xcb_change_window_attributes(c: xcb_connection(), window: w->xcb_window(), value_mask: XCB_CW_CURSOR, value_list: &c);
347 xcb_flush(c: xcb_connection());
348}
349
350static int cursorIdForShape(int cshape)
351{
352 int cursorId = 0;
353 switch (cshape) {
354 case Qt::ArrowCursor:
355 cursorId = XC_left_ptr;
356 break;
357 case Qt::UpArrowCursor:
358 cursorId = XC_center_ptr;
359 break;
360 case Qt::CrossCursor:
361 cursorId = XC_crosshair;
362 break;
363 case Qt::WaitCursor:
364 cursorId = XC_watch;
365 break;
366 case Qt::IBeamCursor:
367 cursorId = XC_xterm;
368 break;
369 case Qt::SizeAllCursor:
370 cursorId = XC_fleur;
371 break;
372 case Qt::PointingHandCursor:
373 cursorId = XC_hand2;
374 break;
375 case Qt::SizeBDiagCursor:
376 cursorId = XC_top_right_corner;
377 break;
378 case Qt::SizeFDiagCursor:
379 cursorId = XC_bottom_right_corner;
380 break;
381 case Qt::SizeVerCursor:
382 case Qt::SplitVCursor:
383 cursorId = XC_sb_v_double_arrow;
384 break;
385 case Qt::SizeHorCursor:
386 case Qt::SplitHCursor:
387 cursorId = XC_sb_h_double_arrow;
388 break;
389 case Qt::WhatsThisCursor:
390 cursorId = XC_question_arrow;
391 break;
392 case Qt::ForbiddenCursor:
393 cursorId = XC_circle;
394 break;
395 case Qt::BusyCursor:
396 cursorId = XC_watch;
397 break;
398 default:
399 break;
400 }
401 return cursorId;
402}
403
404xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape)
405{
406 xcb_cursor_t cursor = 0;
407 xcb_connection_t *conn = xcb_connection();
408
409 if (cshape == Qt::BlankCursor) {
410 xcb_pixmap_t cp = 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 xcb_pixmap_t mp = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(), data: cur_blank_bits, width: 16, height: 16,
413 depth: 1, fg: 0, bg: 0, gcp: nullptr);
414 cursor = xcb_generate_id(c: conn);
415 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);
416 } else if (cshape >= Qt::SizeVerCursor && cshape < Qt::SizeAllCursor) {
417 int i = (cshape - Qt::SizeVerCursor) * 2;
418 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
419 data: const_cast<uint8_t*>(cursor_bits16[i]),
420 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
421 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
422 data: const_cast<uint8_t*>(cursor_bits16[i + 1]),
423 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
424 cursor = xcb_generate_id(c: conn);
425 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);
426 } else if ((cshape >= Qt::SplitVCursor && cshape <= Qt::SplitHCursor)
427 || cshape == Qt::WhatsThisCursor || cshape == Qt::BusyCursor) {
428 int i = (cshape - Qt::SplitVCursor) * 2;
429 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
430 data: const_cast<uint8_t*>(cursor_bits32[i]),
431 width: 32, height: 32, depth: 1, fg: 0, bg: 0, gcp: nullptr);
432 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
433 data: const_cast<uint8_t*>(cursor_bits32[i + 1]),
434 width: 32, height: 32, depth: 1, fg: 0, bg: 0, gcp: nullptr);
435 int hs = (cshape == Qt::PointingHandCursor || cshape == Qt::WhatsThisCursor
436 || cshape == Qt::BusyCursor) ? 0 : 16;
437 cursor = xcb_generate_id(c: conn);
438 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);
439 } else if (cshape == Qt::ForbiddenCursor) {
440 int i = (cshape - Qt::ForbiddenCursor) * 2;
441 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
442 data: const_cast<uint8_t*>(cursor_bits20[i]),
443 width: 20, height: 20, depth: 1, fg: 0, bg: 0, gcp: nullptr);
444 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
445 data: const_cast<uint8_t*>(cursor_bits20[i + 1]),
446 width: 20, height: 20, depth: 1, fg: 0, bg: 0, gcp: nullptr);
447 cursor = xcb_generate_id(c: conn);
448 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);
449 } else if (cshape == Qt::OpenHandCursor || cshape == Qt::ClosedHandCursor) {
450 bool open = cshape == Qt::OpenHandCursor;
451 xcb_pixmap_t pm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
452 data: const_cast<uint8_t*>(open ? openhand_bits : closedhand_bits),
453 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
454 xcb_pixmap_t pmm = xcb_create_pixmap_from_bitmap_data(display: conn, d: m_screen->root(),
455 data: const_cast<uint8_t*>(open ? openhandm_bits : closedhandm_bits),
456 width: 16, height: 16, depth: 1, fg: 0, bg: 0, gcp: nullptr);
457 cursor = xcb_generate_id(c: conn);
458 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);
459 } else if (cshape == Qt::DragCopyCursor || cshape == Qt::DragMoveCursor
460 || cshape == Qt::DragLinkCursor) {
461 QImage image = QGuiApplicationPrivate::instance()->getPixmapCursor(cshape: static_cast<Qt::CursorShape>(cshape)).toImage();
462 if (!image.isNull()) {
463 xcb_pixmap_t pm = qt_xcb_XPixmapFromBitmap(screen: m_screen, image);
464 xcb_pixmap_t pmm = qt_xcb_XPixmapFromBitmap(screen: m_screen, image: image.createAlphaMask());
465 cursor = xcb_generate_id(c: conn);
466 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);
467 xcb_free_pixmap(c: conn, pixmap: pm);
468 xcb_free_pixmap(c: conn, pixmap: pmm);
469 }
470 }
471
472 return cursor;
473}
474
475void QXcbCursor::cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, void *handle)
476{
477 Q_UNUSED(screen);
478 Q_UNUSED(name);
479 Q_UNUSED(property);
480 QXcbCursor *self = static_cast<QXcbCursor *>(handle);
481 self->m_cursorHash.clear();
482 self->updateContext();
483}
484
485xcb_cursor_t QXcbCursor::createFontCursor(int cshape)
486{
487 if (!m_cursorContext)
488 return XCB_NONE;
489
490 xcb_connection_t *conn = xcb_connection();
491 int cursorId = cursorIdForShape(cshape);
492 xcb_cursor_t cursor = XCB_NONE;
493
494 if (!m_callbackForPropertyRegistered && m_screen->xSettings()->initialized()) {
495 m_screen->xSettings()->registerCallbackForProperty(property: "Gtk/CursorThemeName", func: cursorThemePropertyChanged, handle: this);
496
497 m_callbackForPropertyRegistered = true;
498 }
499
500 // Try xcb-cursor first
501 if (cshape >= 0 && cshape <= Qt::LastCursor) {
502 for (const char *cursorName : cursorNames[cshape]) {
503 cursor = xcb_cursor_load_cursor(ctx: m_cursorContext, name: cursorName);
504 if (cursor != XCB_NONE)
505 return cursor;
506 }
507 }
508
509 // Non-standard X11 cursors are created from bitmaps
510 cursor = createNonStandardCursor(cshape);
511
512 // Create a glyph cursor if everything else failed
513 if (!cursor && cursorId) {
514 cursor = xcb_generate_id(c: conn);
515 xcb_create_glyph_cursor(c: conn, cid: cursor, source_font: cursorFont, mask_font: cursorFont,
516 source_char: cursorId, mask_char: cursorId + 1,
517 fore_red: 0xFFFF, fore_green: 0xFFFF, fore_blue: 0xFFFF, back_red: 0, back_green: 0, back_blue: 0);
518 }
519
520 if (cursor && cshape >= 0 && cshape < Qt::LastCursor && connection()->hasXFixes()) {
521 const char *name = cursorNames[cshape].front();
522 xcb_xfixes_set_cursor_name(c: conn, cursor, nbytes: strlen(s: name), name);
523 }
524
525 return cursor;
526}
527
528xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
529{
530 QPoint spot = cursor->hotSpot();
531 xcb_cursor_t c = XCB_NONE;
532 if (cursor->pixmap().depth() > 1) {
533 if (connection()->hasXRender(major: 0, minor: 5))
534 c = qt_xcb_createCursorXRender(screen: m_screen, image: cursor->pixmap().toImage(), spot);
535 else
536 qCWarning(lcQpaXcb, "xrender >= 0.5 required to create pixmap cursors");
537 } else {
538 xcb_connection_t *conn = xcb_connection();
539 xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(screen: m_screen, image: cursor->bitmap().toImage());
540 xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(screen: m_screen, image: cursor->mask().toImage());
541 c = xcb_generate_id(c: conn);
542 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,
543 x: spot.x(), y: spot.y());
544 xcb_free_pixmap(c: conn, pixmap: cp);
545 xcb_free_pixmap(c: conn, pixmap: mp);
546 }
547 return c;
548}
549#endif
550
551/*! \internal
552
553 Note that the logical state of a device (as seen by means of the protocol) may
554 lag the physical state if device event processing is frozen. See QueryPointer
555 in X11 protocol specification.
556*/
557void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask)
558{
559 if (pos)
560 *pos = QPoint();
561
562 xcb_window_t root = c->primaryVirtualDesktop()->root();
563
564 auto reply = Q_XCB_REPLY(xcb_query_pointer, c->xcb_connection(), root);
565 if (reply) {
566 if (virtualDesktop) {
567 const auto virtualDesktops = c->virtualDesktops();
568 for (QXcbVirtualDesktop *vd : virtualDesktops) {
569 if (vd->root() == reply->root) {
570 *virtualDesktop = vd;
571 break;
572 }
573 }
574 }
575 if (pos)
576 *pos = QPoint(reply->root_x, reply->root_y);
577 if (keybMask)
578 *keybMask = reply->mask;
579 return;
580 }
581}
582
583QPoint QXcbCursor::pos() const
584{
585 QPoint p;
586 queryPointer(c: connection(), virtualDesktop: nullptr, pos: &p);
587 return p;
588}
589
590void QXcbCursor::setPos(const QPoint &pos)
591{
592 QXcbVirtualDesktop *virtualDesktop = nullptr;
593 queryPointer(c: connection(), virtualDesktop: &virtualDesktop, pos: nullptr);
594 if (virtualDesktop)
595 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());
596 xcb_flush(c: xcb_connection());
597}
598
599QT_END_NAMESPACE
600

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