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

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