xshm-input.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. Copyright (C) 2014 by Leonhard Oelke <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <inttypes.h>
  17. #include <X11/Xlib.h>
  18. #include <X11/Xutil.h>
  19. #include <obs.h>
  20. #include "xcursor.h"
  21. #include "xhelpers.h"
  22. #define XSHM_DATA(voidptr) struct xshm_data *data = voidptr;
  23. struct xshm_data {
  24. Display *dpy;
  25. Screen *screen;
  26. int_fast32_t x_org, y_org;
  27. int_fast32_t width, height;
  28. xshm_t *xshm;
  29. texture_t texture;
  30. bool show_cursor;
  31. xcursor_t *cursor;
  32. bool use_xinerama;
  33. };
  34. /**
  35. * Resize the texture
  36. *
  37. * This will automatically create the texture if it does not exist
  38. */
  39. static void xshm_resize_texture(struct xshm_data *data)
  40. {
  41. gs_entercontext(obs_graphics());
  42. if (data->texture)
  43. texture_destroy(data->texture);
  44. data->texture = gs_create_texture(data->width, data->height,
  45. GS_BGRA, 1, NULL, GS_DYNAMIC);
  46. gs_leavecontext();
  47. }
  48. /**
  49. * Update the capture
  50. *
  51. * @return < 0 on error, 0 when size is unchanged, > 1 on size change
  52. */
  53. static int_fast32_t xshm_update_geometry(struct xshm_data *data,
  54. obs_data_t settings)
  55. {
  56. int_fast32_t old_width = data->width;
  57. int_fast32_t old_height = data->height;
  58. int_fast32_t screen = obs_data_getint(settings, "screen");
  59. if (data->use_xinerama) {
  60. if (xinerama_screen_geo(data->dpy, screen,
  61. &data->x_org, &data->y_org,
  62. &data->width, &data->height) < 0) {
  63. return -1;
  64. }
  65. data->screen = XDefaultScreenOfDisplay(data->dpy);
  66. }
  67. else {
  68. data->x_org = 0;
  69. data->y_org = 0;
  70. if (x11_screen_geo(data->dpy, screen,
  71. &data->width, &data->height) < 0) {
  72. return -1;
  73. }
  74. data->screen = XScreenOfDisplay(data->dpy, screen);
  75. }
  76. if (!data->width || !data->height) {
  77. blog(LOG_ERROR, "xshm-input: Failed to get geometry");
  78. return -1;
  79. }
  80. blog(LOG_INFO, "xshm-input: Geometry %"PRIdFAST32"x%"PRIdFAST32
  81. " @ %"PRIdFAST32",%"PRIdFAST32,
  82. data->width, data->height, data->x_org, data->y_org);
  83. if (old_width == data->width && old_height == data->height)
  84. return 0;
  85. return 1;
  86. }
  87. /**
  88. * Returns the name of the plugin
  89. */
  90. static const char* xshm_getname(const char* locale)
  91. {
  92. UNUSED_PARAMETER(locale);
  93. return "X11 Shared Memory Screen Input";
  94. }
  95. /**
  96. * Update the capture with changed settings
  97. */
  98. static void xshm_update(void *vptr, obs_data_t settings)
  99. {
  100. XSHM_DATA(vptr);
  101. data->show_cursor = obs_data_getbool(settings, "show_cursor");
  102. if (data->xshm)
  103. xshm_detach(data->xshm);
  104. if (xshm_update_geometry(data, settings) < 0) {
  105. blog(LOG_ERROR, "xshm-input: failed to update geometry !");
  106. return;
  107. }
  108. xshm_resize_texture(data);
  109. xcursor_offset(data->cursor, data->x_org, data->y_org);
  110. data->xshm = xshm_attach(data->dpy, data->screen,
  111. data->width, data->height);
  112. if (!data->xshm) {
  113. blog(LOG_ERROR, "xshm-input: failed to attach shm !");
  114. return;
  115. }
  116. }
  117. /**
  118. * Get the default settings for the capture
  119. */
  120. static void xshm_defaults(obs_data_t defaults)
  121. {
  122. obs_data_set_default_int(defaults, "screen", 0);
  123. obs_data_setbool(defaults, "show_cursor", true);
  124. }
  125. /**
  126. * Get the properties for the capture
  127. */
  128. static obs_properties_t xshm_properties(const char *locale)
  129. {
  130. obs_properties_t props = obs_properties_create(locale);
  131. int_fast32_t screen_max;
  132. Display *dpy = XOpenDisplay(NULL);
  133. screen_max = xinerama_is_active(dpy)
  134. ? xinerama_screen_count(dpy)
  135. : XScreenCount(dpy);
  136. screen_max = (screen_max) ? screen_max - 1 : 0;
  137. XCloseDisplay(dpy);
  138. obs_properties_add_int(props, "screen", "Screen", 0, screen_max, 1);
  139. obs_properties_add_bool(props, "show_cursor", "Capture Cursor");
  140. return props;
  141. }
  142. /**
  143. * Destroy the capture
  144. */
  145. static void xshm_destroy(void *vptr)
  146. {
  147. XSHM_DATA(vptr);
  148. if (!data)
  149. return;
  150. gs_entercontext(obs_graphics());
  151. if (data->texture)
  152. texture_destroy(data->texture);
  153. if (data->cursor)
  154. xcursor_destroy(data->cursor);
  155. gs_leavecontext();
  156. if (data->xshm)
  157. xshm_detach(data->xshm);
  158. if (data->dpy)
  159. XCloseDisplay(data->dpy);
  160. bfree(data);
  161. }
  162. /**
  163. * Create the capture
  164. */
  165. static void *xshm_create(obs_data_t settings, obs_source_t source)
  166. {
  167. UNUSED_PARAMETER(source);
  168. struct xshm_data *data = bzalloc(sizeof(struct xshm_data));
  169. data->dpy = XOpenDisplay(NULL);
  170. if (!data->dpy) {
  171. blog(LOG_ERROR, "xshm-input: Unable to open X display !");
  172. goto fail;
  173. }
  174. if (!XShmQueryExtension(data->dpy)) {
  175. blog(LOG_ERROR, "xshm-input: XShm extension not found !");
  176. goto fail;
  177. }
  178. data->use_xinerama = xinerama_is_active(data->dpy) ? true : false;
  179. gs_entercontext(obs_graphics());
  180. data->cursor = xcursor_init(data->dpy);
  181. gs_leavecontext();
  182. xshm_update(data, settings);
  183. return data;
  184. fail:
  185. xshm_destroy(data);
  186. return NULL;
  187. }
  188. /**
  189. * Prepare the capture data
  190. */
  191. static void xshm_video_tick(void *vptr, float seconds)
  192. {
  193. UNUSED_PARAMETER(seconds);
  194. XSHM_DATA(vptr);
  195. if (!data->xshm)
  196. return;
  197. gs_entercontext(obs_graphics());
  198. XShmGetImage(data->dpy, XRootWindowOfScreen(data->screen),
  199. data->xshm->image, data->x_org, data->y_org, AllPlanes);
  200. texture_setimage(data->texture, (void *) data->xshm->image->data,
  201. data->width * 4, false);
  202. xcursor_tick(data->cursor);
  203. gs_leavecontext();
  204. }
  205. /**
  206. * Render the capture data
  207. */
  208. static void xshm_video_render(void *vptr, effect_t effect)
  209. {
  210. XSHM_DATA(vptr);
  211. if (!data->xshm)
  212. return;
  213. eparam_t image = effect_getparambyname(effect, "image");
  214. effect_settexture(effect, image, data->texture);
  215. gs_enable_blending(false);
  216. gs_draw_sprite(data->texture, 0, 0, 0);
  217. if (data->show_cursor)
  218. xcursor_render(data->cursor);
  219. }
  220. /**
  221. * Width of the captured data
  222. */
  223. static uint32_t xshm_getwidth(void *vptr)
  224. {
  225. XSHM_DATA(vptr);
  226. return data->width;
  227. }
  228. /**
  229. * Height of the captured data
  230. */
  231. static uint32_t xshm_getheight(void *vptr)
  232. {
  233. XSHM_DATA(vptr);
  234. return data->height;
  235. }
  236. struct obs_source_info xshm_input = {
  237. .id = "xshm_input",
  238. .type = OBS_SOURCE_TYPE_INPUT,
  239. .output_flags = OBS_SOURCE_VIDEO,
  240. .getname = xshm_getname,
  241. .create = xshm_create,
  242. .destroy = xshm_destroy,
  243. .update = xshm_update,
  244. .defaults = xshm_defaults,
  245. .properties = xshm_properties,
  246. .video_tick = xshm_video_tick,
  247. .video_render = xshm_video_render,
  248. .getwidth = xshm_getwidth,
  249. .getheight = xshm_getheight
  250. };