Browse Source

Merge pull request #312 from fryshorts/xshm-xcb-conversion

linux-capture: Xshm plugin xcb conversion
Jim 11 years ago
parent
commit
903bb137a0

+ 8 - 3
plugins/linux-capture/CMakeLists.txt

@@ -6,15 +6,20 @@ if(NOT X11_Xcomposite_FOUND)
 	return()
 endif()
 
+find_package(XCB COMPONENTS XCB SHM XFIXES XINERAMA REQUIRED)
+find_package(X11_XCB REQUIRED)
+
 include_directories(SYSTEM
 	"${CMAKE_SOURCE_DIR}/libobs"
 	${X11_Xcomposite_INCLUDE_PATH}
 	${X11_X11_INCLUDE_PATH}
-) 
+	${XCB_INCLUDE_DIRS}
+)
 
 set(linux-capture_SOURCES
 	linux-capture.c
 	xcursor.c
+	xcursor-xcb.c
 	xhelpers.c
 	xshm-input.c
 	xcomposite-main.cpp
@@ -23,6 +28,7 @@ set(linux-capture_SOURCES
 )
 set(linux-capture_HEADERS
 	xcursor.h
+	xcursor-xcb.h
 	xhelpers.h
 	xcompcap-main.hpp
 	xcompcap-helper.hpp
@@ -36,11 +42,10 @@ target_link_libraries(linux-capture
 	libobs
 	glad
 	${X11_LIBRARIES}
-	${X11_XShm_LIB}
 	${X11_Xfixes_LIB}
-	${X11_Xinerama_LIB}
 	${X11_X11_LIB}
 	${X11_Xcomposite_LIB}
+	${XCB_LIBRARIES}
 )
 
 install_obs_plugin_with_data(linux-capture data)

+ 114 - 0
plugins/linux-capture/xcursor-xcb.c

@@ -0,0 +1,114 @@
+/*
+Copyright (C) 2014 by Leonhard Oelke <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+#include <xcb/xfixes.h>
+
+#include <util/bmem.h>
+#include "xcursor-xcb.h"
+
+/*
+ * Create the cursor texture, either by updating if the new cursor has the same
+ * size or by creating a new texture if the size is different
+ */
+static void xcb_xcursor_create(xcb_xcursor_t *data,
+		xcb_xfixes_get_cursor_image_reply_t *xc)
+{
+	uint32_t *pixels = xcb_xfixes_get_cursor_image_cursor_image(xc);
+	if (!pixels)
+		return;
+
+	if (data->tex && data->last_height == xc->width &&
+			data->last_width == xc->height) {
+		gs_texture_set_image(data->tex, (const uint8_t *) pixels,
+			xc->width * sizeof(uint32_t), false);
+	} else {
+		if (data->tex)
+			gs_texture_destroy(data->tex);
+
+		data->tex = gs_texture_create(xc->width, xc->height,
+			GS_BGRA, 1, (const uint8_t **) &pixels, GS_DYNAMIC);
+	}
+
+	data->last_serial = xc->cursor_serial;
+	data->last_width  = xc->width;
+	data->last_height = xc->height;
+}
+
+/**
+ * We need to check for the xfixes version in order to initialize it ?
+ */
+xcb_xcursor_t *xcb_xcursor_init(xcb_connection_t *xcb)
+{
+	xcb_xcursor_t *data = bzalloc(sizeof(xcb_xcursor_t));
+
+	xcb_xfixes_query_version_cookie_t xfix_c;
+
+	xfix_c = xcb_xfixes_query_version_unchecked(xcb,
+			XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION);
+	free(xcb_xfixes_query_version_reply(xcb, xfix_c, NULL));
+
+	return data;
+}
+
+void xcb_xcursor_destroy(xcb_xcursor_t *data)
+{
+	if (data->tex)
+		gs_texture_destroy(data->tex);
+	bfree(data);
+}
+
+void xcb_xcursor_update(xcb_xcursor_t *data,
+		xcb_xfixes_get_cursor_image_reply_t *xc)
+{
+	if (!data || !xc)
+		return;
+
+	if (!data->tex || data->last_serial != xc->cursor_serial)
+		xcb_xcursor_create(data, xc);
+
+	data->x        = xc->x - data->x_org;
+	data->y        = xc->y - data->y_org;
+	data->x_render = data->x - xc->xhot;
+	data->y_render = data->y - xc->yhot;
+}
+
+void xcb_xcursor_render(xcb_xcursor_t *data)
+{
+	if (!data->tex)
+		return;
+
+	gs_effect_t *effect  = gs_get_effect();
+	gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image");
+	gs_effect_set_texture(image, data->tex);
+
+	gs_matrix_push();
+	gs_matrix_translate3f(data->x_render, data->y_render, 0.0f);
+
+	gs_enable_blending(true);
+	gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
+	gs_draw_sprite(data->tex, 0, 0, 0);
+
+	gs_matrix_pop();
+}
+
+void xcb_xcursor_offset(xcb_xcursor_t* data, const int x_org, const int y_org)
+{
+	data->x_org = x_org;
+	data->y_org = y_org;
+}
+

+ 79 - 0
plugins/linux-capture/xcursor-xcb.h

@@ -0,0 +1,79 @@
+/*
+Copyright (C) 2014 by Leonhard Oelke <[email protected]>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <obs.h>
+#include <xcb/xfixes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+	unsigned int last_serial;
+	unsigned int last_width;
+	unsigned int last_height;
+	gs_texture_t *tex;
+
+	int          x;
+	int          y;
+	int          x_org;
+	int          y_org;
+	float        x_render;
+	float        y_render;
+} xcb_xcursor_t;
+
+/**
+ * Initializes the xcursor object
+ *
+ * @return NULL on error
+ */
+xcb_xcursor_t *xcb_xcursor_init(xcb_connection_t *xcb);
+
+/**
+ * Destroys the xcursor object
+ * @param data xcursor object
+ */
+void xcb_xcursor_destroy(xcb_xcursor_t *data);
+
+/**
+ * Update the cursor data
+ * @param data xcursor object
+ * @param xc xcb cursor image reply
+ *
+ * @note This needs to be executed within a valid render context
+ *
+ */
+void xcb_xcursor_update(xcb_xcursor_t *data,
+		xcb_xfixes_get_cursor_image_reply_t *xc);
+
+/**
+ * Draw the cursor
+ *
+ * This needs to be executed within a valid render context
+ */
+void xcb_xcursor_render(xcb_xcursor_t *data);
+
+/**
+ * Specify offset for the cursor
+ */
+void xcb_xcursor_offset(xcb_xcursor_t *data, const int x_org, const int y_org);
+
+#ifdef __cplusplus
+}
+#endif

+ 104 - 80
plugins/linux-capture/xhelpers.c

@@ -17,134 +17,158 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <stdint.h>
 #include <sys/shm.h>
-#include <X11/Xutil.h>
-#include <X11/extensions/Xinerama.h>
+#include <xcb/xcb.h>
+#include <xcb/xinerama.h>
 
 #include "xhelpers.h"
 
-int_fast32_t xinerama_is_active(Display *dpy)
+bool xinerama_is_active(xcb_connection_t *xcb)
 {
-	int minor, major;
-	if (!dpy)
-		return 0;
-	if (!XineramaQueryVersion(dpy, &minor, &major))
-		return 0;
-	if (!XineramaIsActive(dpy))
-		return 0;
-	return 1;
+	if (!xcb || !xcb_get_extension_data(xcb, &xcb_xinerama_id)->present)
+		return false;
+
+	bool active = true;
+	xcb_xinerama_is_active_cookie_t xnr_c;
+	xcb_xinerama_is_active_reply_t  *xnr_r;
+
+	xnr_c = xcb_xinerama_is_active_unchecked(xcb);
+	xnr_r = xcb_xinerama_is_active_reply(xcb, xnr_c, NULL);
+	if (!xnr_r || xnr_r->state == 0)
+		active = false;
+	free(xnr_r);
+
+	return active;
 }
 
-int_fast32_t xinerama_screen_count(Display *dpy)
+int xinerama_screen_count(xcb_connection_t *xcb)
 {
-	int screens;
-	if (!dpy)
+	if (!xcb)
 		return 0;
-	XFree(XineramaQueryScreens(dpy, &screens));
+
+	int screens = 0;
+	xcb_xinerama_query_screens_cookie_t scr_c;
+	xcb_xinerama_query_screens_reply_t  *scr_r;
+
+	scr_c = xcb_xinerama_query_screens_unchecked(xcb);
+	scr_r = xcb_xinerama_query_screens_reply(xcb, scr_c, NULL);
+	if (scr_r)
+		screens = scr_r->number;
+	free(scr_r);
+
 	return screens;
 }
 
-int_fast32_t xinerama_screen_geo(Display *dpy, const int_fast32_t screen,
-	int_fast32_t *x, int_fast32_t *y, int_fast32_t *w, int_fast32_t *h)
+int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
+		int_fast32_t *x, int_fast32_t *y,
+		int_fast32_t *w, int_fast32_t *h)
 {
-	int screens;
-	XineramaScreenInfo *info = NULL;
-
-	if (!dpy)
+	if (!xcb)
 		goto fail;
-	info = XineramaQueryScreens(dpy, &screens);
-	if (screen < 0 || screen >= screens)
+
+	bool success = false;
+	xcb_xinerama_query_screens_cookie_t scr_c;
+	xcb_xinerama_query_screens_reply_t  *scr_r;
+	xcb_xinerama_screen_info_iterator_t iter;
+
+	scr_c = xcb_xinerama_query_screens_unchecked(xcb);
+	scr_r = xcb_xinerama_query_screens_reply(xcb, scr_c, NULL);
+	if (!scr_r)
 		goto fail;
 
-	*x = info[screen].x_org;
-	*y = info[screen].y_org;
-	*w = info[screen].width;
-	*h = info[screen].height;
+	iter = xcb_xinerama_query_screens_screen_info_iterator(scr_r);
+	for (; iter.rem; --screen, xcb_xinerama_screen_info_next(&iter)) {
+		if (!screen) {
+			*x = iter.data->x_org;
+			*y = iter.data->y_org;
+			*w = iter.data->width;
+			*h = iter.data->height;
+			success = true;
+		}
+	}
+	free(scr_r);
+
+	if (success)
+		return 0;
 
-	XFree(info);
-	return 0;
 fail:
-	if (info)
-		XFree(info);
-	
 	*x = *y = *w = *h = 0;
 	return -1;
 }
 
