0

I'm writing a small tool in clojure and want to know when there's been a change on the clipboard. Here's a simplified version of what's going on.

(:import java.awt.Toolkit)

(:import (java.awt.datatransfer Clipboard
                                ClipboardOwner
                                Transferable
                                StringSelection
                                DataFlavor
                                FlavorListener))

(defn get-clipboard [] (. (Toolkit/getDefaultToolkit)
                     (getSystemClipboard)))

(defn get-content []
  (.getContents (get-clipboard) nil))


(def content (agent (get-content)))


(defn watch [key f]
 (add-watch content key f))

(defn -main []
  (while (not= content "banana-man")
    (watch :watcher
           (fn [key agent old-state new-state]
             (prn "-- agent Changed --")
             (prn "key" key)
             (prn "atom" agent)
             (prn "old-state" old-state)
             (prn "new-state" new-state)))))

I've added in a while loop just to keep the main function from shutting down immediately.

This runs without throwing any errors, but does not report when changes have been made on the clipboard or stop the while loop when I copy bannan-man to the clipboard. I've been struggling with this for a few weeks now and I'm sure I'm missing something simple. If anyone has some advice I would really appreciate it!

Sawin
  • 1

1 Answers1

0

For starters, content is an agent, so it will never be equal to a string. You should deref the agent using @ in order to make that comparison.

The while loop is not needed to prevent exit. If you use the agent thread pool, Clojure will not shut down until you explicitly run shutdown-agents. But we will need it to manage your agent updates.

content is not going to change after your initial assignment unless you explicitly send it an updating function with send or send-off. Don't let the name mislead you, agents are not autonomous, and are not scheduled or repeated tasks. Try something like this:

(defn -main []
  (watch :watcher
           (fn [key agent old-state new-state]
             (prn "-- agent Changed --")
             (prn "key" key)
             (prn "atom" agent)
             (prn "old-state" old-state)
             (prn "new-state" new-state)))
  (while (not= @content "banana-man")
    (send-off content (fn [& _] (get-content)))
    (Thread/sleep 250))
  (shutdown-agents))
noisesmith
  • 20,076
  • 2
  • 41
  • 49
  • Thanks for your time Noisesmith, – Sawin Apr 03 '15 at 08:03
  • Sorry, prematurely hit enter. I'm pretty new to clojure and programming in general so I apologize for any inefficient methods I ended up using in my example. And thank you for your suggestion! But i must ask, is there no way to have the add-watch function return the new state without actively sending off for the content? To be clear I'm aiming for a user scanning a document to ctrl-c text which will automatically be pulled from the clipboard and processed for the user to review later. I have a feeling your suggestion is the best option, but I just wanted to check. Again, thank you! – Sawin Apr 03 '15 at 08:22
  • It's not that it wasn't efficient - it's that it wouldn't actually do anything. An agent only changes if you call `send` or `send-off`. It isn't autonomous. And a watcher only triggers if the agent changes - it's even less autonomous. You need *something* that repeatedly checks for the state of the clipboard, in a loop. I think it's easier to explicitly put that action in a loop. – noisesmith Apr 03 '15 at 14:52