1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * XCB input grabber
3cabdff1aSopenharmony_ci * Copyright (C) 2014 Luca Barbato <lu_zero@gentoo.org>
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci#include "config.h"
23cabdff1aSopenharmony_ci
24cabdff1aSopenharmony_ci#include <stdlib.h>
25cabdff1aSopenharmony_ci#include <string.h>
26cabdff1aSopenharmony_ci#include <xcb/xcb.h>
27cabdff1aSopenharmony_ci
28cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_XFIXES
29cabdff1aSopenharmony_ci#include <xcb/xfixes.h>
30cabdff1aSopenharmony_ci#endif
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
33cabdff1aSopenharmony_ci#include <sys/shm.h>
34cabdff1aSopenharmony_ci#include <xcb/shm.h>
35cabdff1aSopenharmony_ci#endif
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHAPE
38cabdff1aSopenharmony_ci#include <xcb/shape.h>
39cabdff1aSopenharmony_ci#endif
40cabdff1aSopenharmony_ci
41cabdff1aSopenharmony_ci#include "libavutil/internal.h"
42cabdff1aSopenharmony_ci#include "libavutil/mathematics.h"
43cabdff1aSopenharmony_ci#include "libavutil/opt.h"
44cabdff1aSopenharmony_ci#include "libavutil/parseutils.h"
45cabdff1aSopenharmony_ci#include "libavutil/time.h"
46cabdff1aSopenharmony_ci
47cabdff1aSopenharmony_ci#include "libavformat/avformat.h"
48cabdff1aSopenharmony_ci#include "libavformat/internal.h"
49cabdff1aSopenharmony_ci
50cabdff1aSopenharmony_citypedef struct XCBGrabContext {
51cabdff1aSopenharmony_ci    const AVClass *class;
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_ci    xcb_connection_t *conn;
54cabdff1aSopenharmony_ci    xcb_screen_t *screen;
55cabdff1aSopenharmony_ci    xcb_window_t window;
56cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
57cabdff1aSopenharmony_ci    AVBufferPool *shm_pool;
58cabdff1aSopenharmony_ci#endif
59cabdff1aSopenharmony_ci    int64_t time_frame;
60cabdff1aSopenharmony_ci    AVRational time_base;
61cabdff1aSopenharmony_ci    int64_t frame_duration;
62cabdff1aSopenharmony_ci
63cabdff1aSopenharmony_ci    xcb_window_t window_id;
64cabdff1aSopenharmony_ci    int x, y;
65cabdff1aSopenharmony_ci    int width, height;
66cabdff1aSopenharmony_ci    int frame_size;
67cabdff1aSopenharmony_ci    int bpp;
68cabdff1aSopenharmony_ci
69cabdff1aSopenharmony_ci    int draw_mouse;
70cabdff1aSopenharmony_ci    int follow_mouse;
71cabdff1aSopenharmony_ci    int show_region;
72cabdff1aSopenharmony_ci    int region_border;
73cabdff1aSopenharmony_ci    int centered;
74cabdff1aSopenharmony_ci    int select_region;
75cabdff1aSopenharmony_ci
76cabdff1aSopenharmony_ci    const char *framerate;
77cabdff1aSopenharmony_ci
78cabdff1aSopenharmony_ci    int has_shm;
79cabdff1aSopenharmony_ci} XCBGrabContext;
80cabdff1aSopenharmony_ci
81cabdff1aSopenharmony_ci#define FOLLOW_CENTER -1
82cabdff1aSopenharmony_ci
83cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(XCBGrabContext, x)
84cabdff1aSopenharmony_ci#define D AV_OPT_FLAG_DECODING_PARAM
85cabdff1aSopenharmony_cistatic const AVOption options[] = {
86cabdff1aSopenharmony_ci    { "window_id", "Window to capture.", OFFSET(window_id), AV_OPT_TYPE_INT, { .i64 = XCB_NONE }, 0, UINT32_MAX, D },
87cabdff1aSopenharmony_ci    { "x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
88cabdff1aSopenharmony_ci    { "y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
89cabdff1aSopenharmony_ci    { "grab_x", "Initial x coordinate.", OFFSET(x), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
90cabdff1aSopenharmony_ci    { "grab_y", "Initial y coordinate.", OFFSET(y), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, D },
91cabdff1aSopenharmony_ci    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL }, 0, 0, D },
92cabdff1aSopenharmony_ci    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc" }, 0, 0, D },
93cabdff1aSopenharmony_ci    { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, D },
94cabdff1aSopenharmony_ci    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
95cabdff1aSopenharmony_ci      OFFSET(follow_mouse), AV_OPT_TYPE_INT, { .i64 = 0 },  FOLLOW_CENTER, INT_MAX, D, "follow_mouse" },
96cabdff1aSopenharmony_ci    { "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" },
97cabdff1aSopenharmony_ci    { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D },
98cabdff1aSopenharmony_ci    { "region_border", "Set the region border thickness.", OFFSET(region_border), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128, D },
99cabdff1aSopenharmony_ci    { "select_region", "Select the grabbing region graphically using the pointer.", OFFSET(select_region), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D },
100cabdff1aSopenharmony_ci    { NULL },
101cabdff1aSopenharmony_ci};
102cabdff1aSopenharmony_ci
103cabdff1aSopenharmony_cistatic const AVClass xcbgrab_class = {
104cabdff1aSopenharmony_ci    .class_name = "xcbgrab indev",
105cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
106cabdff1aSopenharmony_ci    .option     = options,
107cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
108cabdff1aSopenharmony_ci    .category   = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
109cabdff1aSopenharmony_ci};
110cabdff1aSopenharmony_ci
111cabdff1aSopenharmony_cistatic int xcbgrab_reposition(AVFormatContext *s,
112cabdff1aSopenharmony_ci                              xcb_query_pointer_reply_t *p,
113cabdff1aSopenharmony_ci                              xcb_get_geometry_reply_t *geo)
114cabdff1aSopenharmony_ci{
115cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
116cabdff1aSopenharmony_ci    int x = c->x, y = c->y;
117cabdff1aSopenharmony_ci    int w = c->width, h = c->height, f = c->follow_mouse;
118cabdff1aSopenharmony_ci    int p_x, p_y;
119cabdff1aSopenharmony_ci
120cabdff1aSopenharmony_ci    if (!p || !geo)
121cabdff1aSopenharmony_ci        return AVERROR(EIO);
122cabdff1aSopenharmony_ci
123cabdff1aSopenharmony_ci    p_x = p->win_x;
124cabdff1aSopenharmony_ci    p_y = p->win_y;
125cabdff1aSopenharmony_ci
126cabdff1aSopenharmony_ci    if (f == FOLLOW_CENTER) {
127cabdff1aSopenharmony_ci        x = p_x - w / 2;
128cabdff1aSopenharmony_ci        y = p_y - h / 2;
129cabdff1aSopenharmony_ci    } else {
130cabdff1aSopenharmony_ci        int left   = x + f;
131cabdff1aSopenharmony_ci        int right  = x + w - f;
132cabdff1aSopenharmony_ci        int top    = y + f;
133cabdff1aSopenharmony_ci        int bottom = y + h - f;
134cabdff1aSopenharmony_ci        if (p_x > right) {
135cabdff1aSopenharmony_ci            x += p_x - right;
136cabdff1aSopenharmony_ci        } else if (p_x < left) {
137cabdff1aSopenharmony_ci            x -= left - p_x;
138cabdff1aSopenharmony_ci        }
139cabdff1aSopenharmony_ci        if (p_y > bottom) {
140cabdff1aSopenharmony_ci            y += p_y - bottom;
141cabdff1aSopenharmony_ci        } else if (p_y < top) {
142cabdff1aSopenharmony_ci            y -= top - p_y;
143cabdff1aSopenharmony_ci        }
144cabdff1aSopenharmony_ci    }
145cabdff1aSopenharmony_ci
146cabdff1aSopenharmony_ci    c->x = FFMIN(FFMAX(0, x), geo->width  - w);
147cabdff1aSopenharmony_ci    c->y = FFMIN(FFMAX(0, y), geo->height - h);
148cabdff1aSopenharmony_ci
149cabdff1aSopenharmony_ci    return 0;
150cabdff1aSopenharmony_ci}
151cabdff1aSopenharmony_ci
152cabdff1aSopenharmony_cistatic void xcbgrab_image_reply_free(void *opaque, uint8_t *data)
153cabdff1aSopenharmony_ci{
154cabdff1aSopenharmony_ci    free(opaque);
155cabdff1aSopenharmony_ci}
156cabdff1aSopenharmony_ci
157cabdff1aSopenharmony_cistatic int xcbgrab_frame(AVFormatContext *s, AVPacket *pkt)
158cabdff1aSopenharmony_ci{
159cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
160cabdff1aSopenharmony_ci    xcb_get_image_cookie_t iq;
161cabdff1aSopenharmony_ci    xcb_get_image_reply_t *img;
162cabdff1aSopenharmony_ci    xcb_drawable_t drawable = c->window_id;
163cabdff1aSopenharmony_ci    xcb_generic_error_t *e = NULL;
164cabdff1aSopenharmony_ci    uint8_t *data;
165cabdff1aSopenharmony_ci    int length;
166cabdff1aSopenharmony_ci
167cabdff1aSopenharmony_ci    iq  = xcb_get_image(c->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, drawable,
168cabdff1aSopenharmony_ci                        c->x, c->y, c->width, c->height, ~0);
169cabdff1aSopenharmony_ci
170cabdff1aSopenharmony_ci    img = xcb_get_image_reply(c->conn, iq, &e);
171cabdff1aSopenharmony_ci
172cabdff1aSopenharmony_ci    if (e) {
173cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
174cabdff1aSopenharmony_ci               "Cannot get the image data "
175cabdff1aSopenharmony_ci               "event_error: response_type:%u error_code:%u "
176cabdff1aSopenharmony_ci               "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
177cabdff1aSopenharmony_ci               e->response_type, e->error_code,
178cabdff1aSopenharmony_ci               e->sequence, e->resource_id, e->minor_code, e->major_code);
179cabdff1aSopenharmony_ci        free(e);
180cabdff1aSopenharmony_ci        return AVERROR(EACCES);
181cabdff1aSopenharmony_ci    }
182cabdff1aSopenharmony_ci
183cabdff1aSopenharmony_ci    if (!img)
184cabdff1aSopenharmony_ci        return AVERROR(EAGAIN);
185cabdff1aSopenharmony_ci
186cabdff1aSopenharmony_ci    data   = xcb_get_image_data(img);
187cabdff1aSopenharmony_ci    length = xcb_get_image_data_length(img);
188cabdff1aSopenharmony_ci
189cabdff1aSopenharmony_ci    pkt->buf = av_buffer_create(data, length, xcbgrab_image_reply_free, img, 0);
190cabdff1aSopenharmony_ci    if (!pkt->buf) {
191cabdff1aSopenharmony_ci        free(img);
192cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
193cabdff1aSopenharmony_ci    }
194cabdff1aSopenharmony_ci
195cabdff1aSopenharmony_ci    pkt->data = data;
196cabdff1aSopenharmony_ci    pkt->size = length;
197cabdff1aSopenharmony_ci
198cabdff1aSopenharmony_ci    return 0;
199cabdff1aSopenharmony_ci}
200cabdff1aSopenharmony_ci
201cabdff1aSopenharmony_cistatic int64_t wait_frame(AVFormatContext *s, AVPacket *pkt)
202cabdff1aSopenharmony_ci{
203cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
204cabdff1aSopenharmony_ci    int64_t curtime, delay;
205cabdff1aSopenharmony_ci
206cabdff1aSopenharmony_ci    c->time_frame += c->frame_duration;
207cabdff1aSopenharmony_ci
208cabdff1aSopenharmony_ci    for (;;) {
209cabdff1aSopenharmony_ci        curtime = av_gettime_relative();
210cabdff1aSopenharmony_ci        delay   = c->time_frame - curtime;
211cabdff1aSopenharmony_ci        if (delay <= 0)
212cabdff1aSopenharmony_ci            break;
213cabdff1aSopenharmony_ci        av_usleep(delay);
214cabdff1aSopenharmony_ci    }
215cabdff1aSopenharmony_ci
216cabdff1aSopenharmony_ci    return curtime;
217cabdff1aSopenharmony_ci}
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
220cabdff1aSopenharmony_cistatic int check_shm(xcb_connection_t *conn)
221cabdff1aSopenharmony_ci{
222cabdff1aSopenharmony_ci    xcb_shm_query_version_cookie_t cookie = xcb_shm_query_version(conn);
223cabdff1aSopenharmony_ci    xcb_shm_query_version_reply_t *reply;
224cabdff1aSopenharmony_ci
225cabdff1aSopenharmony_ci    reply = xcb_shm_query_version_reply(conn, cookie, NULL);
226cabdff1aSopenharmony_ci    if (reply) {
227cabdff1aSopenharmony_ci        free(reply);
228cabdff1aSopenharmony_ci        return 1;
229cabdff1aSopenharmony_ci    }
230cabdff1aSopenharmony_ci
231cabdff1aSopenharmony_ci    return 0;
232cabdff1aSopenharmony_ci}
233cabdff1aSopenharmony_ci
234cabdff1aSopenharmony_cistatic void free_shm_buffer(void *opaque, uint8_t *data)
235cabdff1aSopenharmony_ci{
236cabdff1aSopenharmony_ci    shmdt(data);
237cabdff1aSopenharmony_ci}
238cabdff1aSopenharmony_ci
239cabdff1aSopenharmony_cistatic AVBufferRef *allocate_shm_buffer(void *opaque, size_t size)
240cabdff1aSopenharmony_ci{
241cabdff1aSopenharmony_ci    xcb_connection_t *conn = opaque;
242cabdff1aSopenharmony_ci    xcb_shm_seg_t segment;
243cabdff1aSopenharmony_ci    AVBufferRef *ref;
244cabdff1aSopenharmony_ci    uint8_t *data;
245cabdff1aSopenharmony_ci    int id;
246cabdff1aSopenharmony_ci
247cabdff1aSopenharmony_ci    id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
248cabdff1aSopenharmony_ci    if (id == -1)
249cabdff1aSopenharmony_ci        return NULL;
250cabdff1aSopenharmony_ci
251cabdff1aSopenharmony_ci    segment = xcb_generate_id(conn);
252cabdff1aSopenharmony_ci    xcb_shm_attach(conn, segment, id, 0);
253cabdff1aSopenharmony_ci    data = shmat(id, NULL, 0);
254cabdff1aSopenharmony_ci    shmctl(id, IPC_RMID, 0);
255cabdff1aSopenharmony_ci    if ((intptr_t)data == -1 || !data)
256cabdff1aSopenharmony_ci        return NULL;
257cabdff1aSopenharmony_ci
258cabdff1aSopenharmony_ci    ref = av_buffer_create(data, size, free_shm_buffer, (void *)(ptrdiff_t)segment, 0);
259cabdff1aSopenharmony_ci    if (!ref)
260cabdff1aSopenharmony_ci        shmdt(data);
261cabdff1aSopenharmony_ci
262cabdff1aSopenharmony_ci    return ref;
263cabdff1aSopenharmony_ci}
264cabdff1aSopenharmony_ci
265cabdff1aSopenharmony_cistatic int xcbgrab_frame_shm(AVFormatContext *s, AVPacket *pkt)
266cabdff1aSopenharmony_ci{
267cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
268cabdff1aSopenharmony_ci    xcb_shm_get_image_cookie_t iq;
269cabdff1aSopenharmony_ci    xcb_shm_get_image_reply_t *img;
270cabdff1aSopenharmony_ci    xcb_drawable_t drawable = c->window_id;
271cabdff1aSopenharmony_ci    xcb_generic_error_t *e = NULL;
272cabdff1aSopenharmony_ci    AVBufferRef *buf;
273cabdff1aSopenharmony_ci    xcb_shm_seg_t segment;
274cabdff1aSopenharmony_ci
275cabdff1aSopenharmony_ci    buf = av_buffer_pool_get(c->shm_pool);
276cabdff1aSopenharmony_ci    if (!buf) {
277cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Could not get shared memory buffer.\n");
278cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
279cabdff1aSopenharmony_ci    }
280cabdff1aSopenharmony_ci    segment = (xcb_shm_seg_t)(uintptr_t)av_buffer_pool_buffer_get_opaque(buf);
281cabdff1aSopenharmony_ci
282cabdff1aSopenharmony_ci    iq = xcb_shm_get_image(c->conn, drawable,
283cabdff1aSopenharmony_ci                           c->x, c->y, c->width, c->height, ~0,
284cabdff1aSopenharmony_ci                           XCB_IMAGE_FORMAT_Z_PIXMAP, segment, 0);
285cabdff1aSopenharmony_ci    img = xcb_shm_get_image_reply(c->conn, iq, &e);
286cabdff1aSopenharmony_ci
287cabdff1aSopenharmony_ci    xcb_flush(c->conn);
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    if (e) {
290cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
291cabdff1aSopenharmony_ci               "Cannot get the image data "
292cabdff1aSopenharmony_ci               "event_error: response_type:%u error_code:%u "
293cabdff1aSopenharmony_ci               "sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
294cabdff1aSopenharmony_ci               e->response_type, e->error_code,
295cabdff1aSopenharmony_ci               e->sequence, e->resource_id, e->minor_code, e->major_code);
296cabdff1aSopenharmony_ci
297cabdff1aSopenharmony_ci        free(e);
298cabdff1aSopenharmony_ci        av_buffer_unref(&buf);
299cabdff1aSopenharmony_ci        return AVERROR(EACCES);
300cabdff1aSopenharmony_ci    }
301cabdff1aSopenharmony_ci
302cabdff1aSopenharmony_ci    free(img);
303cabdff1aSopenharmony_ci
304cabdff1aSopenharmony_ci    pkt->buf = buf;
305cabdff1aSopenharmony_ci    pkt->data = buf->data;
306cabdff1aSopenharmony_ci    pkt->size = c->frame_size;
307cabdff1aSopenharmony_ci
308cabdff1aSopenharmony_ci    return 0;
309cabdff1aSopenharmony_ci}
310cabdff1aSopenharmony_ci#endif /* CONFIG_LIBXCB_SHM */
311cabdff1aSopenharmony_ci
312cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_XFIXES
313cabdff1aSopenharmony_cistatic int check_xfixes(xcb_connection_t *conn)
314cabdff1aSopenharmony_ci{
315cabdff1aSopenharmony_ci    xcb_xfixes_query_version_cookie_t cookie;
316cabdff1aSopenharmony_ci    xcb_xfixes_query_version_reply_t *reply;
317cabdff1aSopenharmony_ci
318cabdff1aSopenharmony_ci    cookie = xcb_xfixes_query_version(conn, XCB_XFIXES_MAJOR_VERSION,
319cabdff1aSopenharmony_ci                                      XCB_XFIXES_MINOR_VERSION);
320cabdff1aSopenharmony_ci    reply  = xcb_xfixes_query_version_reply(conn, cookie, NULL);
321cabdff1aSopenharmony_ci
322cabdff1aSopenharmony_ci    if (reply) {
323cabdff1aSopenharmony_ci        free(reply);
324cabdff1aSopenharmony_ci        return 1;
325cabdff1aSopenharmony_ci    }
326cabdff1aSopenharmony_ci    return 0;
327cabdff1aSopenharmony_ci}
328cabdff1aSopenharmony_ci
329cabdff1aSopenharmony_ci#define BLEND(target, source, alpha) \
330cabdff1aSopenharmony_ci    (target) + ((source) * (255 - (alpha)) + 255 / 2) / 255
331cabdff1aSopenharmony_ci
332cabdff1aSopenharmony_cistatic void xcbgrab_draw_mouse(AVFormatContext *s, AVPacket *pkt,
333cabdff1aSopenharmony_ci                               xcb_query_pointer_reply_t *p,
334cabdff1aSopenharmony_ci                               xcb_get_geometry_reply_t *geo,
335cabdff1aSopenharmony_ci                               int win_x, int win_y)
336cabdff1aSopenharmony_ci{
337cabdff1aSopenharmony_ci    XCBGrabContext *gr = s->priv_data;
338cabdff1aSopenharmony_ci    uint32_t *cursor;
339cabdff1aSopenharmony_ci    uint8_t *image = pkt->data;
340cabdff1aSopenharmony_ci    int stride     = gr->bpp / 8;
341cabdff1aSopenharmony_ci    xcb_xfixes_get_cursor_image_cookie_t cc;
342cabdff1aSopenharmony_ci    xcb_xfixes_get_cursor_image_reply_t *ci;
343cabdff1aSopenharmony_ci    int cx, cy, x, y, w, h, c_off, i_off;
344cabdff1aSopenharmony_ci
345cabdff1aSopenharmony_ci    cc = xcb_xfixes_get_cursor_image(gr->conn);
346cabdff1aSopenharmony_ci    ci = xcb_xfixes_get_cursor_image_reply(gr->conn, cc, NULL);
347cabdff1aSopenharmony_ci    if (!ci)
348cabdff1aSopenharmony_ci        return;
349cabdff1aSopenharmony_ci
350cabdff1aSopenharmony_ci    cursor = xcb_xfixes_get_cursor_image_cursor_image(ci);
351cabdff1aSopenharmony_ci    if (!cursor)
352cabdff1aSopenharmony_ci        return;
353cabdff1aSopenharmony_ci
354cabdff1aSopenharmony_ci    cx = ci->x - ci->xhot;
355cabdff1aSopenharmony_ci    cy = ci->y - ci->yhot;
356cabdff1aSopenharmony_ci
357cabdff1aSopenharmony_ci    x = FFMAX(cx, win_x + gr->x);
358cabdff1aSopenharmony_ci    y = FFMAX(cy, win_y + gr->y);
359cabdff1aSopenharmony_ci
360cabdff1aSopenharmony_ci    w = FFMIN(cx + ci->width,  win_x + gr->x + gr->width)  - x;
361cabdff1aSopenharmony_ci    h = FFMIN(cy + ci->height, win_y + gr->y + gr->height) - y;
362cabdff1aSopenharmony_ci
363cabdff1aSopenharmony_ci    c_off = x - cx;
364cabdff1aSopenharmony_ci    i_off = x - gr->x - win_x;
365cabdff1aSopenharmony_ci
366cabdff1aSopenharmony_ci    cursor += (y - cy) * ci->width;
367cabdff1aSopenharmony_ci    image  += (y - gr->y - win_y) * gr->width * stride;
368cabdff1aSopenharmony_ci
369cabdff1aSopenharmony_ci    for (y = 0; y < h; y++) {
370cabdff1aSopenharmony_ci        cursor += c_off;
371cabdff1aSopenharmony_ci        image  += i_off * stride;
372cabdff1aSopenharmony_ci        for (x = 0; x < w; x++, cursor++, image += stride) {
373cabdff1aSopenharmony_ci            int r, g, b, a;
374cabdff1aSopenharmony_ci
375cabdff1aSopenharmony_ci            r =  *cursor        & 0xff;
376cabdff1aSopenharmony_ci            g = (*cursor >>  8) & 0xff;
377cabdff1aSopenharmony_ci            b = (*cursor >> 16) & 0xff;
378cabdff1aSopenharmony_ci            a = (*cursor >> 24) & 0xff;
379cabdff1aSopenharmony_ci
380cabdff1aSopenharmony_ci            if (!a)
381cabdff1aSopenharmony_ci                continue;
382cabdff1aSopenharmony_ci
383cabdff1aSopenharmony_ci            if (a == 255) {
384cabdff1aSopenharmony_ci                image[0] = r;
385cabdff1aSopenharmony_ci                image[1] = g;
386cabdff1aSopenharmony_ci                image[2] = b;
387cabdff1aSopenharmony_ci            } else {
388cabdff1aSopenharmony_ci                image[0] = BLEND(r, image[0], a);
389cabdff1aSopenharmony_ci                image[1] = BLEND(g, image[1], a);
390cabdff1aSopenharmony_ci                image[2] = BLEND(b, image[2], a);
391cabdff1aSopenharmony_ci            }
392cabdff1aSopenharmony_ci
393cabdff1aSopenharmony_ci        }
394cabdff1aSopenharmony_ci        cursor +=  ci->width - w - c_off;
395cabdff1aSopenharmony_ci        image  += (gr->width - w - i_off) * stride;
396cabdff1aSopenharmony_ci    }
397cabdff1aSopenharmony_ci
398cabdff1aSopenharmony_ci    free(ci);
399cabdff1aSopenharmony_ci}
400cabdff1aSopenharmony_ci#endif /* CONFIG_LIBXCB_XFIXES */
401cabdff1aSopenharmony_ci
402cabdff1aSopenharmony_cistatic void xcbgrab_update_region(AVFormatContext *s, int win_x, int win_y)
403cabdff1aSopenharmony_ci{
404cabdff1aSopenharmony_ci    XCBGrabContext *c     = s->priv_data;
405cabdff1aSopenharmony_ci    const uint32_t args[] = { win_x + c->x - c->region_border,
406cabdff1aSopenharmony_ci                              win_y + c->y - c->region_border };
407cabdff1aSopenharmony_ci
408cabdff1aSopenharmony_ci    xcb_configure_window(c->conn,
409cabdff1aSopenharmony_ci                         c->window,
410cabdff1aSopenharmony_ci                         XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y,
411cabdff1aSopenharmony_ci                         args);
412cabdff1aSopenharmony_ci}
413cabdff1aSopenharmony_ci
414cabdff1aSopenharmony_cistatic int xcbgrab_read_packet(AVFormatContext *s, AVPacket *pkt)
415cabdff1aSopenharmony_ci{
416cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
417cabdff1aSopenharmony_ci    xcb_query_pointer_cookie_t pc;
418cabdff1aSopenharmony_ci    xcb_get_geometry_cookie_t gc;
419cabdff1aSopenharmony_ci    xcb_translate_coordinates_cookie_t tc;
420cabdff1aSopenharmony_ci    xcb_query_pointer_reply_t *p  = NULL;
421cabdff1aSopenharmony_ci    xcb_get_geometry_reply_t *geo = NULL;
422cabdff1aSopenharmony_ci    xcb_translate_coordinates_reply_t *translate = NULL;
423cabdff1aSopenharmony_ci    int ret = 0;
424cabdff1aSopenharmony_ci    int64_t pts;
425cabdff1aSopenharmony_ci    int win_x = 0, win_y = 0;
426cabdff1aSopenharmony_ci
427cabdff1aSopenharmony_ci    wait_frame(s, pkt);
428cabdff1aSopenharmony_ci    pts = av_gettime();
429cabdff1aSopenharmony_ci
430cabdff1aSopenharmony_ci    if (c->follow_mouse || c->draw_mouse) {
431cabdff1aSopenharmony_ci        pc  = xcb_query_pointer(c->conn, c->window_id);
432cabdff1aSopenharmony_ci        gc  = xcb_get_geometry(c->conn, c->window_id);
433cabdff1aSopenharmony_ci        p   = xcb_query_pointer_reply(c->conn, pc, NULL);
434cabdff1aSopenharmony_ci        if (!p) {
435cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Failed to query xcb pointer\n");
436cabdff1aSopenharmony_ci            return AVERROR_EXTERNAL;
437cabdff1aSopenharmony_ci        }
438cabdff1aSopenharmony_ci        geo = xcb_get_geometry_reply(c->conn, gc, NULL);
439cabdff1aSopenharmony_ci        if (!geo) {
440cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Failed to get xcb geometry\n");
441cabdff1aSopenharmony_ci            free(p);
442cabdff1aSopenharmony_ci            return AVERROR_EXTERNAL;
443cabdff1aSopenharmony_ci        }
444cabdff1aSopenharmony_ci    }
445cabdff1aSopenharmony_ci    if (c->window_id != c->screen->root) {
446cabdff1aSopenharmony_ci        tc = xcb_translate_coordinates(c->conn, c->window_id, c->screen->root, 0, 0);
447cabdff1aSopenharmony_ci        translate = xcb_translate_coordinates_reply(c->conn, tc, NULL);
448cabdff1aSopenharmony_ci        if (!translate) {
449cabdff1aSopenharmony_ci            free(p);
450cabdff1aSopenharmony_ci            free(geo);
451cabdff1aSopenharmony_ci            av_log(s, AV_LOG_ERROR, "Failed to translate xcb geometry\n");
452cabdff1aSopenharmony_ci            return AVERROR_EXTERNAL;
453cabdff1aSopenharmony_ci        }
454cabdff1aSopenharmony_ci        win_x = translate->dst_x;
455cabdff1aSopenharmony_ci        win_y = translate->dst_y;
456cabdff1aSopenharmony_ci        free(translate);
457cabdff1aSopenharmony_ci    }
458cabdff1aSopenharmony_ci
459cabdff1aSopenharmony_ci    if (c->follow_mouse && p->same_screen)
460cabdff1aSopenharmony_ci        xcbgrab_reposition(s, p, geo);
461cabdff1aSopenharmony_ci
462cabdff1aSopenharmony_ci    if (c->show_region)
463cabdff1aSopenharmony_ci        xcbgrab_update_region(s, win_x, win_y);
464cabdff1aSopenharmony_ci
465cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
466cabdff1aSopenharmony_ci    if (c->has_shm && xcbgrab_frame_shm(s, pkt) < 0) {
467cabdff1aSopenharmony_ci        av_log(s, AV_LOG_WARNING, "Continuing without shared memory.\n");
468cabdff1aSopenharmony_ci        c->has_shm = 0;
469cabdff1aSopenharmony_ci    }
470cabdff1aSopenharmony_ci#endif
471cabdff1aSopenharmony_ci    if (!c->has_shm)
472cabdff1aSopenharmony_ci        ret = xcbgrab_frame(s, pkt);
473cabdff1aSopenharmony_ci    pkt->dts = pkt->pts = pts;
474cabdff1aSopenharmony_ci    pkt->duration = c->frame_duration;
475cabdff1aSopenharmony_ci
476cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_XFIXES
477cabdff1aSopenharmony_ci    if (ret >= 0 && c->draw_mouse && p->same_screen)
478cabdff1aSopenharmony_ci        xcbgrab_draw_mouse(s, pkt, p, geo, win_x, win_y);
479cabdff1aSopenharmony_ci#endif
480cabdff1aSopenharmony_ci
481cabdff1aSopenharmony_ci    free(p);
482cabdff1aSopenharmony_ci    free(geo);
483cabdff1aSopenharmony_ci
484cabdff1aSopenharmony_ci    return ret;
485cabdff1aSopenharmony_ci}
486cabdff1aSopenharmony_ci
487cabdff1aSopenharmony_cistatic av_cold int xcbgrab_read_close(AVFormatContext *s)
488cabdff1aSopenharmony_ci{
489cabdff1aSopenharmony_ci    XCBGrabContext *ctx = s->priv_data;
490cabdff1aSopenharmony_ci
491cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
492cabdff1aSopenharmony_ci    av_buffer_pool_uninit(&ctx->shm_pool);
493cabdff1aSopenharmony_ci#endif
494cabdff1aSopenharmony_ci
495cabdff1aSopenharmony_ci    xcb_disconnect(ctx->conn);
496cabdff1aSopenharmony_ci
497cabdff1aSopenharmony_ci    return 0;
498cabdff1aSopenharmony_ci}
499cabdff1aSopenharmony_ci
500cabdff1aSopenharmony_cistatic xcb_screen_t *get_screen(const xcb_setup_t *setup, int screen_num)
501cabdff1aSopenharmony_ci{
502cabdff1aSopenharmony_ci    xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
503cabdff1aSopenharmony_ci    xcb_screen_t *screen     = NULL;
504cabdff1aSopenharmony_ci
505cabdff1aSopenharmony_ci    for (; it.rem > 0; xcb_screen_next (&it)) {
506cabdff1aSopenharmony_ci        if (!screen_num) {
507cabdff1aSopenharmony_ci            screen = it.data;
508cabdff1aSopenharmony_ci            break;
509cabdff1aSopenharmony_ci        }
510cabdff1aSopenharmony_ci
511cabdff1aSopenharmony_ci        screen_num--;
512cabdff1aSopenharmony_ci    }
513cabdff1aSopenharmony_ci
514cabdff1aSopenharmony_ci    return screen;
515cabdff1aSopenharmony_ci}
516cabdff1aSopenharmony_ci
517cabdff1aSopenharmony_cistatic int pixfmt_from_pixmap_format(AVFormatContext *s, int depth,
518cabdff1aSopenharmony_ci                                     int *pix_fmt, int *bpp)
519cabdff1aSopenharmony_ci{
520cabdff1aSopenharmony_ci    XCBGrabContext *c        = s->priv_data;
521cabdff1aSopenharmony_ci    const xcb_setup_t *setup = xcb_get_setup(c->conn);
522cabdff1aSopenharmony_ci    const xcb_format_t *fmt  = xcb_setup_pixmap_formats(setup);
523cabdff1aSopenharmony_ci    int length               = xcb_setup_pixmap_formats_length(setup);
524cabdff1aSopenharmony_ci
525cabdff1aSopenharmony_ci    *pix_fmt = 0;
526cabdff1aSopenharmony_ci
527cabdff1aSopenharmony_ci    while (length--) {
528cabdff1aSopenharmony_ci        if (fmt->depth == depth) {
529cabdff1aSopenharmony_ci            switch (depth) {
530cabdff1aSopenharmony_ci            case 32:
531cabdff1aSopenharmony_ci                if (fmt->bits_per_pixel == 32)
532cabdff1aSopenharmony_ci                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
533cabdff1aSopenharmony_ci                               AV_PIX_FMT_BGR0 : AV_PIX_FMT_0RGB;
534cabdff1aSopenharmony_ci                break;
535cabdff1aSopenharmony_ci            case 24:
536cabdff1aSopenharmony_ci                if (fmt->bits_per_pixel == 32)
537cabdff1aSopenharmony_ci                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
538cabdff1aSopenharmony_ci                               AV_PIX_FMT_BGR0 : AV_PIX_FMT_0RGB;
539cabdff1aSopenharmony_ci                else if (fmt->bits_per_pixel == 24)
540cabdff1aSopenharmony_ci                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
541cabdff1aSopenharmony_ci                               AV_PIX_FMT_BGR24 : AV_PIX_FMT_RGB24;
542cabdff1aSopenharmony_ci                break;
543cabdff1aSopenharmony_ci            case 16:
544cabdff1aSopenharmony_ci                if (fmt->bits_per_pixel == 16)
545cabdff1aSopenharmony_ci                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
546cabdff1aSopenharmony_ci                               AV_PIX_FMT_RGB565LE : AV_PIX_FMT_RGB565BE;
547cabdff1aSopenharmony_ci                break;
548cabdff1aSopenharmony_ci            case 15:
549cabdff1aSopenharmony_ci                if (fmt->bits_per_pixel == 16)
550cabdff1aSopenharmony_ci                    *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
551cabdff1aSopenharmony_ci                               AV_PIX_FMT_RGB555LE : AV_PIX_FMT_RGB555BE;
552cabdff1aSopenharmony_ci                break;
553cabdff1aSopenharmony_ci            case 8:
554cabdff1aSopenharmony_ci                if (fmt->bits_per_pixel == 8)
555cabdff1aSopenharmony_ci                    *pix_fmt = AV_PIX_FMT_RGB8;
556cabdff1aSopenharmony_ci                break;
557cabdff1aSopenharmony_ci            }
558cabdff1aSopenharmony_ci        }
559cabdff1aSopenharmony_ci
560cabdff1aSopenharmony_ci        if (*pix_fmt) {
561cabdff1aSopenharmony_ci            *bpp        = fmt->bits_per_pixel;
562cabdff1aSopenharmony_ci            return 0;
563cabdff1aSopenharmony_ci        }
564cabdff1aSopenharmony_ci
565cabdff1aSopenharmony_ci        fmt++;
566cabdff1aSopenharmony_ci    }
567cabdff1aSopenharmony_ci    avpriv_report_missing_feature(s, "Mapping this pixmap format");
568cabdff1aSopenharmony_ci
569cabdff1aSopenharmony_ci    return AVERROR_PATCHWELCOME;
570cabdff1aSopenharmony_ci}
571cabdff1aSopenharmony_ci
572cabdff1aSopenharmony_cistatic int create_stream(AVFormatContext *s)
573cabdff1aSopenharmony_ci{
574cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
575cabdff1aSopenharmony_ci    AVStream *st      = avformat_new_stream(s, NULL);
576cabdff1aSopenharmony_ci    xcb_get_geometry_cookie_t gc;
577cabdff1aSopenharmony_ci    xcb_get_geometry_reply_t *geo;
578cabdff1aSopenharmony_ci    int64_t frame_size_bits;
579cabdff1aSopenharmony_ci    int ret;
580cabdff1aSopenharmony_ci
581cabdff1aSopenharmony_ci    if (!st)
582cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
583cabdff1aSopenharmony_ci
584cabdff1aSopenharmony_ci    ret = av_parse_video_rate(&st->avg_frame_rate, c->framerate);
585cabdff1aSopenharmony_ci    if (ret < 0)
586cabdff1aSopenharmony_ci        return ret;
587cabdff1aSopenharmony_ci
588cabdff1aSopenharmony_ci    avpriv_set_pts_info(st, 64, 1, 1000000);
589cabdff1aSopenharmony_ci
590cabdff1aSopenharmony_ci    gc  = xcb_get_geometry(c->conn, c->window_id);
591cabdff1aSopenharmony_ci    geo = xcb_get_geometry_reply(c->conn, gc, NULL);
592cabdff1aSopenharmony_ci    if (!geo) {
593cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Can't find window '0x%x', aborting.\n", c->window_id);
594cabdff1aSopenharmony_ci        return AVERROR_EXTERNAL;
595cabdff1aSopenharmony_ci    }
596cabdff1aSopenharmony_ci
597cabdff1aSopenharmony_ci    if (!c->width || !c->height) {
598cabdff1aSopenharmony_ci        c->width = geo->width;
599cabdff1aSopenharmony_ci        c->height = geo->height;
600cabdff1aSopenharmony_ci    }
601cabdff1aSopenharmony_ci
602cabdff1aSopenharmony_ci    if (c->x + c->width > geo->width ||
603cabdff1aSopenharmony_ci        c->y + c->height > geo->height) {
604cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
605cabdff1aSopenharmony_ci               "Capture area %dx%d at position %d.%d "
606cabdff1aSopenharmony_ci               "outside the screen size %dx%d\n",
607cabdff1aSopenharmony_ci               c->width, c->height,
608cabdff1aSopenharmony_ci               c->x, c->y,
609cabdff1aSopenharmony_ci               geo->width, geo->height);
610cabdff1aSopenharmony_ci        free(geo);
611cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
612cabdff1aSopenharmony_ci    }
613cabdff1aSopenharmony_ci
614cabdff1aSopenharmony_ci    c->time_base  = (AVRational){ st->avg_frame_rate.den,
615cabdff1aSopenharmony_ci                                  st->avg_frame_rate.num };
616cabdff1aSopenharmony_ci    c->frame_duration = av_rescale_q(1, c->time_base, AV_TIME_BASE_Q);
617cabdff1aSopenharmony_ci    c->time_frame = av_gettime_relative();
618cabdff1aSopenharmony_ci
619cabdff1aSopenharmony_ci    ret = pixfmt_from_pixmap_format(s, geo->depth, &st->codecpar->format, &c->bpp);
620cabdff1aSopenharmony_ci    free(geo);
621cabdff1aSopenharmony_ci    if (ret < 0)
622cabdff1aSopenharmony_ci        return ret;
623cabdff1aSopenharmony_ci
624cabdff1aSopenharmony_ci    frame_size_bits = (int64_t)c->width * c->height * c->bpp;
625cabdff1aSopenharmony_ci    if (frame_size_bits / 8 + AV_INPUT_BUFFER_PADDING_SIZE > INT_MAX) {
626cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Captured area is too large\n");
627cabdff1aSopenharmony_ci        return AVERROR_PATCHWELCOME;
628cabdff1aSopenharmony_ci    }
629cabdff1aSopenharmony_ci    c->frame_size = frame_size_bits / 8;
630cabdff1aSopenharmony_ci
631cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
632cabdff1aSopenharmony_ci    c->shm_pool = av_buffer_pool_init2(c->frame_size + AV_INPUT_BUFFER_PADDING_SIZE,
633cabdff1aSopenharmony_ci                                           c->conn, allocate_shm_buffer, NULL);
634cabdff1aSopenharmony_ci    if (!c->shm_pool)
635cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
636cabdff1aSopenharmony_ci#endif
637cabdff1aSopenharmony_ci
638cabdff1aSopenharmony_ci    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
639cabdff1aSopenharmony_ci    st->codecpar->codec_id   = AV_CODEC_ID_RAWVIDEO;
640cabdff1aSopenharmony_ci    st->codecpar->width      = c->width;
641cabdff1aSopenharmony_ci    st->codecpar->height     = c->height;
642cabdff1aSopenharmony_ci    st->codecpar->bit_rate   = av_rescale(frame_size_bits, st->avg_frame_rate.num, st->avg_frame_rate.den);
643cabdff1aSopenharmony_ci
644cabdff1aSopenharmony_ci    return ret;
645cabdff1aSopenharmony_ci}
646cabdff1aSopenharmony_ci
647cabdff1aSopenharmony_cistatic void draw_rectangle(AVFormatContext *s)
648cabdff1aSopenharmony_ci{
649cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
650cabdff1aSopenharmony_ci    xcb_gcontext_t gc = xcb_generate_id(c->conn);
651cabdff1aSopenharmony_ci    uint32_t mask     = XCB_GC_FOREGROUND |
652cabdff1aSopenharmony_ci                        XCB_GC_BACKGROUND |
653cabdff1aSopenharmony_ci                        XCB_GC_LINE_WIDTH |
654cabdff1aSopenharmony_ci                        XCB_GC_LINE_STYLE |
655cabdff1aSopenharmony_ci                        XCB_GC_FILL_STYLE;
656cabdff1aSopenharmony_ci    uint32_t values[] = { c->screen->black_pixel,
657cabdff1aSopenharmony_ci                          c->screen->white_pixel,
658cabdff1aSopenharmony_ci                          c->region_border,
659cabdff1aSopenharmony_ci                          XCB_LINE_STYLE_DOUBLE_DASH,
660cabdff1aSopenharmony_ci                          XCB_FILL_STYLE_SOLID };
661cabdff1aSopenharmony_ci    xcb_rectangle_t r = { 1, 1,
662cabdff1aSopenharmony_ci                          c->width  + c->region_border * 2 - 3,
663cabdff1aSopenharmony_ci                          c->height + c->region_border * 2 - 3 };
664cabdff1aSopenharmony_ci
665cabdff1aSopenharmony_ci    xcb_create_gc(c->conn, gc, c->window, mask, values);
666cabdff1aSopenharmony_ci
667cabdff1aSopenharmony_ci    xcb_poly_rectangle(c->conn, c->window, gc, 1, &r);
668cabdff1aSopenharmony_ci}
669cabdff1aSopenharmony_ci
670cabdff1aSopenharmony_cistatic void setup_window(AVFormatContext *s)
671cabdff1aSopenharmony_ci{
672cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
673cabdff1aSopenharmony_ci    uint32_t mask     = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
674cabdff1aSopenharmony_ci    uint32_t values[] = { 1,
675cabdff1aSopenharmony_ci                          XCB_EVENT_MASK_EXPOSURE |
676cabdff1aSopenharmony_ci                          XCB_EVENT_MASK_STRUCTURE_NOTIFY };
677cabdff1aSopenharmony_ci    av_unused xcb_rectangle_t rect = { 0, 0, c->width, c->height };
678cabdff1aSopenharmony_ci
679cabdff1aSopenharmony_ci    c->window = xcb_generate_id(c->conn);
680cabdff1aSopenharmony_ci
681cabdff1aSopenharmony_ci    xcb_create_window(c->conn, XCB_COPY_FROM_PARENT,
682cabdff1aSopenharmony_ci                      c->window,
683cabdff1aSopenharmony_ci                      c->screen->root,
684cabdff1aSopenharmony_ci                      c->x - c->region_border,
685cabdff1aSopenharmony_ci                      c->y - c->region_border,
686cabdff1aSopenharmony_ci                      c->width + c->region_border * 2,
687cabdff1aSopenharmony_ci                      c->height + c->region_border * 2,
688cabdff1aSopenharmony_ci                      0,
689cabdff1aSopenharmony_ci                      XCB_WINDOW_CLASS_INPUT_OUTPUT,
690cabdff1aSopenharmony_ci                      XCB_COPY_FROM_PARENT,
691cabdff1aSopenharmony_ci                      mask, values);
692cabdff1aSopenharmony_ci
693cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHAPE
694cabdff1aSopenharmony_ci    xcb_shape_rectangles(c->conn, XCB_SHAPE_SO_SUBTRACT,
695cabdff1aSopenharmony_ci                         XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED,
696cabdff1aSopenharmony_ci                         c->window,
697cabdff1aSopenharmony_ci                         c->region_border, c->region_border,
698cabdff1aSopenharmony_ci                         1, &rect);
699cabdff1aSopenharmony_ci#endif
700cabdff1aSopenharmony_ci
701cabdff1aSopenharmony_ci    xcb_map_window(c->conn, c->window);
702cabdff1aSopenharmony_ci
703cabdff1aSopenharmony_ci    draw_rectangle(s);
704cabdff1aSopenharmony_ci}
705cabdff1aSopenharmony_ci
706cabdff1aSopenharmony_ci#define CROSSHAIR_CURSOR 34
707cabdff1aSopenharmony_ci
708cabdff1aSopenharmony_cistatic xcb_rectangle_t rectangle_from_corners(xcb_point_t *corner_a,
709cabdff1aSopenharmony_ci                                              xcb_point_t *corner_b)
710cabdff1aSopenharmony_ci{
711cabdff1aSopenharmony_ci    xcb_rectangle_t rectangle;
712cabdff1aSopenharmony_ci    rectangle.x = FFMIN(corner_a->x, corner_b->x);
713cabdff1aSopenharmony_ci    rectangle.y = FFMIN(corner_a->y, corner_b->y);
714cabdff1aSopenharmony_ci    rectangle.width = FFABS(corner_a->x - corner_b->x);
715cabdff1aSopenharmony_ci    rectangle.height = FFABS(corner_a->y - corner_b->y);
716cabdff1aSopenharmony_ci    return rectangle;
717cabdff1aSopenharmony_ci}
718cabdff1aSopenharmony_ci
719cabdff1aSopenharmony_cistatic int select_region(AVFormatContext *s)
720cabdff1aSopenharmony_ci{
721cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
722cabdff1aSopenharmony_ci    xcb_connection_t *conn = c->conn;
723cabdff1aSopenharmony_ci    xcb_screen_t *screen = c->screen;
724cabdff1aSopenharmony_ci
725cabdff1aSopenharmony_ci    int ret = 0, done = 0, was_pressed = 0;
726cabdff1aSopenharmony_ci    xcb_cursor_t cursor;
727cabdff1aSopenharmony_ci    xcb_font_t cursor_font;
728cabdff1aSopenharmony_ci    xcb_point_t press_position;
729cabdff1aSopenharmony_ci    xcb_generic_event_t *event;
730cabdff1aSopenharmony_ci    xcb_rectangle_t rectangle = { 0 };
731cabdff1aSopenharmony_ci    xcb_grab_pointer_reply_t *reply;
732cabdff1aSopenharmony_ci    xcb_grab_pointer_cookie_t cookie;
733cabdff1aSopenharmony_ci
734cabdff1aSopenharmony_ci    xcb_window_t root_window = screen->root;
735cabdff1aSopenharmony_ci    xcb_gcontext_t gc = xcb_generate_id(conn);
736cabdff1aSopenharmony_ci    uint32_t mask = XCB_GC_FUNCTION | XCB_GC_SUBWINDOW_MODE;
737cabdff1aSopenharmony_ci    uint32_t values[] = { XCB_GX_INVERT, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS };
738cabdff1aSopenharmony_ci    xcb_create_gc(conn, gc, root_window, mask, values);
739cabdff1aSopenharmony_ci
740cabdff1aSopenharmony_ci    cursor_font = xcb_generate_id(conn);
741cabdff1aSopenharmony_ci    xcb_open_font(conn, cursor_font, strlen("cursor"), "cursor");
742cabdff1aSopenharmony_ci    cursor = xcb_generate_id(conn);
743cabdff1aSopenharmony_ci    xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font,
744cabdff1aSopenharmony_ci                            CROSSHAIR_CURSOR, CROSSHAIR_CURSOR + 1, 0, 0, 0,
745cabdff1aSopenharmony_ci                            0xFFFF, 0xFFFF, 0xFFFF);
746cabdff1aSopenharmony_ci    cookie = xcb_grab_pointer(conn, 0, root_window,
747cabdff1aSopenharmony_ci                              XCB_EVENT_MASK_BUTTON_PRESS |
748cabdff1aSopenharmony_ci                              XCB_EVENT_MASK_BUTTON_RELEASE |
749cabdff1aSopenharmony_ci                              XCB_EVENT_MASK_BUTTON_MOTION,
750cabdff1aSopenharmony_ci                              XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC,
751cabdff1aSopenharmony_ci                              root_window, cursor, XCB_CURRENT_TIME);
752cabdff1aSopenharmony_ci    reply = xcb_grab_pointer_reply(conn, cookie, NULL);
753cabdff1aSopenharmony_ci    if (!reply || reply->status != XCB_GRAB_STATUS_SUCCESS) {
754cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR,
755cabdff1aSopenharmony_ci               "Failed to select region. Could not grab pointer.\n");
756cabdff1aSopenharmony_ci        ret = AVERROR(EIO);
757cabdff1aSopenharmony_ci        free(reply);
758cabdff1aSopenharmony_ci        goto fail;
759cabdff1aSopenharmony_ci    }
760cabdff1aSopenharmony_ci    free(reply);
761cabdff1aSopenharmony_ci
762cabdff1aSopenharmony_ci    xcb_grab_server(conn);
763cabdff1aSopenharmony_ci
764cabdff1aSopenharmony_ci    while (!done && (event = xcb_wait_for_event(conn))) {
765cabdff1aSopenharmony_ci        switch (event->response_type & ~0x80) {
766cabdff1aSopenharmony_ci        case XCB_BUTTON_PRESS: {
767cabdff1aSopenharmony_ci            xcb_button_press_event_t *press = (xcb_button_press_event_t *)event;
768cabdff1aSopenharmony_ci            press_position = (xcb_point_t){ press->event_x, press->event_y };
769cabdff1aSopenharmony_ci            rectangle.x = press_position.x;
770cabdff1aSopenharmony_ci            rectangle.y = press_position.y;
771cabdff1aSopenharmony_ci            xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
772cabdff1aSopenharmony_ci            was_pressed = 1;
773cabdff1aSopenharmony_ci            break;
774cabdff1aSopenharmony_ci        }
775cabdff1aSopenharmony_ci        case XCB_MOTION_NOTIFY: {
776cabdff1aSopenharmony_ci            if (was_pressed) {
777cabdff1aSopenharmony_ci                xcb_motion_notify_event_t *motion =
778cabdff1aSopenharmony_ci                    (xcb_motion_notify_event_t *)event;
779cabdff1aSopenharmony_ci                xcb_point_t cursor_position = { motion->event_x, motion->event_y };
780cabdff1aSopenharmony_ci                xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
781cabdff1aSopenharmony_ci                rectangle = rectangle_from_corners(&press_position, &cursor_position);
782cabdff1aSopenharmony_ci                xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
783cabdff1aSopenharmony_ci            }
784cabdff1aSopenharmony_ci            break;
785cabdff1aSopenharmony_ci        }
786cabdff1aSopenharmony_ci        case XCB_BUTTON_RELEASE: {
787cabdff1aSopenharmony_ci            xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle);
788cabdff1aSopenharmony_ci            done = 1;
789cabdff1aSopenharmony_ci            break;
790cabdff1aSopenharmony_ci        }
791cabdff1aSopenharmony_ci        default:
792cabdff1aSopenharmony_ci            break;
793cabdff1aSopenharmony_ci        }
794cabdff1aSopenharmony_ci        xcb_flush(conn);
795cabdff1aSopenharmony_ci        free(event);
796cabdff1aSopenharmony_ci    }
797cabdff1aSopenharmony_ci    c->width  = rectangle.width;
798cabdff1aSopenharmony_ci    c->height = rectangle.height;
799cabdff1aSopenharmony_ci    if (c->width && c->height) {
800cabdff1aSopenharmony_ci        c->x = rectangle.x;
801cabdff1aSopenharmony_ci        c->y = rectangle.y;
802cabdff1aSopenharmony_ci    } else {
803cabdff1aSopenharmony_ci        c->x = 0;
804cabdff1aSopenharmony_ci        c->y = 0;
805cabdff1aSopenharmony_ci    }
806cabdff1aSopenharmony_ci    xcb_ungrab_server(conn);
807cabdff1aSopenharmony_ci    xcb_ungrab_pointer(conn, XCB_CURRENT_TIME);
808cabdff1aSopenharmony_ci    xcb_flush(conn);
809cabdff1aSopenharmony_ci
810cabdff1aSopenharmony_cifail:
811cabdff1aSopenharmony_ci    xcb_free_cursor(conn, cursor);
812cabdff1aSopenharmony_ci    xcb_close_font(conn, cursor_font);
813cabdff1aSopenharmony_ci    xcb_free_gc(conn, gc);
814cabdff1aSopenharmony_ci    return ret;
815cabdff1aSopenharmony_ci}
816cabdff1aSopenharmony_ci
817cabdff1aSopenharmony_cistatic av_cold int xcbgrab_read_header(AVFormatContext *s)
818cabdff1aSopenharmony_ci{
819cabdff1aSopenharmony_ci    XCBGrabContext *c = s->priv_data;
820cabdff1aSopenharmony_ci    int screen_num, ret;
821cabdff1aSopenharmony_ci    const xcb_setup_t *setup;
822cabdff1aSopenharmony_ci    char *display_name = av_strdup(s->url);
823cabdff1aSopenharmony_ci
824cabdff1aSopenharmony_ci    if (!display_name)
825cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
826cabdff1aSopenharmony_ci
827cabdff1aSopenharmony_ci    if (!sscanf(s->url, "%[^+]+%d,%d", display_name, &c->x, &c->y)) {
828cabdff1aSopenharmony_ci        *display_name = 0;
829cabdff1aSopenharmony_ci        sscanf(s->url, "+%d,%d", &c->x, &c->y);
830cabdff1aSopenharmony_ci    }
831cabdff1aSopenharmony_ci
832cabdff1aSopenharmony_ci    c->conn = xcb_connect(display_name[0] ? display_name : NULL, &screen_num);
833cabdff1aSopenharmony_ci    av_freep(&display_name);
834cabdff1aSopenharmony_ci
835cabdff1aSopenharmony_ci    if ((ret = xcb_connection_has_error(c->conn))) {
836cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "Cannot open display %s, error %d.\n",
837cabdff1aSopenharmony_ci               s->url[0] ? s->url : "default", ret);
838cabdff1aSopenharmony_ci        return AVERROR(EIO);
839cabdff1aSopenharmony_ci    }
840cabdff1aSopenharmony_ci
841cabdff1aSopenharmony_ci    setup = xcb_get_setup(c->conn);
842cabdff1aSopenharmony_ci
843cabdff1aSopenharmony_ci    c->screen = get_screen(setup, screen_num);
844cabdff1aSopenharmony_ci    if (!c->screen) {
845cabdff1aSopenharmony_ci        av_log(s, AV_LOG_ERROR, "The screen %d does not exist.\n",
846cabdff1aSopenharmony_ci               screen_num);
847cabdff1aSopenharmony_ci        xcbgrab_read_close(s);
848cabdff1aSopenharmony_ci        return AVERROR(EIO);
849cabdff1aSopenharmony_ci    }
850cabdff1aSopenharmony_ci
851cabdff1aSopenharmony_ci    if (c->window_id == XCB_NONE)
852cabdff1aSopenharmony_ci        c->window_id = c->screen->root;
853cabdff1aSopenharmony_ci    else {
854cabdff1aSopenharmony_ci        if (c->select_region) {
855cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "select_region ignored with window_id.\n");
856cabdff1aSopenharmony_ci            c->select_region = 0;
857cabdff1aSopenharmony_ci        }
858cabdff1aSopenharmony_ci        if (c->follow_mouse) {
859cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING, "follow_mouse ignored with window_id.\n");
860cabdff1aSopenharmony_ci            c->follow_mouse = 0;
861cabdff1aSopenharmony_ci        }
862cabdff1aSopenharmony_ci    }
863cabdff1aSopenharmony_ci
864cabdff1aSopenharmony_ci    if (c->select_region) {
865cabdff1aSopenharmony_ci        ret = select_region(s);
866cabdff1aSopenharmony_ci        if (ret < 0) {
867cabdff1aSopenharmony_ci            xcbgrab_read_close(s);
868cabdff1aSopenharmony_ci            return ret;
869cabdff1aSopenharmony_ci        }
870cabdff1aSopenharmony_ci    }
871cabdff1aSopenharmony_ci
872cabdff1aSopenharmony_ci    ret = create_stream(s);
873cabdff1aSopenharmony_ci
874cabdff1aSopenharmony_ci    if (ret < 0) {
875cabdff1aSopenharmony_ci        xcbgrab_read_close(s);
876cabdff1aSopenharmony_ci        return ret;
877cabdff1aSopenharmony_ci    }
878cabdff1aSopenharmony_ci
879cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_SHM
880cabdff1aSopenharmony_ci    c->has_shm = check_shm(c->conn);
881cabdff1aSopenharmony_ci#endif
882cabdff1aSopenharmony_ci
883cabdff1aSopenharmony_ci#if CONFIG_LIBXCB_XFIXES
884cabdff1aSopenharmony_ci    if (c->draw_mouse) {
885cabdff1aSopenharmony_ci        if (!(c->draw_mouse = check_xfixes(c->conn))) {
886cabdff1aSopenharmony_ci            av_log(s, AV_LOG_WARNING,
887cabdff1aSopenharmony_ci                   "XFixes not available, cannot draw the mouse.\n");
888cabdff1aSopenharmony_ci        }
889cabdff1aSopenharmony_ci        if (c->bpp < 24) {
890cabdff1aSopenharmony_ci            avpriv_report_missing_feature(s, "%d bits per pixel screen",
891cabdff1aSopenharmony_ci                                          c->bpp);
892cabdff1aSopenharmony_ci            c->draw_mouse = 0;
893cabdff1aSopenharmony_ci        }
894cabdff1aSopenharmony_ci    }
895cabdff1aSopenharmony_ci#endif
896cabdff1aSopenharmony_ci
897cabdff1aSopenharmony_ci    if (c->show_region)
898cabdff1aSopenharmony_ci        setup_window(s);
899cabdff1aSopenharmony_ci
900cabdff1aSopenharmony_ci    return 0;
901cabdff1aSopenharmony_ci}
902cabdff1aSopenharmony_ci
903cabdff1aSopenharmony_ciconst AVInputFormat ff_xcbgrab_demuxer = {
904cabdff1aSopenharmony_ci    .name           = "x11grab",
905cabdff1aSopenharmony_ci    .long_name      = NULL_IF_CONFIG_SMALL("X11 screen capture, using XCB"),
906cabdff1aSopenharmony_ci    .priv_data_size = sizeof(XCBGrabContext),
907cabdff1aSopenharmony_ci    .read_header    = xcbgrab_read_header,
908cabdff1aSopenharmony_ci    .read_packet    = xcbgrab_read_packet,
909cabdff1aSopenharmony_ci    .read_close     = xcbgrab_read_close,
910cabdff1aSopenharmony_ci    .flags          = AVFMT_NOFILE,
911cabdff1aSopenharmony_ci    .priv_class     = &xcbgrab_class,
912cabdff1aSopenharmony_ci};
913