-int_fast32_t x11_screen_geo(Display *dpy, const int_fast32_t screen,
-	int_fast32_t *w, int_fast32_t *h)
+int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
+		int_fast32_t *w, int_fast32_t *h)
 {
-	Screen *scr;
-
-	if (!dpy || screen < 0 || screen >= XScreenCount(dpy))
+	if (!xcb)
 		goto fail;
 
-	scr = XScreenOfDisplay(dpy, screen);
-	if (!scr)
-		goto fail;
+	bool success = false;
+	xcb_screen_iterator_t iter;
 
-	*w = XWidthOfScreen(scr);
-	*h = XHeightOfScreen(scr);
+	iter = xcb_setup_roots_iterator(xcb_get_setup(xcb));
+	for (; iter.rem; --screen, xcb_screen_next(&iter)) {
+		if (!screen) {
+			*w = iter.data->width_in_pixels;
+			*h = iter.data->height_in_pixels;
+			success = true;
+		}
+	}
+
+	if (success)
+		return 0;
 
-	return 0;
 fail:
 	*w = *h = 0;
 	return -1;
 }
 
-xshm_t *xshm_attach(Display *dpy, Screen *screen,
-	int_fast32_t w, int_fast32_t h)
+xcb_shm_t* xshm_xcb_attach(xcb_connection_t *xcb, const int w, const int h)
 {
-	if (!dpy || !screen)
+	if (!xcb)
 		return NULL;
 
-	xshm_t *xshm = bzalloc(sizeof(xshm_t));
+	xcb_shm_t *shm = bzalloc(sizeof(xcb_shm_t));
+	shm->xcb       = xcb;
+	shm->seg       = xcb_generate_id(shm->xcb);
 
-	xshm->dpy = dpy;
-	xshm->image = XShmCreateImage(xshm->dpy, DefaultVisualOfScreen(screen),
-		DefaultDepthOfScreen(screen), ZPixmap, NULL, &xshm->info,
-		w, h);
-	if (!xshm->image)
+	shm->shmid = shmget(IPC_PRIVATE, w * h * 4, IPC_CREAT | 0777);
+	if (shm->shmid == -1)
 		goto fail;
 
-	xshm->info.shmid = shmget(IPC_PRIVATE,
-		xshm->image->bytes_per_line * xshm->image->height,
-		IPC_CREAT | 0700);
-	if (xshm->info.shmid < 0)
-		goto fail;
+	xcb_shm_attach(shm->xcb, shm->seg, shm->shmid, false);
 
-	xshm->info.shmaddr
-		= xshm->image->data
-		= (char *) shmat(xshm->info.shmid, 0, 0);
-	if (xshm->info.shmaddr == (char *) -1)
-		goto fail;
-	xshm->info.readOnly = false;
+	shm->data = shmat(shm->shmid, NULL, 0);
 
-	if (!XShmAttach(xshm->dpy, &xshm->info))
-		goto fail;
-
-	xshm->attached = true;
-	return xshm;
+	return shm;
 fail:
-	xshm_detach(xshm);
+	xshm_xcb_detach(shm);
 	return NULL;
 }
 
