Draft: Add DBus screenshot service to compositor for popup/menu capture

Problem

It is currently impossible to take a screenshot of an open popup menu, dropdown, or combo box using xfce4-screenshooter.

When a popup menu is open, the menu owns an active X11 keyboard grab. Any attempt by xfce4-screenshooter to grab the keyboard or register a key binding is rejected by the X server. The menu also dismisses itself when focus changes, so even if the keypress is detected, the menu is gone before pixels are captured.

This is a fundamental X11 limitation — keyboard grabs are exclusive and cannot be shared.

Why xfwm4 is the right place to fix this

xfwm4 runs as the compositor and window manager. It already has the fully composited framebuffer in memory at all times, including the pixels of open popup menus. It is not subject to X11 keyboard grabs because it owns the display connection at a privileged level.

Exposing a DBus method that reads directly from the compositor buffer gives any screenshot tool a clean, grab-safe path to capture the screen — including open menus — without touching any X11 input mechanism.

Proposed solution

Add a DBus service org.xfce.ScreenShooter to xfwm4 that exposes a TakeScreenshot method. The method reads pixels directly from the compositor root pixmap via XGetImage and returns raw RGB pixel data over DBus.

I have implemented this in my fork on branch feature/dbus-screenshot-service.

API

Bus name: org.xfce.ScreenShooter
Object path: /org/xfce/ScreenShooter
Interface: org.xfce.ScreenShooter

Method: TakeScreenshot(x, y, width, height)

Parameter Type Description
x i Left edge (0 for full screen)
y i Top edge (0 for full screen)
width i Width in pixels (0 = full screen width)
height i Height in pixels (0 = full screen height)

Returns:

Parameter Type Description
pixels ay Packed RGB bytes
out_width i Actual width captured
out_height i Actual height captured
rowstride i Bytes per row (width * 3)

Implementation

  • Only compiled when HAVE_COMPOSITOR is defined — zero impact when compositor is disabled
  • Reads from root window via XGetImage — compositor has already painted the full composited scene including open menus
  • Converts XImage pixel format to packed RGB using channel masks
  • Registers on the session bus at startup alongside the GTK main loop
  • Minimal code — ~180 lines in two new files

Files changed

File Change
src/dbus-screenshot.c DBus service, XGetImage capture, RGB conversion
src/dbus-screenshot.h dbusScreenshotInit() declaration
src/main.c Call dbusScreenshotInit() after GTK main loop starts
src/meson.build Add new sources, gio dependency
meson.build Add gio-2.0 >= 2.72.0 dependency

Integration with xfce4-screenshooter

A companion branch feature/xi2-listen-mode in xfce4-screenshooter uses XI2 raw key events to detect the Print keypress even while a menu grab is active, then calls this DBus method to get the pixels. Together the two branches solve the popup screenshot problem completely:

  1. xfce4-screenshooter detects Print via XI2 (bypasses keyboard grab)
  2. xfce4-screenshooter calls org.xfce.ScreenShooter.TakeScreenshot (gets pixels from compositor)
  3. Screenshot includes the open menu

The xfwm4 DBus service can also be used independently by any other tool.

Testing

Tested on Linux Mint 22.3 XFCE with xfwm4 compositor enabled:

  • Full screen capture via DBus returns correct pixel data ✓
  • Partial screen capture (x, y, width, height) works correctly ✓
  • Compositor disabled → returns error gracefully ✓
  • Service registers on session bus at startup ✓
  • No performance impact on normal window manager operation ✓

Branch

feature/dbus-screenshot-service on https://gitlab.xfce.org/newman2x/xfwm4

Questions for maintainers

  1. Is exposing a DBus screenshot service from xfwm4 acceptable, or should this live elsewhere?
  2. Should the bus name be org.xfce.ScreenShooter or something different (e.g. org.xfce.Xfwm4)?
  3. Should this be behind a build option (-Ddbus-screenshot=enabled) rather than always built with the compositor?
  4. Is XGetImage on the root window the right capture method, or should we read from the compositor backing store directly?
  5. Should the pixel format be RGB, RGBA, or let the caller specify?