#include "config.h"
#include <stdlib.h>
+#include <string.h>
#include <xcb/xcb.h>
#if CONFIG_LIBXCB_XFIXES
int show_region;
int region_border;
int centered;
+ int select_region;
const char *framerate;
{ "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 },
};
c->time_frame += c->frame_duration;
for (;;) {
- curtime = av_gettime();
+ curtime = av_gettime_relative();
delay = c->time_frame - curtime;
if (delay <= 0)
break;
shmdt(data);
}
-static AVBufferRef *allocate_shm_buffer(void *opaque, int size)
+static AVBufferRef *allocate_shm_buffer(void *opaque, buffer_size_t size)
{
xcb_connection_t *conn = opaque;
xcb_shm_seg_t segment;
int ret = 0;
int64_t pts;
- pts = wait_frame(s, pkt);
+ wait_frame(s, pkt);
+ pts = av_gettime();
if (c->follow_mouse || c->draw_mouse) {
pc = xcb_query_pointer(c->conn, c->screen->root);
switch (depth) {
case 32:
if (fmt->bits_per_pixel == 32)
- *pix_fmt = AV_PIX_FMT_0RGB;
+ *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+ AV_PIX_FMT_BGR0 : AV_PIX_FMT_0RGB;
break;
case 24:
if (fmt->bits_per_pixel == 32)
- *pix_fmt = AV_PIX_FMT_0RGB32;
+ *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+ AV_PIX_FMT_BGR0 : AV_PIX_FMT_0RGB;
else if (fmt->bits_per_pixel == 24)
- *pix_fmt = AV_PIX_FMT_RGB24;
+ *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+ AV_PIX_FMT_BGR24 : AV_PIX_FMT_RGB24;
break;
case 16:
if (fmt->bits_per_pixel == 16)
- *pix_fmt = AV_PIX_FMT_RGB565;
+ *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+ AV_PIX_FMT_RGB565LE : AV_PIX_FMT_RGB565BE;
break;
case 15:
if (fmt->bits_per_pixel == 16)
- *pix_fmt = AV_PIX_FMT_RGB555;
+ *pix_fmt = setup->image_byte_order == XCB_IMAGE_ORDER_LSB_FIRST ?
+ AV_PIX_FMT_RGB555LE : AV_PIX_FMT_RGB555BE;
break;
case 8:
if (fmt->bits_per_pixel == 8)
c->time_base = (AVRational){ st->avg_frame_rate.den,
st->avg_frame_rate.num };
c->frame_duration = av_rescale_q(1, c->time_base, AV_TIME_BASE_Q);
- c->time_frame = av_gettime();
+ c->time_frame = av_gettime_relative();
ret = pixfmt_from_pixmap_format(s, geo->depth, &st->codecpar->format, &c->bpp);
free(geo);
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;
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) {