1010#include < fcntl.h>
1111#include < gbm.h>
1212#include < poll.h>
13+ #include < sys/mman.h>
1314#include < unistd.h>
1415#include < wayland-client.h>
1516#include < wayland-util.h>
@@ -213,6 +214,9 @@ namespace wl {
213214 dmabuf_interface = (zwp_linux_dmabuf_v1 *) wl_registry_bind (registry, id, &zwp_linux_dmabuf_v1_interface, version);
214215
215216 this ->interface [LINUX_DMABUF] = true ;
217+ } else if (!std::strcmp (interface, wl_shm_interface.name )) {
218+ BOOST_LOG (info) << " [wayland] Found interface: " sv << interface << ' (' << id << " ) version " sv << version;
219+ shm_interface = (wl_shm *) wl_registry_bind (registry, id, &wl_shm_interface, 1 );
216220 }
217221 }
218222
@@ -273,6 +277,110 @@ namespace wl {
273277 }
274278 }
275279
280+ void dmabuf_t::cleanup_shm () {
281+ if (shm_wl_buffer) {
282+ wl_buffer_destroy (shm_wl_buffer);
283+ shm_wl_buffer = nullptr ;
284+ }
285+ if (shm_pool) {
286+ wl_shm_pool_destroy (shm_pool);
287+ shm_pool = nullptr ;
288+ }
289+ }
290+
291+ void dmabuf_t::destroy_shm () {
292+ cleanup_shm ();
293+ if (shm_mmap && shm_mmap_size > 0 ) {
294+ munmap (shm_mmap, shm_mmap_size);
295+ shm_mmap = nullptr ;
296+ shm_mmap_size = 0 ;
297+ }
298+ if (shm_fd >= 0 ) {
299+ close (shm_fd);
300+ shm_fd = -1 ;
301+ }
302+ }
303+
304+ bool dmabuf_t::init_shm (size_t size) {
305+ if (size == shm_mmap_size) {
306+ return true ;
307+ }
308+
309+ destroy_shm ();
310+
311+ shm_fd = memfd_create (" sunshine-shm" , 0 );
312+ if (shm_fd < 0 ) {
313+ BOOST_LOG (error) << " [wayland] memfd_create failed: " sv << strerror (errno);
314+ return false ;
315+ }
316+
317+ if (ftruncate (shm_fd, size) < 0 ) {
318+ BOOST_LOG (error) << " [wayland] ftruncate failed: " sv << strerror (errno);
319+ close (shm_fd);
320+ shm_fd = -1 ;
321+ return false ;
322+ }
323+
324+ shm_mmap = mmap (nullptr , size, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0 );
325+ if (shm_mmap == MAP_FAILED) {
326+ BOOST_LOG (error) << " [wayland] mmap failed: " sv << strerror (errno);
327+ shm_mmap = nullptr ;
328+ close (shm_fd);
329+ shm_fd = -1 ;
330+ return false ;
331+ }
332+
333+ shm_mmap_size = size;
334+ return true ;
335+ }
336+
337+ void dmabuf_t::create_and_copy_shm (zwlr_screencopy_frame_v1 *frame) {
338+ size_t needed = (size_t ) shm_info.stride * shm_info.height ;
339+
340+ if (!init_shm (needed)) {
341+ zwlr_screencopy_frame_v1_destroy (frame);
342+ status = REINIT;
343+ return ;
344+ }
345+
346+ // Recreate pool+buffer each frame (compositor may require fresh buffer)
347+ cleanup_shm ();
348+
349+ shm_pool = wl_shm_create_pool (shm_interface, shm_fd, shm_mmap_size);
350+ if (!shm_pool) {
351+ BOOST_LOG (error) << " [wayland] wl_shm_pool_create failed" sv;
352+ zwlr_screencopy_frame_v1_destroy (frame);
353+ status = REINIT;
354+ return ;
355+ }
356+
357+ shm_wl_buffer = wl_shm_pool_create_buffer (
358+ shm_pool,
359+ 0 ,
360+ shm_info.width ,
361+ shm_info.height ,
362+ shm_info.stride ,
363+ shm_info.format
364+ );
365+ if (!shm_wl_buffer) {
366+ BOOST_LOG (error) << " [wayland] wl_shm_pool_create_buffer failed" sv;
367+ cleanup_shm ();
368+ zwlr_screencopy_frame_v1_destroy (frame);
369+ status = REINIT;
370+ return ;
371+ }
372+
373+ shm_mode = true ;
374+
375+ BOOST_LOG (info) << " [wayland] SHM capture: " sv
376+ << shm_info.width << " x" sv << shm_info.height
377+ << " stride=" sv << shm_info.stride
378+ << " format=0x" sv << std::hex << shm_info.format << std::dec;
379+
380+ // Tell compositor to copy the frame into our SHM buffer
381+ zwlr_screencopy_frame_v1_copy (frame, shm_wl_buffer);
382+ }
383+
276384 dmabuf_t::dmabuf_t ():
277385 status {READY},
278386 frames {},
@@ -292,10 +400,12 @@ namespace wl {
292400 void dmabuf_t::listen (
293401 zwlr_screencopy_manager_v1 *screencopy_manager,
294402 zwp_linux_dmabuf_v1 *dmabuf_interface,
403+ wl_shm *shm_interface,
295404 wl_output *output,
296405 bool blend_cursor
297406 ) {
298407 this ->dmabuf_interface = dmabuf_interface;
408+ this ->shm_interface = shm_interface;
299409 // Reset state
300410 shm_info.supported = false ;
301411 dmabuf_info.supported = false ;
@@ -318,13 +428,13 @@ namespace wl {
318428
319429 dmabuf_t ::~dmabuf_t () {
320430 cleanup_gbm ();
431+ destroy_shm ();
321432
322433 for (auto &frame : frames) {
323434 frame.destroy ();
324435 }
325436
326437 if (gbm_device) {
327- // We should close the DRM FD, but it's owned by GBM
328438 gbm_device_destroy (gbm_device);
329439 gbm_device = nullptr ;
330440 }
@@ -364,74 +474,78 @@ namespace wl {
364474 BOOST_LOG (verbose) << " Frame flags: " sv << flags << (y_invert ? " (y_invert)" : " " );
365475 }
366476
367- // DMA-BUF creation helper
368- void dmabuf_t::create_and_copy_dmabuf (zwlr_screencopy_frame_v1 *frame) {
477+ // DMA-BUF creation helper — returns false on GBM failure (caller can fall back to SHM)
478+ bool dmabuf_t::create_and_copy_dmabuf (zwlr_screencopy_frame_v1 *frame) {
369479 if (!init_gbm ()) {
370480 BOOST_LOG (error) << " Failed to initialize GBM" sv;
371- zwlr_screencopy_frame_v1_destroy (frame);
372- status = REINIT;
373- return ;
481+ return false ;
374482 }
375483
376- // Create GBM buffer
377484 current_bo = gbm_bo_create (gbm_device, dmabuf_info.width , dmabuf_info.height , dmabuf_info.format , GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
378485 if (!current_bo) {
379486 BOOST_LOG (error) << " Failed to create GBM buffer" sv;
380- zwlr_screencopy_frame_v1_destroy (frame);
381- status = REINIT;
382- return ;
487+ return false ;
383488 }
384489
385- // Get buffer info
386490 int fd = gbm_bo_get_fd (current_bo);
387491 if (fd < 0 ) {
388492 BOOST_LOG (error) << " Failed to get buffer FD" sv;
389493 gbm_bo_destroy (current_bo);
390494 current_bo = nullptr ;
391- zwlr_screencopy_frame_v1_destroy (frame);
392- status = REINIT;
393- return ;
495+ return false ;
394496 }
395497
396498 uint32_t stride = gbm_bo_get_stride (current_bo);
397499 uint64_t modifier = gbm_bo_get_modifier (current_bo);
398500
399- // Store in surface descriptor for later use
400501 auto next_frame = get_next_frame ();
401502 next_frame->sd .fds [0 ] = fd;
402503 next_frame->sd .pitches [0 ] = stride;
403504 next_frame->sd .offsets [0 ] = 0 ;
404505 next_frame->sd .modifier = modifier;
405506
406- // Create linux-dmabuf buffer
407507 auto params = zwp_linux_dmabuf_v1_create_params (dmabuf_interface);
408508 zwp_linux_buffer_params_v1_add (params, fd, 0 , 0 , stride, modifier >> 32 , modifier & 0xffffffff );
409-
410- // Add listener for buffer creation
411509 zwp_linux_buffer_params_v1_add_listener (params, ¶ms_listener, frame);
412-
413- // Create Wayland buffer (async - callback will handle copy)
414510 zwp_linux_buffer_params_v1_create (params, dmabuf_info.width , dmabuf_info.height , dmabuf_info.format , 0 );
511+
512+ return true ;
415513 }
416514
417515 // Buffer done callback - time to create buffer
418516 void dmabuf_t::buffer_done (zwlr_screencopy_frame_v1 *frame) {
419517 auto next_frame = get_next_frame ();
518+ shm_mode = false ;
420519
421- // Prefer DMA-BUF if supported
422- if (dmabuf_info.supported && dmabuf_interface) {
423- // Store format info first
520+ // Prefer DMA-BUF if supported (skip if GBM already failed once)
521+ if (dmabuf_info.supported && dmabuf_interface && !gbm_failed) {
424522 next_frame->sd .fourcc = dmabuf_info.format ;
425523 next_frame->sd .width = dmabuf_info.width ;
426524 next_frame->sd .height = dmabuf_info.height ;
427525
428- // Create and start copy
429- create_and_copy_dmabuf (frame);
430- } else if (shm_info.supported ) {
431- // SHM fallback would go here
432- BOOST_LOG (warning) << " [wayland] SHM capture not implemented" sv;
526+ if (create_and_copy_dmabuf (frame)) {
527+ return ; // async path continues via buffer_params callbacks
528+ }
529+
530+ // DMA-BUF failed (e.g. GBM on headless NVIDIA) — remember and fall back
531+ gbm_failed = true ;
532+ cleanup_gbm ();
533+ if (shm_info.supported && shm_interface) {
534+ BOOST_LOG (warning) << " [wayland] DMA-BUF capture failed, falling back to SHM permanently" sv;
535+ create_and_copy_shm (frame);
536+ return ;
537+ }
538+
539+ BOOST_LOG (error) << " [wayland] DMA-BUF failed and no SHM fallback available" sv;
433540 zwlr_screencopy_frame_v1_destroy (frame);
434541 status = REINIT;
542+ } else if (shm_info.supported && shm_interface) {
543+ static bool shm_logged = false ;
544+ if (!shm_logged) {
545+ BOOST_LOG (info) << " [wayland] Using SHM capture (no DMA-BUF available)" sv;
546+ shm_logged = true ;
547+ }
548+ create_and_copy_shm (frame);
435549 } else {
436550 BOOST_LOG (error) << " [wayland] No supported buffer types" sv;
437551 zwlr_screencopy_frame_v1_destroy (frame);
@@ -479,7 +593,6 @@ namespace wl {
479593 std::uint32_t tv_sec_lo,
480594 std::uint32_t tv_nsec
481595 ) {
482- // Frame is ready for use, GBM buffer now contains screen content
483596 current_frame->destroy ();
484597 current_frame = get_next_frame ();
485598
@@ -489,13 +602,26 @@ namespace wl {
489602 std::chrono::duration_cast<std::chrono::steady_clock::duration>(ready_ts)
490603 };
491604
492- // Keep the GBM buffer alive but destroy the Wayland objects
493- if (current_wl_buffer) {
494- wl_buffer_destroy (current_wl_buffer);
495- current_wl_buffer = nullptr ;
496- }
605+ if (shm_mode) {
606+ // SHM frame: populate dimensions for wlr_t::snapshot() check
607+ current_frame->sd .width = shm_info.width ;
608+ current_frame->sd .height = shm_info.height ;
609+ current_frame->shm_data = shm_mmap;
610+ current_frame->shm_stride = shm_info.stride ;
611+ current_frame->is_shm = true ;
497612
498- cleanup_gbm ();
613+ // Destroy Wayland objects, keep memfd+mmap for next frame
614+ cleanup_shm ();
615+ } else {
616+ current_frame->is_shm = false ;
617+
618+ // DMA-BUF path: destroy Wayland buffer, keep GBM bo alive
619+ if (current_wl_buffer) {
620+ wl_buffer_destroy (current_wl_buffer);
621+ current_wl_buffer = nullptr ;
622+ }
623+ cleanup_gbm ();
624+ }
499625
500626 zwlr_screencopy_frame_v1_destroy (frame);
501627 status = READY;
@@ -505,8 +631,8 @@ namespace wl {
505631 void dmabuf_t::failed (zwlr_screencopy_frame_v1 *frame) {
506632 BOOST_LOG (error) << " [wayland] Frame capture failed" sv;
507633
508- // Clean up resources
509634 cleanup_gbm ();
635+ cleanup_shm ();
510636 auto next_frame = get_next_frame ();
511637 next_frame->destroy ();
512638
0 commit comments