dbus-listener.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. /*
  2. * QEMU DBus display console
  3. *
  4. * Copyright (c) 2021 Marc-André Lureau <marcandre.lureau@redhat.com>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include "qemu/error-report.h"
  26. #include "qapi/error.h"
  27. #include "system/system.h"
  28. #include "dbus.h"
  29. #include "glib.h"
  30. #ifdef G_OS_UNIX
  31. #include <gio/gunixfdlist.h>
  32. #endif
  33. #ifdef WIN32
  34. #include <d3d11.h>
  35. #include <dxgi1_2.h>
  36. #endif
  37. #ifdef CONFIG_OPENGL
  38. #include "ui/shader.h"
  39. #include "ui/egl-helpers.h"
  40. #include "ui/egl-context.h"
  41. #include "ui/qemu-pixman.h"
  42. #endif
  43. #include "trace.h"
  44. static void dbus_gfx_switch(DisplayChangeListener *dcl,
  45. struct DisplaySurface *new_surface);
  46. enum share_kind {
  47. SHARE_KIND_NONE,
  48. SHARE_KIND_MAPPED,
  49. SHARE_KIND_D3DTEX,
  50. };
  51. struct _DBusDisplayListener {
  52. GObject parent;
  53. char *bus_name;
  54. DBusDisplayConsole *console;
  55. GDBusConnection *conn;
  56. QemuDBusDisplay1Listener *proxy;
  57. #ifdef CONFIG_PIXMAN
  58. /* Keep track of the damage region */
  59. pixman_region32_t gl_damage;
  60. #else
  61. int gl_damage;
  62. #endif
  63. DisplayChangeListener dcl;
  64. DisplaySurface *ds;
  65. enum share_kind ds_share;
  66. bool ds_mapped;
  67. bool can_share_map;
  68. #ifdef WIN32
  69. QemuDBusDisplay1ListenerWin32Map *map_proxy;
  70. QemuDBusDisplay1ListenerWin32D3d11 *d3d11_proxy;
  71. HANDLE peer_process;
  72. ID3D11Texture2D *d3d_texture;
  73. #ifdef CONFIG_OPENGL
  74. egl_fb fb;
  75. #endif
  76. #else /* !WIN32 */
  77. QemuDBusDisplay1ListenerUnixMap *map_proxy;
  78. #endif
  79. guint dbus_filter;
  80. guint32 display_serial_to_discard;
  81. guint32 cursor_serial_to_discard;
  82. };
  83. G_DEFINE_TYPE(DBusDisplayListener, dbus_display_listener, G_TYPE_OBJECT)
  84. static void dbus_gfx_update(DisplayChangeListener *dcl,
  85. int x, int y, int w, int h);
  86. static void ddl_discard_display_messages(DBusDisplayListener *ddl)
  87. {
  88. guint32 serial = g_dbus_connection_get_last_serial(
  89. g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)));
  90. g_atomic_int_set(&ddl->display_serial_to_discard, serial);
  91. }
  92. static void ddl_discard_cursor_messages(DBusDisplayListener *ddl)
  93. {
  94. guint32 serial = g_dbus_connection_get_last_serial(
  95. g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy)));
  96. g_atomic_int_set(&ddl->cursor_serial_to_discard, serial);
  97. }
  98. #ifdef CONFIG_OPENGL
  99. static void dbus_scanout_disable(DisplayChangeListener *dcl)
  100. {
  101. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  102. ddl_discard_display_messages(ddl);
  103. qemu_dbus_display1_listener_call_disable(
  104. ddl->proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  105. }
  106. #ifdef WIN32
  107. static bool d3d_texture2d_share(ID3D11Texture2D *d3d_texture,
  108. HANDLE *handle, Error **errp)
  109. {
  110. IDXGIResource1 *dxgiResource = NULL;
  111. HRESULT hr;
  112. hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
  113. &IID_IDXGIResource1,
  114. (void **)&dxgiResource);
  115. if (FAILED(hr)) {
  116. goto fail;
  117. }
  118. hr = dxgiResource->lpVtbl->CreateSharedHandle(
  119. dxgiResource,
  120. NULL,
  121. DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE,
  122. NULL,
  123. handle
  124. );
  125. dxgiResource->lpVtbl->Release(dxgiResource);
  126. if (SUCCEEDED(hr)) {
  127. return true;
  128. }
  129. fail:
  130. error_setg_win32(errp, GetLastError(), "failed to create shared handle");
  131. return false;
  132. }
  133. static bool d3d_texture2d_acquire0(ID3D11Texture2D *d3d_texture, Error **errp)
  134. {
  135. IDXGIKeyedMutex *dxgiMutex = NULL;
  136. HRESULT hr;
  137. hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
  138. &IID_IDXGIKeyedMutex,
  139. (void **)&dxgiMutex);
  140. if (FAILED(hr)) {
  141. goto fail;
  142. }
  143. hr = dxgiMutex->lpVtbl->AcquireSync(dxgiMutex, 0, INFINITE);
  144. dxgiMutex->lpVtbl->Release(dxgiMutex);
  145. if (SUCCEEDED(hr)) {
  146. return true;
  147. }
  148. fail:
  149. error_setg_win32(errp, GetLastError(), "failed to acquire texture mutex");
  150. return false;
  151. }
  152. static bool d3d_texture2d_release0(ID3D11Texture2D *d3d_texture, Error **errp)
  153. {
  154. IDXGIKeyedMutex *dxgiMutex = NULL;
  155. HRESULT hr;
  156. hr = d3d_texture->lpVtbl->QueryInterface(d3d_texture,
  157. &IID_IDXGIKeyedMutex,
  158. (void **)&dxgiMutex);
  159. if (FAILED(hr)) {
  160. goto fail;
  161. }
  162. hr = dxgiMutex->lpVtbl->ReleaseSync(dxgiMutex, 0);
  163. dxgiMutex->lpVtbl->Release(dxgiMutex);
  164. if (SUCCEEDED(hr)) {
  165. return true;
  166. }
  167. fail:
  168. error_setg_win32(errp, GetLastError(), "failed to release texture mutex");
  169. return false;
  170. }
  171. #endif /* WIN32 */
  172. #if defined(CONFIG_GBM) || defined(WIN32)
  173. static void dbus_update_gl_cb(GObject *source_object,
  174. GAsyncResult *res,
  175. gpointer user_data)
  176. {
  177. g_autoptr(GError) err = NULL;
  178. DBusDisplayListener *ddl = user_data;
  179. bool success;
  180. #ifdef CONFIG_GBM
  181. success = qemu_dbus_display1_listener_call_update_dmabuf_finish(
  182. ddl->proxy, res, &err);
  183. #endif
  184. #ifdef WIN32
  185. success = qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d_finish(
  186. ddl->d3d11_proxy, res, &err);
  187. d3d_texture2d_acquire0(ddl->d3d_texture, &error_warn);
  188. #endif
  189. if (!success) {
  190. error_report("Failed to call update: %s", err->message);
  191. }
  192. graphic_hw_gl_block(ddl->dcl.con, false);
  193. g_object_unref(ddl);
  194. }
  195. #endif
  196. static void dbus_call_update_gl(DisplayChangeListener *dcl,
  197. int x, int y, int w, int h)
  198. {
  199. #if defined(CONFIG_GBM) || defined(WIN32)
  200. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  201. #endif
  202. trace_dbus_update_gl(x, y, w, h);
  203. glFlush();
  204. #ifdef CONFIG_GBM
  205. graphic_hw_gl_block(ddl->dcl.con, true);
  206. qemu_dbus_display1_listener_call_update_dmabuf(ddl->proxy,
  207. x, y, w, h,
  208. G_DBUS_CALL_FLAGS_NONE,
  209. DBUS_DEFAULT_TIMEOUT, NULL,
  210. dbus_update_gl_cb,
  211. g_object_ref(ddl));
  212. #endif
  213. #ifdef WIN32
  214. switch (ddl->ds_share) {
  215. case SHARE_KIND_MAPPED:
  216. egl_fb_read_rect(ddl->ds, &ddl->fb, x, y, w, h);
  217. dbus_gfx_update(dcl, x, y, w, h);
  218. break;
  219. case SHARE_KIND_D3DTEX: {
  220. Error *err = NULL;
  221. assert(ddl->d3d_texture);
  222. graphic_hw_gl_block(ddl->dcl.con, true);
  223. if (!d3d_texture2d_release0(ddl->d3d_texture, &err)) {
  224. error_report_err(err);
  225. return;
  226. }
  227. qemu_dbus_display1_listener_win32_d3d11_call_update_texture2d(
  228. ddl->d3d11_proxy,
  229. x, y, w, h,
  230. G_DBUS_CALL_FLAGS_NONE,
  231. DBUS_DEFAULT_TIMEOUT, NULL,
  232. dbus_update_gl_cb,
  233. g_object_ref(ddl));
  234. break;
  235. }
  236. default:
  237. g_warn_if_reached();
  238. }
  239. #endif
  240. }
  241. #ifdef CONFIG_GBM
  242. static void dbus_scanout_dmabuf(DisplayChangeListener *dcl,
  243. QemuDmaBuf *dmabuf)
  244. {
  245. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  246. g_autoptr(GError) err = NULL;
  247. g_autoptr(GUnixFDList) fd_list = NULL;
  248. int fd;
  249. uint32_t width, height, stride, fourcc;
  250. uint64_t modifier;
  251. bool y0_top;
  252. fd = qemu_dmabuf_get_fd(dmabuf);
  253. fd_list = g_unix_fd_list_new();
  254. if (g_unix_fd_list_append(fd_list, fd, &err) != 0) {
  255. error_report("Failed to setup dmabuf fdlist: %s", err->message);
  256. return;
  257. }
  258. ddl_discard_display_messages(ddl);
  259. width = qemu_dmabuf_get_width(dmabuf);
  260. height = qemu_dmabuf_get_height(dmabuf);
  261. stride = qemu_dmabuf_get_stride(dmabuf);
  262. fourcc = qemu_dmabuf_get_fourcc(dmabuf);
  263. modifier = qemu_dmabuf_get_modifier(dmabuf);
  264. y0_top = qemu_dmabuf_get_y0_top(dmabuf);
  265. /* FIXME: add missing x/y/w/h support */
  266. qemu_dbus_display1_listener_call_scanout_dmabuf(
  267. ddl->proxy, g_variant_new_handle(0),
  268. width, height, stride, fourcc, modifier,
  269. y0_top, G_DBUS_CALL_FLAGS_NONE,
  270. -1, fd_list, NULL, NULL, NULL);
  271. }
  272. #endif /* GBM */
  273. #endif /* OPENGL */
  274. #ifdef WIN32
  275. static bool dbus_scanout_map(DBusDisplayListener *ddl)
  276. {
  277. g_autoptr(GError) err = NULL;
  278. BOOL success;
  279. HANDLE target_handle;
  280. if (ddl->ds_share == SHARE_KIND_MAPPED) {
  281. return true;
  282. }
  283. if (!ddl->can_share_map || !ddl->ds->share_handle) {
  284. return false;
  285. }
  286. success = DuplicateHandle(
  287. GetCurrentProcess(),
  288. ddl->ds->share_handle,
  289. ddl->peer_process,
  290. &target_handle,
  291. FILE_MAP_READ | SECTION_QUERY,
  292. FALSE, 0);
  293. if (!success) {
  294. g_autofree char *msg = g_win32_error_message(GetLastError());
  295. g_debug("Failed to DuplicateHandle: %s", msg);
  296. ddl->can_share_map = false;
  297. return false;
  298. }
  299. ddl_discard_display_messages(ddl);
  300. if (!qemu_dbus_display1_listener_win32_map_call_scanout_map_sync(
  301. ddl->map_proxy,
  302. GPOINTER_TO_UINT(target_handle),
  303. ddl->ds->share_handle_offset,
  304. surface_width(ddl->ds),
  305. surface_height(ddl->ds),
  306. surface_stride(ddl->ds),
  307. surface_format(ddl->ds),
  308. G_DBUS_CALL_FLAGS_NONE,
  309. DBUS_DEFAULT_TIMEOUT,
  310. NULL,
  311. &err)) {
  312. g_debug("Failed to call ScanoutMap: %s", err->message);
  313. ddl->can_share_map = false;
  314. return false;
  315. }
  316. ddl->ds_share = SHARE_KIND_MAPPED;
  317. return true;
  318. }
  319. #ifdef CONFIG_OPENGL
  320. static bool
  321. dbus_scanout_share_d3d_texture(
  322. DBusDisplayListener *ddl,
  323. ID3D11Texture2D *tex,
  324. bool backing_y_0_top,
  325. uint32_t backing_width,
  326. uint32_t backing_height,
  327. uint32_t x, uint32_t y,
  328. uint32_t w, uint32_t h)
  329. {
  330. Error *err = NULL;
  331. BOOL success;
  332. HANDLE share_handle, target_handle;
  333. if (!d3d_texture2d_release0(tex, &err)) {
  334. error_report_err(err);
  335. return false;
  336. }
  337. if (!d3d_texture2d_share(tex, &share_handle, &err)) {
  338. error_report_err(err);
  339. return false;
  340. }
  341. success = DuplicateHandle(
  342. GetCurrentProcess(),
  343. share_handle,
  344. ddl->peer_process,
  345. &target_handle,
  346. 0,
  347. FALSE, DUPLICATE_SAME_ACCESS);
  348. if (!success) {
  349. g_autofree char *msg = g_win32_error_message(GetLastError());
  350. g_debug("Failed to DuplicateHandle: %s", msg);
  351. CloseHandle(share_handle);
  352. return false;
  353. }
  354. ddl_discard_display_messages(ddl);
  355. qemu_dbus_display1_listener_win32_d3d11_call_scanout_texture2d(
  356. ddl->d3d11_proxy,
  357. GPOINTER_TO_INT(target_handle),
  358. backing_width,
  359. backing_height,
  360. backing_y_0_top,
  361. x, y, w, h,
  362. G_DBUS_CALL_FLAGS_NONE,
  363. -1,
  364. NULL, NULL, NULL);
  365. CloseHandle(share_handle);
  366. if (!d3d_texture2d_acquire0(tex, &err)) {
  367. error_report_err(err);
  368. return false;
  369. }
  370. ddl->d3d_texture = tex;
  371. ddl->ds_share = SHARE_KIND_D3DTEX;
  372. return true;
  373. }
  374. #endif /* CONFIG_OPENGL */
  375. #else /* !WIN32 */
  376. static bool dbus_scanout_map(DBusDisplayListener *ddl)
  377. {
  378. g_autoptr(GError) err = NULL;
  379. g_autoptr(GUnixFDList) fd_list = NULL;
  380. if (ddl->ds_share == SHARE_KIND_MAPPED) {
  381. return true;
  382. }
  383. if (!ddl->can_share_map || ddl->ds->share_handle == SHAREABLE_NONE) {
  384. return false;
  385. }
  386. ddl_discard_display_messages(ddl);
  387. fd_list = g_unix_fd_list_new();
  388. if (g_unix_fd_list_append(fd_list, ddl->ds->share_handle, &err) != 0) {
  389. g_debug("Failed to setup scanout map fdlist: %s", err->message);
  390. ddl->can_share_map = false;
  391. return false;
  392. }
  393. if (!qemu_dbus_display1_listener_unix_map_call_scanout_map_sync(
  394. ddl->map_proxy,
  395. g_variant_new_handle(0),
  396. ddl->ds->share_handle_offset,
  397. surface_width(ddl->ds),
  398. surface_height(ddl->ds),
  399. surface_stride(ddl->ds),
  400. surface_format(ddl->ds),
  401. G_DBUS_CALL_FLAGS_NONE,
  402. DBUS_DEFAULT_TIMEOUT,
  403. fd_list,
  404. NULL,
  405. NULL,
  406. &err)) {
  407. g_debug("Failed to call ScanoutMap: %s", err->message);
  408. ddl->can_share_map = false;
  409. return false;
  410. }
  411. ddl->ds_share = SHARE_KIND_MAPPED;
  412. return true;
  413. }
  414. #endif /* WIN32 */
  415. #ifdef CONFIG_OPENGL
  416. static void dbus_scanout_borrowed_texture(DisplayChangeListener *dcl,
  417. uint32_t tex_id,
  418. bool backing_y_0_top,
  419. uint32_t backing_width,
  420. uint32_t backing_height,
  421. uint32_t x, uint32_t y,
  422. uint32_t w, uint32_t h,
  423. void *d3d_tex2d)
  424. {
  425. trace_dbus_scanout_texture(tex_id, backing_y_0_top,
  426. backing_width, backing_height, x, y, w, h);
  427. #ifdef CONFIG_GBM
  428. g_autoptr(QemuDmaBuf) dmabuf = NULL;
  429. int fd;
  430. uint32_t stride, fourcc;
  431. uint64_t modifier;
  432. assert(tex_id);
  433. fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc,
  434. &modifier);
  435. if (fd < 0) {
  436. error_report("%s: failed to get fd for texture", __func__);
  437. return;
  438. }
  439. dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width,
  440. backing_height, fourcc, modifier, fd,
  441. false, backing_y_0_top);
  442. dbus_scanout_dmabuf(dcl, dmabuf);
  443. qemu_dmabuf_close(dmabuf);
  444. #endif
  445. #ifdef WIN32
  446. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  447. /* there must be a matching gfx_switch before */
  448. assert(surface_width(ddl->ds) == w);
  449. assert(surface_height(ddl->ds) == h);
  450. if (d3d_tex2d) {
  451. dbus_scanout_share_d3d_texture(ddl, d3d_tex2d, backing_y_0_top,
  452. backing_width, backing_height, x, y, w, h);
  453. } else {
  454. dbus_scanout_map(ddl);
  455. egl_fb_setup_for_tex(&ddl->fb, backing_width, backing_height, tex_id, false);
  456. }
  457. #endif
  458. }
  459. static void dbus_scanout_texture(DisplayChangeListener *dcl,
  460. uint32_t backing_id,
  461. DisplayGLTextureBorrower backing_borrow,
  462. uint32_t x, uint32_t y,
  463. uint32_t w, uint32_t h)
  464. {
  465. bool backing_y_0_top;
  466. uint32_t backing_width;
  467. uint32_t backing_height;
  468. void *d3d_tex2d;
  469. uint32_t tex_id = backing_borrow(backing_id, &backing_y_0_top,
  470. &backing_width, &backing_height,
  471. &d3d_tex2d);
  472. dbus_scanout_borrowed_texture(dcl, tex_id, backing_y_0_top,
  473. backing_width, backing_height,
  474. x, y, w, h, d3d_tex2d);
  475. }
  476. #ifdef CONFIG_GBM
  477. static void dbus_cursor_dmabuf(DisplayChangeListener *dcl,
  478. QemuDmaBuf *dmabuf, bool have_hot,
  479. uint32_t hot_x, uint32_t hot_y)
  480. {
  481. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  482. DisplaySurface *ds;
  483. GVariant *v_data = NULL;
  484. egl_fb cursor_fb = EGL_FB_INIT;
  485. uint32_t width, height, texture;
  486. if (!dmabuf) {
  487. qemu_dbus_display1_listener_call_mouse_set(
  488. ddl->proxy, 0, 0, false,
  489. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  490. return;
  491. }
  492. ddl_discard_cursor_messages(ddl);
  493. egl_dmabuf_import_texture(dmabuf);
  494. texture = qemu_dmabuf_get_texture(dmabuf);
  495. if (!texture) {
  496. return;
  497. }
  498. width = qemu_dmabuf_get_width(dmabuf);
  499. height = qemu_dmabuf_get_height(dmabuf);
  500. egl_fb_setup_for_tex(&cursor_fb, width, height, texture, false);
  501. ds = qemu_create_displaysurface(width, height);
  502. egl_fb_read(ds, &cursor_fb);
  503. v_data = g_variant_new_from_data(
  504. G_VARIANT_TYPE("ay"),
  505. surface_data(ds),
  506. surface_width(ds) * surface_height(ds) * 4,
  507. TRUE,
  508. (GDestroyNotify)qemu_free_displaysurface,
  509. ds);
  510. qemu_dbus_display1_listener_call_cursor_define(
  511. ddl->proxy,
  512. surface_width(ds),
  513. surface_height(ds),
  514. hot_x,
  515. hot_y,
  516. v_data,
  517. G_DBUS_CALL_FLAGS_NONE,
  518. -1,
  519. NULL,
  520. NULL,
  521. NULL);
  522. }
  523. static void dbus_release_dmabuf(DisplayChangeListener *dcl,
  524. QemuDmaBuf *dmabuf)
  525. {
  526. dbus_scanout_disable(dcl);
  527. }
  528. #endif /* GBM */
  529. static void dbus_gl_cursor_position(DisplayChangeListener *dcl,
  530. uint32_t pos_x, uint32_t pos_y)
  531. {
  532. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  533. qemu_dbus_display1_listener_call_mouse_set(
  534. ddl->proxy, pos_x, pos_y, true,
  535. G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  536. }
  537. static void dbus_scanout_update(DisplayChangeListener *dcl,
  538. uint32_t x, uint32_t y,
  539. uint32_t w, uint32_t h)
  540. {
  541. dbus_call_update_gl(dcl, x, y, w, h);
  542. }
  543. static void dbus_gl_refresh(DisplayChangeListener *dcl)
  544. {
  545. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  546. graphic_hw_update(dcl->con);
  547. if (!ddl->ds || qemu_console_is_gl_blocked(ddl->dcl.con)) {
  548. return;
  549. }
  550. #ifdef CONFIG_PIXMAN
  551. int n_rects = pixman_region32_n_rects(&ddl->gl_damage);
  552. for (int i = 0; i < n_rects; i++) {
  553. pixman_box32_t *box;
  554. box = pixman_region32_rectangles(&ddl->gl_damage, NULL) + i;
  555. /* TODO: Add a UpdateList call to send multiple updates at once */
  556. dbus_call_update_gl(dcl, box->x1, box->y1,
  557. box->x2 - box->x1, box->y2 - box->y1);
  558. }
  559. pixman_region32_clear(&ddl->gl_damage);
  560. #else
  561. if (ddl->gl_damage) {
  562. dbus_call_update_gl(dcl, 0, 0,
  563. surface_width(ddl->ds), surface_height(ddl->ds));
  564. ddl->gl_damage = 0;
  565. }
  566. #endif
  567. }
  568. #endif /* OPENGL */
  569. static void dbus_refresh(DisplayChangeListener *dcl)
  570. {
  571. graphic_hw_update(dcl->con);
  572. }
  573. #ifdef CONFIG_OPENGL
  574. static void dbus_gl_gfx_update(DisplayChangeListener *dcl,
  575. int x, int y, int w, int h)
  576. {
  577. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  578. #ifdef CONFIG_PIXMAN
  579. pixman_region32_t rect_region;
  580. pixman_region32_init_rect(&rect_region, x, y, w, h);
  581. pixman_region32_union(&ddl->gl_damage, &ddl->gl_damage, &rect_region);
  582. pixman_region32_fini(&rect_region);
  583. #else
  584. ddl->gl_damage++;
  585. #endif
  586. }
  587. #endif
  588. static void dbus_gfx_update_sub(DBusDisplayListener *ddl,
  589. int x, int y, int w, int h)
  590. {
  591. pixman_image_t *img;
  592. size_t stride;
  593. GVariant *v_data;
  594. /* make a copy, since gvariant only handles linear data */
  595. stride = w * DIV_ROUND_UP(PIXMAN_FORMAT_BPP(surface_format(ddl->ds)), 8);
  596. img = pixman_image_create_bits(surface_format(ddl->ds),
  597. w, h, NULL, stride);
  598. #ifdef CONFIG_PIXMAN
  599. pixman_image_composite(PIXMAN_OP_SRC, ddl->ds->image, NULL, img,
  600. x, y, 0, 0, 0, 0, w, h);
  601. #else
  602. {
  603. uint8_t *src = (uint8_t *)pixman_image_get_data(ddl->ds->image);
  604. uint8_t *dst = (uint8_t *)pixman_image_get_data(img);
  605. int bp = PIXMAN_FORMAT_BPP(surface_format(ddl->ds)) / 8;
  606. int hh;
  607. for (hh = 0; hh < h; hh++) {
  608. memcpy(&dst[stride * hh],
  609. &src[surface_stride(ddl->ds) * (hh + y) + x * bp],
  610. stride);
  611. }
  612. }
  613. #endif
  614. v_data = g_variant_new_from_data(
  615. G_VARIANT_TYPE("ay"),
  616. pixman_image_get_data(img),
  617. pixman_image_get_stride(img) * h,
  618. TRUE,
  619. (GDestroyNotify)pixman_image_unref,
  620. img);
  621. qemu_dbus_display1_listener_call_update(ddl->proxy,
  622. x, y, w, h, pixman_image_get_stride(img), pixman_image_get_format(img),
  623. v_data,
  624. G_DBUS_CALL_FLAGS_NONE,
  625. DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
  626. }
  627. static void ddl_scanout(DBusDisplayListener *ddl)
  628. {
  629. GVariant *v_data;
  630. v_data = g_variant_new_from_data(
  631. G_VARIANT_TYPE("ay"), surface_data(ddl->ds),
  632. surface_stride(ddl->ds) * surface_height(ddl->ds), TRUE,
  633. (GDestroyNotify)pixman_image_unref, pixman_image_ref(ddl->ds->image));
  634. ddl_discard_display_messages(ddl);
  635. qemu_dbus_display1_listener_call_scanout(
  636. ddl->proxy, surface_width(ddl->ds), surface_height(ddl->ds),
  637. surface_stride(ddl->ds), surface_format(ddl->ds), v_data,
  638. G_DBUS_CALL_FLAGS_NONE, DBUS_DEFAULT_TIMEOUT, NULL, NULL,
  639. g_object_ref(ddl));
  640. }
  641. static void dbus_gfx_update(DisplayChangeListener *dcl,
  642. int x, int y, int w, int h)
  643. {
  644. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  645. assert(ddl->ds);
  646. trace_dbus_update(x, y, w, h);
  647. if (dbus_scanout_map(ddl)) {
  648. #ifdef WIN32
  649. qemu_dbus_display1_listener_win32_map_call_update_map(
  650. ddl->map_proxy,
  651. x, y, w, h,
  652. G_DBUS_CALL_FLAGS_NONE,
  653. DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
  654. #else
  655. qemu_dbus_display1_listener_unix_map_call_update_map(
  656. ddl->map_proxy,
  657. x, y, w, h,
  658. G_DBUS_CALL_FLAGS_NONE,
  659. DBUS_DEFAULT_TIMEOUT, NULL, NULL, NULL);
  660. #endif
  661. return;
  662. }
  663. if (x == 0 && y == 0 && w == surface_width(ddl->ds) && h == surface_height(ddl->ds)) {
  664. return ddl_scanout(ddl);
  665. }
  666. dbus_gfx_update_sub(ddl, x, y, w, h);
  667. }
  668. #ifdef CONFIG_OPENGL
  669. static void dbus_gl_gfx_switch(DisplayChangeListener *dcl,
  670. struct DisplaySurface *new_surface)
  671. {
  672. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  673. trace_dbus_gl_gfx_switch(new_surface);
  674. ddl->ds = new_surface;
  675. ddl->ds_share = SHARE_KIND_NONE;
  676. if (ddl->ds) {
  677. int width = surface_width(ddl->ds);
  678. int height = surface_height(ddl->ds);
  679. /* TODO: lazy send dmabuf (there are unnecessary sent otherwise) */
  680. dbus_scanout_borrowed_texture(&ddl->dcl, ddl->ds->texture, false,
  681. width, height, 0, 0, width, height, NULL);
  682. }
  683. }
  684. #endif
  685. static void dbus_gfx_switch(DisplayChangeListener *dcl,
  686. struct DisplaySurface *new_surface)
  687. {
  688. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  689. ddl->ds = new_surface;
  690. ddl->ds_share = SHARE_KIND_NONE;
  691. }
  692. static void dbus_mouse_set(DisplayChangeListener *dcl,
  693. int x, int y, bool on)
  694. {
  695. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  696. qemu_dbus_display1_listener_call_mouse_set(
  697. ddl->proxy, x, y, on, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
  698. }
  699. static void dbus_cursor_define(DisplayChangeListener *dcl,
  700. QEMUCursor *c)
  701. {
  702. DBusDisplayListener *ddl = container_of(dcl, DBusDisplayListener, dcl);
  703. GVariant *v_data = NULL;
  704. ddl_discard_cursor_messages(ddl);
  705. v_data = g_variant_new_from_data(
  706. G_VARIANT_TYPE("ay"),
  707. c->data,
  708. c->width * c->height * 4,
  709. TRUE,
  710. (GDestroyNotify)cursor_unref,
  711. cursor_ref(c));
  712. qemu_dbus_display1_listener_call_cursor_define(
  713. ddl->proxy,
  714. c->width,
  715. c->height,
  716. c->hot_x,
  717. c->hot_y,
  718. v_data,
  719. G_DBUS_CALL_FLAGS_NONE,
  720. -1,
  721. NULL,
  722. NULL,
  723. NULL);
  724. }
  725. #ifdef CONFIG_OPENGL
  726. const DisplayChangeListenerOps dbus_gl_dcl_ops = {
  727. .dpy_name = "dbus-gl",
  728. .dpy_gfx_update = dbus_gl_gfx_update,
  729. .dpy_gfx_switch = dbus_gl_gfx_switch,
  730. .dpy_gfx_check_format = console_gl_check_format,
  731. .dpy_refresh = dbus_gl_refresh,
  732. .dpy_mouse_set = dbus_mouse_set,
  733. .dpy_cursor_define = dbus_cursor_define,
  734. .dpy_gl_scanout_disable = dbus_scanout_disable,
  735. .dpy_gl_scanout_texture = dbus_scanout_texture,
  736. #ifdef CONFIG_GBM
  737. .dpy_gl_scanout_dmabuf = dbus_scanout_dmabuf,
  738. .dpy_gl_cursor_dmabuf = dbus_cursor_dmabuf,
  739. .dpy_gl_release_dmabuf = dbus_release_dmabuf,
  740. #endif
  741. .dpy_gl_cursor_position = dbus_gl_cursor_position,
  742. .dpy_gl_update = dbus_scanout_update,
  743. };
  744. #endif
  745. const DisplayChangeListenerOps dbus_dcl_ops = {
  746. .dpy_name = "dbus",
  747. .dpy_gfx_update = dbus_gfx_update,
  748. .dpy_gfx_switch = dbus_gfx_switch,
  749. .dpy_refresh = dbus_refresh,
  750. .dpy_mouse_set = dbus_mouse_set,
  751. .dpy_cursor_define = dbus_cursor_define,
  752. };
  753. static void
  754. dbus_display_listener_dispose(GObject *object)
  755. {
  756. DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
  757. unregister_displaychangelistener(&ddl->dcl);
  758. g_clear_object(&ddl->conn);
  759. g_clear_pointer(&ddl->bus_name, g_free);
  760. g_clear_object(&ddl->proxy);
  761. #ifdef WIN32
  762. g_clear_object(&ddl->map_proxy);
  763. g_clear_object(&ddl->d3d11_proxy);
  764. g_clear_pointer(&ddl->peer_process, CloseHandle);
  765. #ifdef CONFIG_PIXMAN
  766. pixman_region32_fini(&ddl->gl_damage);
  767. #endif
  768. #ifdef CONFIG_OPENGL
  769. egl_fb_destroy(&ddl->fb);
  770. #endif
  771. #endif
  772. G_OBJECT_CLASS(dbus_display_listener_parent_class)->dispose(object);
  773. }
  774. static void
  775. dbus_display_listener_constructed(GObject *object)
  776. {
  777. DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(object);
  778. ddl->dcl.ops = &dbus_dcl_ops;
  779. #ifdef CONFIG_OPENGL
  780. if (display_opengl) {
  781. ddl->dcl.ops = &dbus_gl_dcl_ops;
  782. }
  783. #endif
  784. G_OBJECT_CLASS(dbus_display_listener_parent_class)->constructed(object);
  785. }
  786. static void
  787. dbus_display_listener_class_init(DBusDisplayListenerClass *klass)
  788. {
  789. GObjectClass *object_class = G_OBJECT_CLASS(klass);
  790. object_class->dispose = dbus_display_listener_dispose;
  791. object_class->constructed = dbus_display_listener_constructed;
  792. }
  793. static void
  794. dbus_display_listener_init(DBusDisplayListener *ddl)
  795. {
  796. #ifdef CONFIG_PIXMAN
  797. pixman_region32_init(&ddl->gl_damage);
  798. #endif
  799. }
  800. const char *
  801. dbus_display_listener_get_bus_name(DBusDisplayListener *ddl)
  802. {
  803. return ddl->bus_name ?: "p2p";
  804. }
  805. DBusDisplayConsole *
  806. dbus_display_listener_get_console(DBusDisplayListener *ddl)
  807. {
  808. return ddl->console;
  809. }
  810. static bool
  811. dbus_display_listener_implements(DBusDisplayListener *ddl, const char *iface)
  812. {
  813. QemuDBusDisplay1Listener *l = QEMU_DBUS_DISPLAY1_LISTENER(ddl->proxy);
  814. bool implements;
  815. implements = g_strv_contains(qemu_dbus_display1_listener_get_interfaces(l), iface);
  816. if (!implements) {
  817. g_debug("Display listener does not implement: `%s`", iface);
  818. }
  819. return implements;
  820. }
  821. #ifdef WIN32
  822. static bool
  823. dbus_display_listener_setup_peer_process(DBusDisplayListener *ddl)
  824. {
  825. g_autoptr(GError) err = NULL;
  826. GDBusConnection *conn;
  827. GIOStream *stream;
  828. GSocket *sock;
  829. g_autoptr(GCredentials) creds = NULL;
  830. DWORD *pid;
  831. if (ddl->peer_process) {
  832. return true;
  833. }
  834. conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(ddl->proxy));
  835. stream = g_dbus_connection_get_stream(conn);
  836. if (!G_IS_UNIX_CONNECTION(stream)) {
  837. return false;
  838. }
  839. sock = g_socket_connection_get_socket(G_SOCKET_CONNECTION(stream));
  840. creds = g_socket_get_credentials(sock, &err);
  841. if (!creds) {
  842. g_debug("Failed to get peer credentials: %s", err->message);
  843. return false;
  844. }
  845. pid = g_credentials_get_native(creds, G_CREDENTIALS_TYPE_WIN32_PID);
  846. if (pid == NULL) {
  847. g_debug("Failed to get peer PID");
  848. return false;
  849. }
  850. ddl->peer_process = OpenProcess(
  851. PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
  852. false, *pid);
  853. if (!ddl->peer_process) {
  854. g_autofree char *msg = g_win32_error_message(GetLastError());
  855. g_debug("Failed to OpenProcess: %s", msg);
  856. return false;
  857. }
  858. return true;
  859. }
  860. #endif
  861. static void
  862. dbus_display_listener_setup_d3d11(DBusDisplayListener *ddl)
  863. {
  864. #ifdef WIN32
  865. g_autoptr(GError) err = NULL;
  866. if (!dbus_display_listener_implements(ddl,
  867. "org.qemu.Display1.Listener.Win32.D3d11")) {
  868. return;
  869. }
  870. if (!dbus_display_listener_setup_peer_process(ddl)) {
  871. return;
  872. }
  873. ddl->d3d11_proxy =
  874. qemu_dbus_display1_listener_win32_d3d11_proxy_new_sync(ddl->conn,
  875. G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
  876. NULL,
  877. "/org/qemu/Display1/Listener",
  878. NULL,
  879. &err);
  880. if (!ddl->d3d11_proxy) {
  881. g_debug("Failed to setup win32 d3d11 proxy: %s", err->message);
  882. return;
  883. }
  884. #endif
  885. }
  886. static void
  887. dbus_display_listener_setup_shared_map(DBusDisplayListener *ddl)
  888. {
  889. g_autoptr(GError) err = NULL;
  890. #ifdef WIN32
  891. if (!dbus_display_listener_implements(
  892. ddl, "org.qemu.Display1.Listener.Win32.Map")) {
  893. return;
  894. }
  895. if (!dbus_display_listener_setup_peer_process(ddl)) {
  896. return;
  897. }
  898. ddl->map_proxy =
  899. qemu_dbus_display1_listener_win32_map_proxy_new_sync(ddl->conn,
  900. G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
  901. NULL,
  902. "/org/qemu/Display1/Listener",
  903. NULL,
  904. &err);
  905. if (!ddl->map_proxy) {
  906. g_debug("Failed to setup win32 map proxy: %s", err->message);
  907. return;
  908. }
  909. ddl->can_share_map = true;
  910. #else /* !WIN32 */
  911. if (!dbus_display_listener_implements(
  912. ddl, "org.qemu.Display1.Listener.Unix.Map")) {
  913. return;
  914. }
  915. ddl->map_proxy = qemu_dbus_display1_listener_unix_map_proxy_new_sync(
  916. ddl->conn, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL,
  917. "/org/qemu/Display1/Listener", NULL, &err);
  918. if (!ddl->map_proxy) {
  919. g_debug("Failed to setup Unix map proxy: %s", err->message);
  920. return;
  921. }
  922. ddl->can_share_map = true;
  923. #endif
  924. }
  925. static GDBusMessage *
  926. dbus_filter(GDBusConnection *connection,
  927. GDBusMessage *message,
  928. gboolean incoming,
  929. gpointer user_data)
  930. {
  931. DBusDisplayListener *ddl = DBUS_DISPLAY_LISTENER(user_data);
  932. guint32 serial, discard_serial;
  933. if (incoming) {
  934. return message;
  935. }
  936. serial = g_dbus_message_get_serial(message);
  937. discard_serial = g_atomic_int_get(&ddl->display_serial_to_discard);
  938. if (serial <= discard_serial) {
  939. const char *member = g_dbus_message_get_member(message);
  940. static const char *const display_messages[] = {
  941. "Scanout",
  942. "Update",
  943. #ifdef CONFIG_GBM
  944. "ScanoutDMABUF",
  945. "UpdateDMABUF",
  946. #endif
  947. "ScanoutMap",
  948. "UpdateMap",
  949. "Disable",
  950. NULL,
  951. };
  952. if (g_strv_contains(display_messages, member)) {
  953. trace_dbus_filter(serial, discard_serial);
  954. g_object_unref(message);
  955. return NULL;
  956. }
  957. }
  958. discard_serial = g_atomic_int_get(&ddl->cursor_serial_to_discard);
  959. if (serial <= discard_serial) {
  960. const gchar *member = g_dbus_message_get_member(message);
  961. static const char *const cursor_messages[] = {
  962. "CursorDefine",
  963. NULL
  964. };
  965. if (g_strv_contains(cursor_messages, member)) {
  966. trace_dbus_filter(serial, discard_serial);
  967. g_object_unref(message);
  968. return NULL;
  969. }
  970. }
  971. return message;
  972. }
  973. DBusDisplayListener *
  974. dbus_display_listener_new(const char *bus_name,
  975. GDBusConnection *conn,
  976. DBusDisplayConsole *console)
  977. {
  978. DBusDisplayListener *ddl;
  979. QemuConsole *con;
  980. g_autoptr(GError) err = NULL;
  981. ddl = g_object_new(DBUS_DISPLAY_TYPE_LISTENER, NULL);
  982. ddl->proxy =
  983. qemu_dbus_display1_listener_proxy_new_sync(conn,
  984. G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
  985. NULL,
  986. "/org/qemu/Display1/Listener",
  987. NULL,
  988. &err);
  989. if (!ddl->proxy) {
  990. error_report("Failed to setup proxy: %s", err->message);
  991. g_object_unref(conn);
  992. g_object_unref(ddl);
  993. return NULL;
  994. }
  995. ddl->dbus_filter = g_dbus_connection_add_filter(conn, dbus_filter, g_object_ref(ddl), g_object_unref);
  996. ddl->bus_name = g_strdup(bus_name);
  997. ddl->conn = conn;
  998. ddl->console = console;
  999. dbus_display_listener_setup_shared_map(ddl);
  1000. trace_dbus_can_share_map(ddl->can_share_map);
  1001. dbus_display_listener_setup_d3d11(ddl);
  1002. con = qemu_console_lookup_by_index(dbus_display_console_get_index(console));
  1003. assert(con);
  1004. ddl->dcl.con = con;
  1005. register_displaychangelistener(&ddl->dcl);
  1006. return ddl;
  1007. }