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,106 @@ 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+ void dmabuf_t::create_and_copy_shm (zwlr_screencopy_frame_v1 *frame) {
305+ size_t needed = (size_t ) shm_info.stride * shm_info.height ;
306+
307+ // (Re)allocate memfd+mmap if size changed
308+ if (needed != shm_mmap_size) {
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+ zwlr_screencopy_frame_v1_destroy (frame);
315+ status = REINIT;
316+ return ;
317+ }
318+
319+ if (ftruncate (shm_fd, needed) < 0 ) {
320+ BOOST_LOG (error) << " [wayland] ftruncate failed: " sv << strerror (errno);
321+ close (shm_fd);
322+ shm_fd = -1 ;
323+ zwlr_screencopy_frame_v1_destroy (frame);
324+ status = REINIT;
325+ return ;
326+ }
327+
328+ shm_mmap = mmap (nullptr , needed, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0 );
329+ if (shm_mmap == MAP_FAILED) {
330+ BOOST_LOG (error) << " [wayland] mmap failed: " sv << strerror (errno);
331+ shm_mmap = nullptr ;
332+ close (shm_fd);
333+ shm_fd = -1 ;
334+ zwlr_screencopy_frame_v1_destroy (frame);
335+ status = REINIT;
336+ return ;
337+ }
338+
339+ shm_mmap_size = needed;
340+ }
341+
342+ // Recreate pool+buffer each frame (compositor may require fresh buffer)
343+ cleanup_shm ();
344+
345+ shm_pool = wl_shm_create_pool (shm_interface, shm_fd, shm_mmap_size);
346+ if (!shm_pool) {
347+ BOOST_LOG (error) << " [wayland] wl_shm_pool_create failed" sv;
348+ zwlr_screencopy_frame_v1_destroy (frame);
349+ status = REINIT;
350+ return ;
351+ }
352+
353+ shm_wl_buffer = wl_shm_pool_create_buffer (
354+ shm_pool,
355+ 0 ,
356+ shm_info.width ,
357+ shm_info.height ,
358+ shm_info.stride ,
359+ shm_info.format
360+ );
361+ if (!shm_wl_buffer) {
362+ BOOST_LOG (error) << " [wayland] wl_shm_pool_create_buffer failed" sv;
363+ cleanup_shm ();
364+ zwlr_screencopy_frame_v1_destroy (frame);
365+ status = REINIT;
366+ return ;
367+ }
368+
369+ shm_mode = true ;
370+
371+ BOOST_LOG (info) << " [wayland] SHM capture: " sv
372+ << shm_info.width << " x" sv << shm_info.height
373+ << " stride=" sv << shm_info.stride
374+ << " format=0x" sv << std::hex << shm_info.format << std::dec;
375+
376+ // Tell compositor to copy the frame into our SHM buffer
377+ zwlr_screencopy_frame_v1_copy (frame, shm_wl_buffer);
378+ }
379+
276380 dmabuf_t::dmabuf_t ():
277381 status {READY},
278382 frames {},
@@ -292,10 +396,12 @@ namespace wl {
292396 void dmabuf_t::listen (
293397 zwlr_screencopy_manager_v1 *screencopy_manager,
294398 zwp_linux_dmabuf_v1 *dmabuf_interface,
399+ wl_shm *shm_interface,
295400 wl_output *output,
296401 bool blend_cursor
297402 ) {
298403 this ->dmabuf_interface = dmabuf_interface;
404+ this ->shm_interface = shm_interface;
299405 // Reset state
300406 shm_info.supported = false ;
301407 dmabuf_info.supported = false ;
@@ -318,13 +424,13 @@ namespace wl {
318424
319425 dmabuf_t ::~dmabuf_t () {
320426 cleanup_gbm ();
427+ destroy_shm ();
321428
322429 for (auto &frame : frames) {
323430 frame.destroy ();
324431 }
325432
326433 if (gbm_device) {
327- // We should close the DRM FD, but it's owned by GBM
328434 gbm_device_destroy (gbm_device);
329435 gbm_device = nullptr ;
330436 }
@@ -364,74 +470,78 @@ namespace wl {
364470 BOOST_LOG (verbose) << " Frame flags: " sv << flags << (y_invert ? " (y_invert)" : " " );
365471 }
366472
367- // DMA-BUF creation helper
368- void dmabuf_t::create_and_copy_dmabuf (zwlr_screencopy_frame_v1 *frame) {
473+ // DMA-BUF creation helper — returns false on GBM failure (caller can fall back to SHM)
474+ bool dmabuf_t::create_and_copy_dmabuf (zwlr_screencopy_frame_v1 *frame) {
369475 if (!init_gbm ()) {
370476 BOOST_LOG (error) << " Failed to initialize GBM" sv;
371- zwlr_screencopy_frame_v1_destroy (frame);
372- status = REINIT;
373- return ;
477+ return false ;
374478 }
375479
376- // Create GBM buffer
377480 current_bo = gbm_bo_create (gbm_device, dmabuf_info.width , dmabuf_info.height , dmabuf_info.format , GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR);
378481 if (!current_bo) {
379482 BOOST_LOG (error) << " Failed to create GBM buffer" sv;
380- zwlr_screencopy_frame_v1_destroy (frame);
381- status = REINIT;
382- return ;
483+ return false ;
383484 }
384485
385- // Get buffer info
386486 int fd = gbm_bo_get_fd (current_bo);
387487 if (fd < 0 ) {
388488 BOOST_LOG (error) << " Failed to get buffer FD" sv;
389489 gbm_bo_destroy (current_bo);
390490 current_bo = nullptr ;
391- zwlr_screencopy_frame_v1_destroy (frame);
392- status = REINIT;
393- return ;
491+ return false ;
394492 }
395493
396494 uint32_t stride = gbm_bo_get_stride (current_bo);
397495 uint64_t modifier = gbm_bo_get_modifier (current_bo);
398496
399- // Store in surface descriptor for later use
400497 auto next_frame = get_next_frame ();
401498 next_frame->sd .fds [0 ] = fd;
402499 next_frame->sd .pitches [0 ] = stride;
403500 next_frame->sd .offsets [0 ] = 0 ;
404501 next_frame->sd .modifier = modifier;
405502
406- // Create linux-dmabuf buffer
407503 auto params = zwp_linux_dmabuf_v1_create_params (dmabuf_interface);
408504 zwp_linux_buffer_params_v1_add (params, fd, 0 , 0 , stride, modifier >> 32 , modifier & 0xffffffff );
409-
410- // Add listener for buffer creation
411505 zwp_linux_buffer_params_v1_add_listener (params, ¶ms_listener, frame);
412-
413- // Create Wayland buffer (async - callback will handle copy)
414506 zwp_linux_buffer_params_v1_create (params, dmabuf_info.width , dmabuf_info.height , dmabuf_info.format , 0 );
507+
508+ return true ;
415509 }
416510
417511 // Buffer done callback - time to create buffer
418512 void dmabuf_t::buffer_done (zwlr_screencopy_frame_v1 *frame) {
419513 auto next_frame = get_next_frame ();
514+ shm_mode = false ;
420515
421- // Prefer DMA-BUF if supported
422- if (dmabuf_info.supported && dmabuf_interface) {
423- // Store format info first
516+ // Prefer DMA-BUF if supported (skip if GBM already failed once)
517+ if (dmabuf_info.supported && dmabuf_interface && !gbm_failed) {
424518 next_frame->sd .fourcc = dmabuf_info.format ;
425519 next_frame->sd .width = dmabuf_info.width ;
426520 next_frame->sd .height = dmabuf_info.height ;
427521
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;
522+ if (create_and_copy_dmabuf (frame)) {
523+ return ; // async path continues via buffer_params callbacks
524+ }
525+
526+ // DMA-BUF failed (e.g. GBM on headless NVIDIA) — remember and fall back
527+ gbm_failed = true ;
528+ cleanup_gbm ();
529+ if (shm_info.supported && shm_interface) {
530+ BOOST_LOG (warning) << " [wayland] DMA-BUF capture failed, falling back to SHM permanently" sv;
531+ create_and_copy_shm (frame);
532+ return ;
533+ }
534+
535+ BOOST_LOG (error) << " [wayland] DMA-BUF failed and no SHM fallback available" sv;
433536 zwlr_screencopy_frame_v1_destroy (frame);
434537 status = REINIT;
538+ } else if (shm_info.supported && shm_interface) {
539+ static bool shm_logged = false ;
540+ if (!shm_logged) {
541+ BOOST_LOG (info) << " [wayland] Using SHM capture (no DMA-BUF available)" sv;
542+ shm_logged = true ;
543+ }
544+ create_and_copy_shm (frame);
435545 } else {
436546 BOOST_LOG (error) << " [wayland] No supported buffer types" sv;
437547 zwlr_screencopy_frame_v1_destroy (frame);
@@ -479,7 +589,6 @@ namespace wl {
479589 std::uint32_t tv_sec_lo,
480590 std::uint32_t tv_nsec
481591 ) {
482- // Frame is ready for use, GBM buffer now contains screen content
483592 current_frame->destroy ();
484593 current_frame = get_next_frame ();
485594
@@ -489,13 +598,26 @@ namespace wl {
489598 std::chrono::duration_cast<std::chrono::steady_clock::duration>(ready_ts)
490599 };
491600
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- }
601+ if (shm_mode) {
602+ // SHM frame: populate dimensions for wlr_t::snapshot() check
603+ current_frame->sd .width = shm_info.width ;
604+ current_frame->sd .height = shm_info.height ;
605+ current_frame->shm_data = shm_mmap;
606+ current_frame->shm_stride = shm_info.stride ;
607+ current_frame->is_shm = true ;
497608
498- cleanup_gbm ();
609+ // Destroy Wayland objects, keep memfd+mmap for next frame
610+ cleanup_shm ();
611+ } else {
612+ current_frame->is_shm = false ;
613+
614+ // DMA-BUF path: destroy Wayland buffer, keep GBM bo alive
615+ if (current_wl_buffer) {
616+ wl_buffer_destroy (current_wl_buffer);
617+ current_wl_buffer = nullptr ;
618+ }
619+ cleanup_gbm ();
620+ }
499621
500622 zwlr_screencopy_frame_v1_destroy (frame);
501623 status = READY;
@@ -505,8 +627,8 @@ namespace wl {
505627 void dmabuf_t::failed (zwlr_screencopy_frame_v1 *frame) {
506628 BOOST_LOG (error) << " [wayland] Frame capture failed" sv;
507629
508- // Clean up resources
509630 cleanup_gbm ();
631+ cleanup_shm ();
510632 auto next_frame = get_next_frame ();
511633 next_frame->destroy ();
512634
0 commit comments