|
|
|
@@ -22,6 +22,7 @@ |
|
|
|
#include "config.h" |
|
|
|
|
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <xcb/xcb.h> |
|
|
|
|
|
|
|
#if CONFIG_LIBXCB_XFIXES |
|
|
|
@@ -69,6 +70,7 @@ typedef struct XCBGrabContext { |
|
|
|
int show_region; |
|
|
|
int region_border; |
|
|
|
int centered; |
|
|
|
int select_region; |
|
|
|
|
|
|
|
const char *framerate; |
|
|
|
|
|
|
|
@@ -92,6 +94,7 @@ static const AVOption options[] = { |
|
|
|
{ "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" }, |
|
|
|
{ "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D }, |
|
|
|
{ "region_border", "Set the region border thickness.", OFFSET(region_border), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128, D }, |
|
|
|
{ "select_region", "Select the grabbing region graphically using the pointer.", OFFSET(select_region), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D }, |
|
|
|
{ NULL }, |
|
|
|
}; |
|
|
|
|
|
|
|
@@ -677,6 +680,117 @@ static void setup_window(AVFormatContext *s) |
|
|
|
draw_rectangle(s); |
|
|
|
} |
|
|
|
|
|
|
|
#define CROSSHAIR_CURSOR 34 |
|
|
|
|
|
|
|
static xcb_rectangle_t rectangle_from_corners(xcb_point_t *corner_a, |
|
|
|
xcb_point_t *corner_b) |
|
|
|
{ |
|
|
|
xcb_rectangle_t rectangle; |
|
|
|
rectangle.x = FFMIN(corner_a->x, corner_b->x); |
|
|
|
rectangle.y = FFMIN(corner_a->y, corner_b->y); |
|
|
|
rectangle.width = FFABS(corner_a->x - corner_b->x); |
|
|
|
rectangle.height = FFABS(corner_a->y - corner_b->y); |
|
|
|
return rectangle; |
|
|
|
} |
|
|
|
|
|
|
|
static int select_region(AVFormatContext *s) |
|
|
|
{ |
|
|
|
XCBGrabContext *c = s->priv_data; |
|
|
|
xcb_connection_t *conn = c->conn; |
|
|
|
xcb_screen_t *screen = c->screen; |
|
|
|
|
|
|
|
int ret = 0, done = 0, was_pressed = 0; |
|
|
|
xcb_cursor_t cursor; |
|
|
|
xcb_font_t cursor_font; |
|
|
|
xcb_point_t press_position; |
|
|
|
xcb_generic_event_t *event; |
|
|
|
xcb_rectangle_t rectangle = { 0 }; |
|
|
|
xcb_grab_pointer_reply_t *reply; |
|
|
|
xcb_grab_pointer_cookie_t cookie; |
|
|
|
|
|
|
|
xcb_window_t root_window = screen->root; |
|
|
|
xcb_gcontext_t gc = xcb_generate_id(conn); |
|
|
|
uint32_t mask = XCB_GC_FUNCTION | XCB_GC_SUBWINDOW_MODE; |
|
|
|
uint32_t values[] = { XCB_GX_INVERT, XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS }; |
|
|
|
xcb_create_gc(conn, gc, root_window, mask, values); |
|
|
|
|
|
|
|
cursor_font = xcb_generate_id(conn); |
|
|
|
xcb_open_font(conn, cursor_font, strlen("cursor"), "cursor"); |
|
|
|
cursor = xcb_generate_id(conn); |
|
|
|
xcb_create_glyph_cursor(conn, cursor, cursor_font, cursor_font, |
|
|
|
CROSSHAIR_CURSOR, CROSSHAIR_CURSOR + 1, 0, 0, 0, |
|
|
|
0xFFFF, 0xFFFF, 0xFFFF); |
|
|
|
cookie = xcb_grab_pointer(conn, 0, root_window, |
|
|
|
XCB_EVENT_MASK_BUTTON_PRESS | |
|
|
|
XCB_EVENT_MASK_BUTTON_RELEASE | |
|
|
|
XCB_EVENT_MASK_BUTTON_MOTION, |
|
|
|
XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, |
|
|
|
root_window, cursor, XCB_CURRENT_TIME); |
|
|
|
reply = xcb_grab_pointer_reply(conn, cookie, NULL); |
|
|
|
if (!reply || reply->status != XCB_GRAB_STATUS_SUCCESS) { |
|
|
|
av_log(s, AV_LOG_ERROR, |
|
|
|
"Failed to select region. Could not grab pointer.\n"); |
|
|
|
ret = AVERROR(EIO); |
|
|
|
free(reply); |
|
|
|
goto fail; |
|
|
|
} |
|
|
|
free(reply); |
|
|
|
|
|
|
|
xcb_grab_server(conn); |
|
|
|
|
|
|
|
while (!done && (event = xcb_wait_for_event(conn))) { |
|
|
|
switch (event->response_type & ~0x80) { |
|
|
|
case XCB_BUTTON_PRESS: { |
|
|
|
xcb_button_press_event_t *press = (xcb_button_press_event_t *)event; |
|
|
|
press_position = (xcb_point_t){ press->event_x, press->event_y }; |
|
|
|
rectangle.x = press_position.x; |
|
|
|
rectangle.y = press_position.y; |
|
|
|
xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); |
|
|
|
was_pressed = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
case XCB_MOTION_NOTIFY: { |
|
|
|
if (was_pressed) { |
|
|
|
xcb_motion_notify_event_t *motion = |
|
|
|
(xcb_motion_notify_event_t *)event; |
|
|
|
xcb_point_t cursor_position = { motion->event_x, motion->event_y }; |
|
|
|
xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); |
|
|
|
rectangle = rectangle_from_corners(&press_position, &cursor_position); |
|
|
|
xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
case XCB_BUTTON_RELEASE: { |
|
|
|
xcb_poly_rectangle(conn, root_window, gc, 1, &rectangle); |
|
|
|
done = 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
xcb_flush(conn); |
|
|
|
free(event); |
|
|
|
} |
|
|
|
c->width = rectangle.width; |
|
|
|
c->height = rectangle.height; |
|
|
|
if (c->width && c->height) { |
|
|
|
c->x = rectangle.x; |
|
|
|
c->y = rectangle.y; |
|
|
|
} else { |
|
|
|
c->x = 0; |
|
|
|
c->y = 0; |
|
|
|
} |
|
|
|
xcb_ungrab_server(conn); |
|
|
|
xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); |
|
|
|
xcb_flush(conn); |
|
|
|
|
|
|
|
fail: |
|
|
|
xcb_free_cursor(conn, cursor); |
|
|
|
xcb_close_font(conn, cursor_font); |
|
|
|
xcb_free_gc(conn, gc); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
static av_cold int xcbgrab_read_header(AVFormatContext *s) |
|
|
|
{ |
|
|
|
XCBGrabContext *c = s->priv_data; |
|
|
|
@@ -711,6 +825,14 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) |
|
|
|
return AVERROR(EIO); |
|
|
|
} |
|
|
|
|
|
|
|
if (c->select_region) { |
|
|
|
ret = select_region(s); |
|
|
|
if (ret < 0) { |
|
|
|
xcbgrab_read_close(s); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
ret = create_stream(s); |
|
|
|
|
|
|
|
if (ret < 0) { |
|
|
|
|