-void xshm_detach(xshm_t *xshm)
+void xshm_xcb_detach(xcb_shm_t *shm)
 {
-	if (!xshm)
+	if (!shm)
 		return;
 
-	if (xshm->attached)
-		XShmDetach(xshm->dpy, &xshm->info);
+	xcb_shm_detach(shm->xcb, shm->seg);
+
+	if ((char *) shm->data != (char *) -1)
+		shmdt(shm->data);
+
+	if (shm->shmid != -1)
+		shmctl(shm->shmid, IPC_RMID, NULL);
 
-	if (xshm->info.shmaddr != (char *) -1)
-		shmdt(xshm->info.shmaddr);
+	bfree(shm);
+}
 
-	if (xshm->info.shmid != -1)
-		shmctl(xshm->info.shmid, IPC_RMID, NULL);
+xcb_screen_t *xcb_get_screen(xcb_connection_t *xcb, int screen)
+{
+	xcb_screen_iterator_t iter;
 
-	if (xshm->image)
-		XDestroyImage(xshm->image);
+	iter = xcb_setup_roots_iterator(xcb_get_setup(xcb));
+	for (; iter.rem; --screen, xcb_screen_next(&iter)) {
+		if (screen == 0)
+			return iter.data;
+	}
 
-	bfree(xshm);
+	return NULL;
 }

