2 * code protected with a GNU affero GPLv3 license
3 * copyright (C) 2020 Sylvain BERTRAND
7 * "x11cursorvis" alone will turn _ON_ the cursor visibility for the lifetime
9 * "x11cursorvis WHATEVER" will turn _OFF_ x11 the cursor visibility for the
10 * lifetime of the client
12 * XXX: this code is there for cut and paste as the visibility will be reset
13 * once the client is gone
22 #include <sys/socket.h>
27 * auth : AUTHentication
30 * fd : File Descriptor
45 * sz : SiZe (usually a count of bytes)
46 * w(s) : Word(S) (32 bits)
53 #define FATAL(fmt, ...) ({fprintf(stderr, fmt, ##__VA_ARGS__); exit(EXIT_FAILURE);})
54 #define POUT(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
55 #define PERR(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
56 static u8
*so_pathname
= "/tmp/.X11-unix/X0";
59 #define PACKED __attribute__((packed))
61 #error "missing C extension for packed structure declaration"
72 struct x11_setup_status_common
{
74 u8 unused_or_reason_sz
;
77 u16 additional_data_ws_n
;
79 struct x11_query_extension_req
{
83 /*--------------------------------------------------------------------*/
88 struct x11_query_extension_rep
{
93 /*--------------------------------------------------------------------*/
100 struct x11_xfixes_query_version_req
{
104 /*--------------------------------------------------------------------*/
108 struct x11_xfixes_query_version_rep
{
113 /*--------------------------------------------------------------------*/
118 struct x11_xfixes_hide_or_show_cursor_req
{
122 /*--------------------------------------------------------------------*/
125 /* handle short write */
126 static void x11_write(void *data
, u16 sz
)
139 r
= write(so_fd
, p
, (size_t)sz
- sent_bytes_n
);
141 FATAL("error while sending %u bytes to the x11 server:%s\n", (int)((size_t)sz
- sent_bytes_n
), strerror(errno
));
143 sent_bytes_n
+= (size_t)r
;
144 if (sent_bytes_n
== (size_t)sz
)
149 /* handle short read */
150 static u16
x11_read(void *buf
, u16 max_sz
)
163 r
= read(so_fd
, p
, (size_t)max_sz
- recv_bytes_n
);
165 FATAL("error while receiving %u bytes from the x11 server:%s\n", (int)((size_t)max_sz
- recv_bytes_n
), strerror(errno
));
166 if (r
== 0) /* no more data: 0-sized datagram, connection properly closed, end of file... */
168 recv_bytes_n
+= (size_t)r
;
169 if (recv_bytes_n
== (size_t)max_sz
)
173 return (u16
)recv_bytes_n
;
175 int main(int argc
, u8
**argv
)
179 struct sockaddr_un addr
;
180 struct x11_setup x11_setup
;
181 struct x11_setup_status_common x11_status
;
183 u16 vendor_str_bytes_n
;
184 u8
*vendor_str_bytes_n_of
;
185 u32 vendor_str_pad_bytes_n
;
196 struct x11_query_extension_req
*qe_req
;
197 u16 ext_name_pad_bytes_n
;
198 struct x11_query_extension_rep qe_rep
;
199 struct x11_xfixes_query_version_req xf_qv_req
;
200 struct x11_xfixes_query_version_rep xf_qv_rep
;
201 struct x11_xfixes_hide_or_show_cursor_req xf_hos_req
;
208 /* xserver expects a SOCK_STREAM socket */
209 ri
= socket(AF_UNIX
, SOCK_STREAM
, 0);
211 FATAL("unable to create a socket:%s\n", strerror(errno
));
214 memset(&addr
, 0, sizeof(addr
));
215 addr
.sun_family
= AF_UNIX
;
216 strncpy(addr
.sun_path
, so_pathname
, sizeof(addr
.sun_path
));
217 ri
= connect(so_fd
, (struct sockaddr
*)&addr
, sizeof(addr
));
219 FATAL("unable to connect the socket %d to address '%s':%s\n", so_fd
, so_pathname
, strerror(errno
));
220 POUT("connected to unix socket '%s'\n", so_pathname
);
221 /*====================================================================*/
222 POUT("\nx11 setup -- START\n");
223 memset(&x11_setup
, 0, sizeof(x11_setup
));
224 x11_setup
.endian
= 'l'; /* l-ittle endian or 'B'-ig endian */
225 x11_setup
.major
= 11; /* wayland is x12 */
226 x11_write(&x11_setup
, sizeof(x11_setup
));
227 POUT("x11 connection setup sent\n");
228 POUT("receiving x11 setup status common data...\n");
229 r16
= x11_read(&x11_status
, sizeof(x11_status
));
230 if (r16
!= sizeof(x11_status
))
231 FATAL("unable to get x11 setup status common data\n");
232 if (x11_status
.additional_data_ws_n
!= 0) {
233 additional_data
= realloc(additional_data
, x11_status
.additional_data_ws_n
* 4);
234 if (additional_data
== 0)
235 FATAL("unable to allocate memory to x11 setup status additional data\n");
236 POUT("receiving x11 setup status additional data, %u bytes...\n", x11_status
.additional_data_ws_n
* 4);
237 r16
= x11_read(additional_data
, x11_status
.additional_data_ws_n
* 4);
238 if (r16
!= (x11_status
.additional_data_ws_n
* 4))
239 FATAL("incomplete x11 setup status additional data\n");
241 if (x11_status
.code
== 0) {
242 POUT("x11 setup: failure\n");
243 if (x11_status
.additional_data_ws_n
!= 0)
244 /* don't expect any string to end with '\0' */
245 FATAL("reason:%.*s\n", (int)x11_status
.unused_or_reason_sz
, additional_data
);
246 FATAL("no failure reason provided\n");
248 if (x11_status
.code
== 2)
249 FATAL("x11 setup: authentication is not supported\n");
250 if (x11_status
.code
!= 1)
251 FATAL("x11 setup: unknown status code (0x%02x)\n", x11_status
.code
);
252 /* x11_status.code == 1 */
253 POUT("x11 setup success\n");
254 POUT("x11 setup -- END\n");
255 /*====================================================================*/
256 POUT("\nx11 setup additional data processing -- START\n");
257 #define VENDOR_STR_OF 32
258 #define FORMAT_BYTES_N 8
259 vendor_str_bytes_n_of
= additional_data
+ 16; /* x11 specs */
260 vendor_str_bytes_n
= *(u16
*)vendor_str_bytes_n_of
;
261 vendor_str_pad_bytes_n
= vendor_str_bytes_n
% 4 ?
262 4 - (vendor_str_bytes_n
% 4) : 0;
263 POUT("vendor string size=%u bytes/%u padding bytes\n", vendor_str_bytes_n
, vendor_str_pad_bytes_n
);
264 /* don't expect any sting to end with '\0' */
265 POUT("vendor str is \"%.*s\"\n", vendor_str_bytes_n
, additional_data
+ VENDOR_STR_OF
);
266 /*--------------------------------------------------------------------*/
267 scrs_n_of
= additional_data
+ 20;
269 POUT("count of screens is %u\n", scrs_n
);
270 /*--------------------------------------------------------------------*/
271 formats_n_of
= additional_data
+ 21;
272 formats_n
= *formats_n_of
;
273 POUT("count of formats is %u\n", formats_n
);
274 /*--------------------------------------------------------------------*/
275 scrs_of
= additional_data
+ VENDOR_STR_OF
+ vendor_str_bytes_n
276 + vendor_str_pad_bytes_n
+ FORMAT_BYTES_N
* formats_n
;
277 rootwin_id
= *(u32
*)scrs_of
;
278 scr0_width_of
= (u16
*)(scrs_of
+ 20);
279 scr0_height_of
= (u16
*)(scrs_of
+ 22);
280 POUT("screen 0:root window id=0x%08x;width=%u pixels;height=%u pixels\n", rootwin_id
, *scr0_width_of
, *scr0_height_of
);
282 #undef FORMAT_BYTES_N
283 POUT("x11 setup additional data processing -- END\n");
284 /*====================================================================*/
285 POUT("\nQueryExtension request -- START\n");
286 #define STR_SZ(x) (sizeof(x)-1) /* terminate the terminating '\0' */
287 #define EXTENSION_NAME "XFIXES"
288 ext_name_pad_bytes_n
= STR_SZ(EXTENSION_NAME
) % 4 ? 4
289 - (STR_SZ(EXTENSION_NAME
) % 4) : 0;
290 req_ws_n
= 2 + (STR_SZ(EXTENSION_NAME
) + ext_name_pad_bytes_n
) / 4;
291 qe_req
= calloc(1, req_ws_n
* 4);
293 qe_req
->req_ws_n
= req_ws_n
;
294 qe_req
->name_bytes_n
= STR_SZ(EXTENSION_NAME
);
295 strncpy(qe_req
->name
, EXTENSION_NAME
, STR_SZ(EXTENSION_NAME
));
296 x11_write(qe_req
, req_ws_n
* 4);
297 POUT("QueryExtension request sent\n");
299 #undef EXTENSION_NAME
300 POUT("QueryExtension request -- END\n");
301 /*====================================================================*/
302 POUT("\nQueryExtension reply -- START\n");
303 memset(&qe_rep
, 0, sizeof(qe_rep
));
304 r16
= x11_read(&qe_rep
, sizeof(qe_rep
));
305 if (r16
!= sizeof(qe_rep
))
306 FATAL("unable to get query extension reply\n");
308 QueryExtension reply:\n\
310 sequence number = %u\n\
311 reply 32bits words count = %u\n\
316 ", qe_rep
.code
, qe_rep
.seq_nr
, qe_rep
.rep_ws_n
, qe_rep
.present
? "yes" : "no", qe_rep
.opcode_major
, qe_rep
.first_evt
, qe_rep
.first_err
);
317 POUT("QueryExtension reply -- END\n");
318 /*====================================================================*/
319 POUT("\nxfixes QueryVersion request -- START\n");
320 #define QUERY_VERSION_MINOR 0
322 memset(&xf_qv_req
, 0, sizeof(xf_qv_req
));
323 xf_qv_req
.opcode_major
= qe_rep
.opcode_major
;
324 xf_qv_req
.opcode_minor
= QUERY_VERSION_MINOR
;
325 xf_qv_req
.req_ws_n
= REQ_WS_N
;
326 xf_qv_req
.version_major
= 4;
327 xf_qv_req
.version_minor
= 0;
328 x11_write(&xf_qv_req
, REQ_WS_N
* 4);
329 POUT("xfixes QueryVersion request sent\n");
330 #undef QUERY_VERSION_MINOR
332 POUT("xfixes QueryVersion request -- END\n");
333 /*====================================================================*/
334 POUT("\nxfixes QueryVersion reply -- START\n");
335 memset(&xf_qv_rep
, 0, sizeof(xf_qv_rep
));
336 r16
= x11_read(&xf_qv_rep
, sizeof(xf_qv_rep
));
337 if (r16
!= sizeof(xf_qv_rep
))
338 FATAL("unable to get xfixes query version reply\n");
340 xfixes QueryVersion reply:\n\
342 sequence number = %u\n\
343 reply 32bits words count = %u\n\
344 major version = %u\n\
345 minor version = %u\n\
346 ", xf_qv_rep
.code
, xf_qv_rep
.seq_nr
, xf_qv_rep
.rep_ws_n
, xf_qv_rep
.version_major
, xf_qv_rep
.version_minor
);
347 POUT("xfixes QueryVersion reply -- END\n");
348 /*====================================================================*/
349 POUT("\nxfixes [Hide/Show]Cursor request -- START\n");
350 #define XFIXES_HIDE_CURSOR 29
351 #define XFIXES_SHOW_CURSOR 30
353 memset(&xf_hos_req
, 0, sizeof(xf_hos_req
));
354 xf_hos_req
.opcode_major
= qe_rep
.opcode_major
;
357 xf_hos_req
.opcode_minor
= XFIXES_HIDE_CURSOR
;
360 xf_hos_req
.opcode_minor
= XFIXES_SHOW_CURSOR
;
362 xf_hos_req
.req_ws_n
= REQ_WS_N
;
363 xf_hos_req
.win_id
= rootwin_id
;
364 x11_write(&xf_hos_req
, REQ_WS_N
* 4);
365 POUT("xfixes %sCursor request sent on the root window (id = 0x%08x)\n", show_cursor
? "Show" : "Hide", rootwin_id
);
366 #undef XFIXES_HIDE_CURSOR
367 #undef XFIXES_SHOW_CURSOR
369 POUT("xfixes [Hide/Show]Cursor request -- END\n");
370 /*====================================================================*/