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