+ 29 - 21
plugins/linux-capture/xhelpers.h

@@ -21,30 +21,30 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 extern "C" {
 #endif
 
-#include <X11/Xlib.h>
-#include <X11/extensions/XShm.h>
+#include <xcb/shm.h>
+#include <xcb/xproto.h>
 #include <obs.h>
 
 typedef struct {
-	XShmSegmentInfo info;
-	XImage *image;
-	Display *dpy;
-	bool attached;
-} xshm_t;
+	xcb_connection_t *xcb;
+	xcb_shm_seg_t    seg;
+	int              shmid;
+	uint8_t          *data;
+} xcb_shm_t;
 
 /**
  * Check for Xinerama extension
  *
- * @return > 0 if Xinerama is available and active
+ * @return true if xinerama is available and active
  */
-int_fast32_t xinerama_is_active(Display *dpy);
+bool xinerama_is_active(xcb_connection_t *xcb);
 
 /**
  * Get the number of Xinerama screens
  *
  * @return number of screens
  */
-int_fast32_t xinerama_screen_count(Display *dpy);
+int xinerama_screen_count(xcb_connection_t *xcb);
 
 /**
  * Get screen geometry for a Xinerama screen
@@ -60,8 +60,9 @@ int_fast32_t xinerama_screen_count(Display *dpy);
  *
  * @return < 0 on error
  */
