Flameshot + Emacs + Dbus

I've been looking for faster ways to include screenshots and other media in org mode.

With 0 keystrokes in emacs:

The video sequence:

  1. I press the Print Screen key, bound on my desktop to call flameshot gui.
  2. I confirm the area selection looks good to me with Ctrl + C.
  3. Flameshot sends a method call over dbus after it records the bytes of the image into the system clipboard.
  4. Emacs has been monitoring for that specific method call, and (yank-media) is called automatically in the (current-buffer).
  5. Due to a quirk in the clipboard not being ready when this notification fires, (yank-media) thinks we're pasting plain text, and fails.
  6. The quirk is a nonissue if existing image data exists in the clipboard, so the second time I do steps 1 - 4, it works, with the newest image.
  7. Finally I call org-toggle-inline-images (ok, 1 keystroke, but it was for the demo).

So there's a quirk, but I hope the folks at flameshot will continue to iterate on it. Or maybe it's an issue with Wayland + Gnome interaction?

Jump to Code to steal this.

Update 2025-01-09 I find that flameshot is really finicky if it will work outside of being called within Gnome terminal on wayland. Sometimes it works if I restart my computer.

Desired Workflow

  • Take a screenshot
  • Emacs should immediately either
    1. add a link to the media in kill-ring so I can decide what to do with it later.
    2. auto insert into the org doc in (current-buffer), allowing me to take a bunch of screenshots and then I can just rearrange them as I like, like a scrapbook.

In the video, you see me take option 2. But hopefully after reading this, you'll realize option 1 is a piece of cake too.

Tradeoffs Made

Other Approaches

Other non-dbus options exist, and they look complicated.

  • https://github.com/bburns/clipmon polls the system clipboard.
    • Works for text and images in system clipboard.
    • Only works on X.
  • https://github.com/cdown/clipnotify is properly evented and I experimented with calling dbus commands to send signals to emacs
    • It's not as easy to package a solution using it for various reasons1.
    • Also only works on X.
    • Works for text and images in system clipboard.
  • Use the elisp#File Notifications api, by listening to changes in the file system. For this, configure the screenshot tool to always save to a file.
    • Probably the most robust, cross platform solution. I did not choose this, partially because of fashion.
    • I would have to save a file first, before likely moving it.

DBUS + Flameshot approach

You might be wondering why I didn't use a DBUS event for when the system clipboard changes. It's because I couldn't find one.

Pros:

  • Should work on Wayland and X.
  • Is event-based, no polling required
  • Uses native emacs DBUS API
  • No integration layer to set up in Flameshot–it has native dbus support.

Cons:

  • Has 2 hard dependencies: dbus and flameshot.
  • Doesn't work with text, I've only supported images since I'm focusing on flameshot.
  • DBUS support in Flameshot could be improved.

So I've settled on flameshot

flameshot --version
Flameshot v12.1.0 (-)
Compiled with Qt 5.15.14

If you're on wayland, there's scattered help online, but see if https://flameshot.org/docs/guide/wayland helps.

Flameshot settings

I like the "Use last region" option, which you can access by running

flameshot config
2025-01-07_12-56-52_screenshot.png

Then running

flameshot gui

brings up a screen where you can select your region. (Shown in demo video). Hover over icons to learn the shortcuts.

One thing I love in emacs is browsing man pages with (woman), where you can learn more about flameshot.

(woman "flameshot")
output-2025-01-07-18:05:04.gif
Figure 2: animation of scrolling through woman in emacs

Code

There are only two components:

  1. Monitor: gets notified of events in dbus. You specify fields to filter only the events you want.
  2. Handler: handles the event. Call arbitrary code.
(defun jwow/dbus-flameshot-handler (&rest _args)
  "A dbus event handler that adds picture attachments. Forces a
 selection of media type as \"image/png\" to speed up workflow"
  (let ((completing-read-function
         (lambda (&rest _)
           ;; customize a variable for this instead to your liking
           "image/png")))               
    (yank-media))
  (insert "\n"))

;; Save the dbus object, which is a monitor, so we can unregister it if needed
;; This invocation can be derived by watching dbus-monito messages
(setq jwow/dbus-flameshot-monitor
      (dbus-register-monitor
       :session                       ; bus
       #'jwow/dbus-flameshot-handler
       :sender "org.flameshot.Flameshot"
       :path "/org/freedesktop/Notifications"
       :interface "org.freedesktop.Notifications"
       :member "Notify"))

;; To disable the auto yank-media behavior, unregister the monitor
(dbus-unregister-object jwow/dbus-flameshot-monitor)

Quirks

Notify?

Yes, I waited for Notify to get called, which populates system notifications:

2025-01-07_16-10-35_screenshot.png

I did this in part because there weren't many identifiable events from flameshot. I was a little sad about that.

method call time=1736280897.494725 sender=:1.229 -> destination=org.freedesktop.Notifications serial=111
   path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify string "flameshot" uint32
   0 string "flameshot" string "Flameshot Info" string "Capture saved to clipboard." array [ ] array [ ] int32 5000

No Signals?

At this point, it's a bit weird IMO, that Flameshot doesn't have any signals (for broadcasting) in its DBUS interface. I think it should. That way, emacs can register itself as a proper service and get notified that way.

Emacs registering a monitor might be hacky (I don't know, I'm not a heavy dbus user) and I cannot comment on its performance. Creating a monitor is based on dbus-monitor, a shell command (and elisp command).

2025-01-07_15-49-18_screenshot.png
Figure 4: In D-Spy, I inspect the interface for Flameshot. It has 3 methods and 0 signals.

Flameshot sends an incorrect DBUS message?

In the shell command I get something like this:

method call time=1736280897.456930 sender=:1.460 -> destination=org.flameshot.Flameshot serial=9 path=/;
   interface=(null); member=attachScreenshotToClipboard array of bytes [ 00 00 00 01 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d
   49 48 44 52 00 00 02 90 00 00 01 ec 0
 ...

In emacs's code path for dbus events:

dbus-event-bus-name: D-Bus error: "Not a valid D-Bus event", (dbus-event
 :session-private 1 9 ":1.576" "org.flameshot.Flameshot" "/" nil "
attachScreenshotToClipboard" jwow/dbus-flameshot-handler ...)

The interfaces is missing; seems like a bug. In any case, it doesn't avoid the issues of text being in the clipboard, and (yank-media) having no idea how to handle it.

Emacs (dbus-monitor), funnels all events

If you set up a monitor, unfortunately calling (dbus-monitor) will make your previously defined handler handle all events.

I think this is a flaw in the emacs dbus api. dbus-monitor should call a built-in handler.

At this point, unregister the dbus monitor to get back to normal.

(dbus-unregister-object jwow/dbus-flameshot-monitor)

Explore More

  • See dbus#Top for an overview of the dbus api.
  • Ian Eure's informative and concise presentation at https://emacsconf.org/2022/talks/dbus/. He shows off an interface where he uses DBUS to detect if a usb drive connects to the computer.
  • Think about things you could do with the handler:
    • Save the data to a file and save the file name to kill-ring
    • experiment with (gui-get-selection 'CLIPBOARD 'TARGETS)
    • pipe the screenshot to a program to resize it
    • get auto-ocr, and yank the text from the image to kill-ring

Thanks for supporting my work. Turning into an unstoppable space god wouldn't be possible without your help!


1

For one, you have to create a loop in bash to keep it listening. Which isn't bad. It notifies whenever text is selected, not even copied, as well. To change that, you have to change the C code and recompile the binary.

See https://unix.stackexchange.com/a/723818