1 /*
2 * XCB input grabber
3 * Copyright (C) 2014 Luca Barbato <lu_zero@gentoo.org>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <xcb/xcb.h>
26
27 #if CONFIG_LIBXCB_XFIXES
28 #include <xcb/xfixes.h>
29 #endif
30
31 #if CONFIG_LIBXCB_SHM
32 #include <sys/shm.h>
33 #include <xcb/shm.h>
34 #endif
35
36 #if CONFIG_LIBXCB_SHAPE
37 #include <xcb/shape.h>
38 #endif
39
42
47
50
54 #if CONFIG_LIBXCB_SHM
56 #endif
59
64
70
73
76
77 #define FOLLOW_CENTER -1
78
79 #define OFFSET(x) offsetof(XCBGrabContext, x)
80 #define D AV_OPT_FLAG_DECODING_PARAM
86 {
"video_size",
"A string describing frame size, such as 640x480 or hd720.",
OFFSET(video_size),
AV_OPT_TYPE_STRING, {.str =
"vga" }, 0, 0,
D },
89 { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
91 {
"centered",
"Keep the mouse pointer at the center of grabbing region when following.", 0,
AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX,
D,
"follow_mouse" },
92 {
"show_region",
"Show the grabbing region.",
OFFSET(show_region),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1,
D },
93 {
"region_border",
"Set the region border thickness.",
OFFSET(region_border),
AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128,
D },
95 };
96
103 };
104
106 xcb_query_pointer_reply_t *p,
107 xcb_get_geometry_reply_t *geo)
108 {
110 int x = c->
x,
y = c->
y;
112 int p_x, p_y;
113
114 if (!p || !geo)
116
117 p_x = p->win_x;
118 p_y = p->win_y;
119
121 x = p_x - w / 2;
123 } else {
124 int left = x + f;
125 int right = x + w - f;
127 int bottom =
y + h + f;
128 if (p_x > right) {
129 x += p_x - right;
130 } else if (p_x < left) {
131 x -= left - p_x;
132 }
133 if (p_y > bottom) {
135 } else if (p_y < top) {
137 }
138 }
139
142
143 return 0;
144 }
145
147 {
149 xcb_get_image_cookie_t iq;
150 xcb_get_image_reply_t *
img;
151 xcb_drawable_t drawable = c->
screen->root;
154
155 iq = xcb_get_image(c->
conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable,
157
158 img = xcb_get_image_reply(c->
conn, iq,
NULL);
159 if (!img)
161
162 data = xcb_get_image_data(img);
163 length = xcb_get_image_data_length(img);
164
166
167 if (!ret)
168 memcpy(pkt->
data, data, length);
169
170 free(img);
171
173 }
174
176 {
178 int64_t curtime, delay;
180
182
183 for (;;) {
186 if (delay <= 0)
187 break;
189 }
190
192 }
193
194 #if CONFIG_LIBXCB_SHM
195 static int check_shm(xcb_connection_t *conn)
196 {
197 xcb_shm_query_version_cookie_t cookie = xcb_shm_query_version(conn);
198 xcb_shm_query_version_reply_t *reply;
199
200 reply = xcb_shm_query_version_reply(conn, cookie,
NULL);
201 if (reply) {
202 free(reply);
203 return 1;
204 }
205
206 return 0;
207 }
208
209 static void dealloc_shm(
void *unused,
uint8_t *
data)
210 {
211 shmdt(data);
212 }
213
215 {
217 xcb_shm_get_image_cookie_t iq;
218 xcb_shm_get_image_reply_t *
img;
219 xcb_drawable_t drawable = c->
screen->root;
222 int id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
223 xcb_generic_error_t *e =
NULL;
224
225 if (id == -1) {
226 char errbuf[1024];
230 size, errbuf);
231 return err;
232 }
233
234 xcb_shm_attach(c->
conn, c->segment,
id, 0);
235
236 iq = xcb_shm_get_image(c->
conn, drawable,
238 XCB_IMAGE_FORMAT_Z_PIXMAP, c->segment, 0);
239
240 xcb_shm_detach(c->
conn, c->segment);
241
242 img = xcb_shm_get_image_reply(c->
conn, iq, &e);
243
245
246 if (e) {
248 "Cannot get the image data "
249 "event_error: response_type:%u error_code:%u "
250 "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
251 e->response_type, e->error_code,
252 e->sequence, e->resource_id, e->minor_code, e->major_code);
253
254 shmctl(id, IPC_RMID, 0);
256 }
257
258 free(img);
259
260 data = shmat(
id,
NULL, 0);
261 shmctl(id, IPC_RMID, 0);
262
263 if ((intptr_t)data == -1)
265
268 shmdt(data);
270 }
271
274
275 return 0;
276 }
277 #endif /* CONFIG_LIBXCB_SHM */
278
279 #if CONFIG_LIBXCB_XFIXES
280 static int check_xfixes(xcb_connection_t *conn)
281 {
282 xcb_xfixes_query_version_cookie_t cookie;
283 xcb_xfixes_query_version_reply_t *reply;
284
285 cookie = xcb_xfixes_query_version(conn, XCB_XFIXES_MAJOR_VERSION,
286 XCB_XFIXES_MINOR_VERSION);
287 reply = xcb_xfixes_query_version_reply(conn, cookie,
NULL);
288
289 if (reply) {
290 free(reply);
291 return 1;
292 }
293 return 0;
294 }
295
296 #define BLEND(target, source, alpha) \
297 (target) + ((source) * (255 - (alpha)) + 255 / 2) / 255
298
300 xcb_query_pointer_reply_t *p,
301 xcb_get_geometry_reply_t *geo)
302 {
304 uint32_t *cursor;
307 xcb_xfixes_get_cursor_image_cookie_t cc;
308 xcb_xfixes_get_cursor_image_reply_t *ci;
309 int cx, cy, x,
y, w, h, c_off, i_off;
310
311 cc = xcb_xfixes_get_cursor_image(gr->
conn);
312 ci = xcb_xfixes_get_cursor_image_reply(gr->
conn, cc,
NULL);
313 if (!ci)
314 return;
315
316 cursor = xcb_xfixes_get_cursor_image_cursor_image(ci);
317 if (!cursor)
318 return;
319
320 cx = ci->x - ci->xhot;
321 cy = ci->y - ci->yhot;
322
325
328
329 c_off = x - cx;
331
332 cursor += (y - cy) * ci->width;
333 image += (y - gr->
y) * gr->
width * stride;
334
335 for (y = 0; y < h; y++) {
336 cursor += c_off;
337 image += i_off * stride;
338 for (x = 0; x < w; x++, cursor++, image += stride) {
340
341 r = *cursor & 0xff;
342 g = (*cursor >> 8) & 0xff;
343 b = (*cursor >> 16) & 0xff;
344 a = (*cursor >> 24) & 0xff;
345
346 if (!a)
347 continue;
348
349 if (a == 255) {
353 } else {
354 image[0] = BLEND(r, image[0], a);
355 image[1] = BLEND(g, image[1], a);
356 image[2] = BLEND(b, image[2], a);
357 }
358
359 }
360 cursor += ci->width - w - c_off;
361 image += (gr->
width - w - i_off) * stride;
362 }
363
364 free(ci);
365 }
366 #endif /* CONFIG_LIBXCB_XFIXES */
367
369 {
373
374 xcb_configure_window(c->
conn,
376 XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
377 args);
378 }
379
381 {
383 xcb_query_pointer_cookie_t pc;
384 xcb_get_geometry_cookie_t gc;
385 xcb_query_pointer_reply_t *p =
NULL;
386 xcb_get_geometry_reply_t *geo =
NULL;
388
390
392 pc = xcb_query_pointer(c->
conn, c->
screen->root);
393 gc = xcb_get_geometry(c->
conn, c->
screen->root);
394 p = xcb_query_pointer_reply(c->
conn, pc,
NULL);
395 geo = xcb_get_geometry_reply(c->
conn, gc,
NULL);
396 }
397
400
403
404 #if CONFIG_LIBXCB_SHM
405 if (c->
has_shm && xcbgrab_frame_shm(s, pkt) < 0)
407 #endif
410
411 #if CONFIG_LIBXCB_XFIXES
413 xcbgrab_draw_mouse(s, pkt, p, geo);
414 #endif
415
416 free(p);
417 free(geo);
418
420 }
421
423 {
425
426 xcb_disconnect(ctx->
conn);
427
428 return 0;
429 }
430
431 static xcb_screen_t *
get_screen(
const xcb_setup_t *setup,
int screen_num)
432 {
433 xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
435
436 for (; it.rem > 0; xcb_screen_next (&it)) {
437 if (!screen_num) {
438 screen = it.data;
439 break;
440 }
441
442 screen_num--;
443 }
444
446 }
447
450 {
452 const xcb_setup_t *setup = xcb_get_setup(c->
conn);
453 const xcb_format_t *
fmt = xcb_setup_pixmap_formats(setup);
454 int length = xcb_setup_pixmap_formats_length(setup);
455
456 *pix_fmt = 0;
457
458 while (length--) {
459 if (fmt->depth == depth) {
460 switch (depth) {
461 case 32:
462 if (fmt->bits_per_pixel == 32)
464 break;
465 case 24:
466 if (fmt->bits_per_pixel == 32)
468 else if (fmt->bits_per_pixel == 24)
470 break;
471 case 16:
472 if (fmt->bits_per_pixel == 16)
474 break;
475 case 15:
476 if (fmt->bits_per_pixel == 16)
478 break;
479 case 8:
480 if (fmt->bits_per_pixel == 8)
482 break;
483 }
484 }
485
486 if (*pix_fmt) {
487 c->
bpp = fmt->bits_per_pixel;
489 return 0;
490 }
491
492 fmt++;
493 }
495
497 }
498
500 {
503 xcb_get_geometry_cookie_t gc;
504 xcb_get_geometry_reply_t *geo;
506
507 if (!st)
509
511 if (ret < 0)
513
515 if (ret < 0)
517
519
520 gc = xcb_get_geometry(c->
conn, c->
screen->root);
521 geo = xcb_get_geometry_reply(c->
conn, gc,
NULL);
522
528
534
536
537 free(geo);
538
540 }
541
543 {
545 xcb_gcontext_t gc = xcb_generate_id(c->
conn);
546 uint32_t
mask = XCB_GC_FOREGROUND |
547 XCB_GC_BACKGROUND |
548 XCB_GC_LINE_WIDTH |
549 XCB_GC_LINE_STYLE |
550 XCB_GC_FILL_STYLE;
551 uint32_t values[] = { c->
screen->black_pixel,
554 XCB_LINE_STYLE_DOUBLE_DASH,
555 XCB_FILL_STYLE_SOLID };
556 xcb_rectangle_t
r = { 1, 1,
559
560 xcb_create_gc(c->
conn, gc, c->
window, mask, values);
561
562 xcb_poly_rectangle(c->
conn, c->
window, gc, 1, &r);
563 }
564
566 {
568 uint32_t
mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
569 uint32_t values[] = { 1,
570 XCB_EVENT_MASK_EXPOSURE |
571 XCB_EVENT_MASK_STRUCTURE_NOTIFY };
573
575
576 xcb_create_window(c->
conn, XCB_COPY_FROM_PARENT,
583 0,
584 XCB_WINDOW_CLASS_INPUT_OUTPUT,
585 XCB_COPY_FROM_PARENT,
586 mask, values);
587
588 #if CONFIG_LIBXCB_SHAPE
589 xcb_shape_rectangles(c->
conn, XCB_SHAPE_SO_SUBTRACT,
590 XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
593 1, &rect);
594 #endif
595
597
599 }
600
602 {
605 const xcb_setup_t *setup;
607
608 if (!display_name)
610
611 if (!sscanf(s->
filename,
"%[^+]+%d,%d", display_name, &c->
x, &c->
y)) {
612 *display_name = 0;
614 }
615
616 c->
conn = xcb_connect(display_name[0] ? display_name :
NULL, &screen_num);
618
619 if ((ret = xcb_connection_has_error(c->
conn))) {
623 }
624 setup = xcb_get_setup(c->
conn);
625
629 screen_num);
632 }
633
635
636 if (ret < 0) {
639 }
640
641 #if CONFIG_LIBXCB_SHM
643 c->segment = xcb_generate_id(c->
conn);
644 #endif
645
646 #if CONFIG_LIBXCB_XFIXES
650 "XFixes not available, cannot draw the mouse.\n");
651 }
656 }
657 }
658 #endif
659
662
663 return 0;
664 }
665
674 .priv_class = &xcbgrab_class,
675 };