-int_fast32_t xinerama_screen_geo(Display *dpy, const int_fast32_t screen,
-	int_fast32_t *x, int_fast32_t *y, int_fast32_t *w, int_fast32_t *h);
+int xinerama_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
+		int_fast32_t *x, int_fast32_t *y,
+		int_fast32_t *w, int_fast32_t *h);
 
 /**
  * Get screen geometry for a X11 screen
@@ -75,26 +76,33 @@ int_fast32_t xinerama_screen_geo(Display *dpy, const int_fast32_t screen,
  *
  * @return < 0 on error
  */
-int_fast32_t x11_screen_geo(Display *dpy, const int_fast32_t screen,
-	int_fast32_t *w, int_fast32_t *h);
+int x11_screen_geo(xcb_connection_t *xcb, int_fast32_t screen,
+		int_fast32_t *w, int_fast32_t *h);
 
 /**
  * Attach a shared memory segment to the X-Server
  *
- * @param dpy X11 Display
- * @param screen X11 Screen
- * @param w width for the shared memory segment
- * @param h height for the shared memory segment
+ * @param xcb xcb connection
+ * @param w width of the captured screen
+ * @param h height of the captured screen
  *
  * @return NULL on error
  */
-xshm_t *xshm_attach(Display *dpy, Screen *screen,
-	int_fast32_t w, int_fast32_t h);
+xcb_shm_t *xshm_xcb_attach(xcb_connection_t *xcb, const int w, const int h);
 
 /**
  * Detach a shared memory segment
  */
-void xshm_detach(xshm_t *xshm);
+void xshm_xcb_detach(xcb_shm_t *shm);
+
+/**
+ * Get screen by id for a xcb connection
+ *
+ * @param xcb xcb connection
+ * @param screen id of the screen
+ * @return screen info structure
+ */
+xcb_screen_t *xcb_get_screen(xcb_connection_t *xcb, int screen);
 
 #ifdef __cplusplus
 }

+ 89 - 62
plugins/linux-capture/xshm-input.c

@@ -18,12 +18,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <stdio.h>
 #include <stdlib.h>
 #include <inttypes.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
+#include <xcb/shm.h>
+#include <xcb/xfixes.h>
+#include <xcb/xinerama.h>
 
 #include <obs-module.h>
 #include <util/dstr.h>
-#include "xcursor.h"
+#include "xcursor-xcb.h"
 #include "xhelpers.h"
 
 #define XSHM_DATA(voidptr) struct xshm_data *data = voidptr;
@@ -31,32 +32,25 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define blog(level, msg, ...) blog(level, "xshm-input: " msg, ##__VA_ARGS__)
 
 struct xshm_data {
-	/** The source object */
-	obs_source_t *source;
-	/** Xlib display object */
-	Display *dpy;
-	/** Xlib screen object */
-	Screen *screen;
-	/** user setting - the server name to capture from */
-	char *server;
-	/** user setting - the id of the screen that should be captured */
-	uint_fast32_t screen_id;
-	/** root coordinates for the capture */
-	int_fast32_t x_org, y_org;
-	/** size for the capture */
-	int_fast32_t width, height;
-	/** shared memory management object */
-	xshm_t *xshm;
-	/** the texture used to display the capture */
-	gs_texture_t *texture;
-	/** cursor object for displaying the server */
-	xcursor_t *cursor;
-	/** user setting - if cursor should be displayed  */
-	bool show_cursor;
-	/** set if xinerama is available and active on the screen */
-	bool use_xinerama;
-	/** user setting - if advanced settings should be displayed */
-	bool advanced;
+	obs_source_t     *source;
+
+	xcb_connection_t *xcb;
+	xcb_screen_t     *xcb_screen;
+	xcb_shm_t        *xshm;
+	xcb_xcursor_t    *cursor;
+
+	char             *server;
+	uint_fast32_t    screen_id;
+	int_fast32_t     x_org;
+	int_fast32_t     y_org;
+	int_fast32_t     width;
+	int_fast32_t     height;
+
+	gs_texture_t     *texture;
+
+	bool             show_cursor;
+	bool             use_xinerama;
+	bool             advanced;
 };
 
 /**
@@ -74,6 +68,24 @@ static inline void xshm_resize_texture(struct xshm_data *data)
 		GS_BGRA, 1, NULL, GS_DYNAMIC);
 }
 
+/**
+ * Check if the xserver supports all the extensions we need
+ */
+static bool xshm_check_extensions(xcb_connection_t *xcb)
+{
+	bool ok = true;
+
+	if (!xcb_get_extension_data(xcb, &xcb_shm_id)->present) {
+		blog(LOG_ERROR, "Missing SHM extension !");
+		ok = false;
+	}
+
+	if (!xcb_get_extension_data(xcb, &xcb_xinerama_id)->present)
+		blog(LOG_INFO, "Missing Xinerama extension !");
+
+	return ok;
+}
+
 /**
  * Update the capture
  *
@@ -85,21 +97,21 @@ static int_fast32_t xshm_update_geometry(struct xshm_data *data)
 	int_fast32_t old_height = data->height;
 
 	if (data->use_xinerama) {
-		if (xinerama_screen_geo(data->dpy, data->screen_id,
+		if (xinerama_screen_geo(data->xcb, data->screen_id,
 			&data->x_org, &data->y_org,
 			&data->width, &data->height) < 0) {
 			return -1;
 		}
-		data->screen = XDefaultScreenOfDisplay(data->dpy);
+		data->xcb_screen = xcb_get_screen(data->xcb, 0);
 	}
 	else {
 		data->x_org = 0;
 		data->y_org = 0;
-		if (x11_screen_geo(data->dpy, data->screen_id,
+		if (x11_screen_geo(data->xcb, data->screen_id,
 			&data->width, &data->height) < 0) {
 			return -1;
 		}
-		data->screen = XScreenOfDisplay(data->dpy, data->screen_id);
+		data->xcb_screen = xcb_get_screen(data->xcb, data->screen_id);
 	}
 
 	if (!data->width || !data->height) {
@@ -137,21 +149,20 @@ static void xshm_capture_stop(struct xshm_data *data)
 		data->texture = NULL;
 	}
 	if (data->cursor) {
-		xcursor_destroy(data->cursor);
+		xcb_xcursor_destroy(data->cursor);
 		data->cursor = NULL;
 	}
 
 	obs_leave_graphics();
 
 	if (data->xshm) {
-		xshm_detach(data->xshm);
+		xshm_xcb_detach(data->xshm);
 		data->xshm = NULL;
 	}
 
-	if (data->dpy) {
-		XSync(data->dpy, true);
-		XCloseDisplay(data->dpy);
-		data->dpy = NULL;
+	if (data->xcb) {
+		xcb_disconnect(data->xcb);
+		data->xcb = NULL;
 	}
 
 	if (data->server) {
@@ -168,35 +179,33 @@ static void xshm_capture_start(struct xshm_data *data)
 	const char *server = (data->advanced && *data->server)
 			? data->server : NULL;
 
-	data->dpy = XOpenDisplay(server);
-	if (!data->dpy) {
+	data->xcb = xcb_connect(server, NULL);
+	if (!data->xcb || xcb_connection_has_error(data->xcb)) {
 		blog(LOG_ERROR, "Unable to open X display !");
 		goto fail;
 	}
 
-	if (!XShmQueryExtension(data->dpy)) {
-		blog(LOG_ERROR, "XShm extension not found !");
+	if (!xshm_check_extensions(data->xcb))
 		goto fail;
-	}
 
-	data->use_xinerama = xinerama_is_active(data->dpy) ? true : false;
+	data->use_xinerama = xinerama_is_active(data->xcb) ? true : false;
 
 	if (xshm_update_geometry(data) < 0) {
 		blog(LOG_ERROR, "failed to update geometry !");
 		goto fail;
 	}
 
-	data->xshm = xshm_attach(data->dpy, data->screen,
-		data->width, data->height);
+	data->xshm = xshm_xcb_attach(data->xcb, data->width, data->height);
 	if (!data->xshm) {
 		blog(LOG_ERROR, "failed to attach shm !");
 		goto fail;
 	}
 
+	data->cursor = xcb_xcursor_init(data->xcb);
+	xcb_xcursor_offset(data->cursor, data->x_org, data->y_org);
+
 	obs_enter_graphics();
 
-	data->cursor = xcursor_init(data->dpy);
-	xcursor_offset(data->cursor, data->x_org, data->y_org);
 	xshm_resize_texture(data);
 
 	obs_leave_graphics();
@@ -269,26 +278,27 @@ static bool xshm_server_changed(obs_properties_t *props,
 
 	obs_property_list_clear(screens);
 
-	Display *dpy = XOpenDisplay(server);
-	if (!dpy) {
+	xcb_connection_t *xcb = xcb_connect(server, NULL);
+	if (!xcb || xcb_connection_has_error(xcb)) {
 		obs_property_set_enabled(screens, false);
 		return true;
 	}
 
 	struct dstr screen_info;
 	dstr_init(&screen_info);
-	bool xinerama = xinerama_is_active(dpy);
+	bool xinerama = xinerama_is_active(xcb);
 	int_fast32_t count = (xinerama) ?
-			xinerama_screen_count(dpy) : XScreenCount(dpy);
+			xinerama_screen_count(xcb) :
+			xcb_setup_roots_length(xcb_get_setup(xcb));
 
 	for (int_fast32_t i = 0; i < count; ++i) {
 		int_fast32_t x, y, w, h;
 		x = y = w = h = 0;
 
 		if (xinerama)
-			xinerama_screen_geo(dpy, i, &x, &y, &w, &h);
+			xinerama_screen_geo(xcb, i, &x, &y, &w, &h);
 		else
-			x11_screen_geo(dpy, i, &w, &h);
+			x11_screen_geo(xcb, i, &w, &h);
 
 		dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32
 				"x%"PRIuFAST32" @ %"PRIuFAST32
@@ -309,7 +319,7 @@ static bool xshm_server_changed(obs_properties_t *props,
 
 	dstr_free(&screen_info);
 
-	XCloseDisplay(dpy);
+	xcb_disconnect(xcb);
 	obs_property_set_enabled(screens, true);
 
 	return true;
@@ -383,16 +393,33 @@ static void xshm_video_tick(void *vptr, float seconds)
 	if (!data->texture)
 		return;
 
+	xcb_shm_get_image_cookie_t           img_c;
+	xcb_shm_get_image_reply_t            *img_r;
+	xcb_xfixes_get_cursor_image_cookie_t cur_c;
+	xcb_xfixes_get_cursor_image_reply_t  *cur_r;
+
+	img_c = xcb_shm_get_image_unchecked(data->xcb, data->xcb_screen->root,
+			data->x_org, data->y_org, data->width, data->height,
+			~0, XCB_IMAGE_FORMAT_Z_PIXMAP, data->xshm->seg, 0);
+	cur_c = xcb_xfixes_get_cursor_image_unchecked(data->xcb);
+
+	img_r = xcb_shm_get_image_reply(data->xcb, img_c, NULL);
+	cur_r = xcb_xfixes_get_cursor_image_reply(data->xcb, cur_c, NULL);
+
+	if (!img_r)
+		goto exit;
+
 	obs_enter_graphics();
 
-	XShmGetImage(data->dpy, XRootWindowOfScreen(data->screen),
-		data->xshm->image, data->x_org, data->y_org, AllPlanes);
-	gs_texture_set_image(data->texture, (void *) data->xshm->image->data,
+	gs_texture_set_image(data->texture, (void *) data->xshm->data,
 		data->width * 4, false);
-
-	xcursor_tick(data->cursor);
+	xcb_xcursor_update(data->cursor, cur_r);
 
 	obs_leave_graphics();
+
+exit:
+	free(img_r);
+	free(cur_r);
 }
 
 /**
@@ -412,7 +439,7 @@ static void xshm_video_render(void *vptr, gs_effect_t *effect)
 	gs_draw_sprite(data->texture, 0, 0, 0);
 
 	if (data->show_cursor)
-		xcursor_render(data->cursor);
+		xcb_xcursor_render(data->cursor);
 
 	gs_reset_blend_state();
 }