Mastodon
Page image
Custom scripts with Waybar
Making a toggleable clipboard watcher for automatic dictionary lookup
Published: 2025-08-16
Updated: 2025-12-27

The wl-paste command from wl-clipboard has a --watch flag that allows you to run a command with the copied text passed in as input. This is an awesome feature if you wanted to integrate your clipboard to a script or program, for example, looking up words on dictionaries automatically by just copying the text.

The only problem is that you don’t always want every copy to be looked up. Like when you are reading a novel in a foreign language, for example, sometimes you may take a break and go to some other apps, if in case you needed to copy something there, a dictionary pop-up would show up! You will have to kill the script and run it again for this to not happen, which would be a hassle to manage, so I came up with an idea: just use file to manage the state of these scripts.

GoldenDict pop-up

Let’s write a script that monitors your clipboard and send all copies to a dictionary. I’ll be using GoldenDict-ng as my dictionary.

read query
goldendict -s "$query"

The above script simply reads the standard input (which will be our copied text) and send it to GoldenDict. Now to start clipboard monitoring, you just run:

wl-paste --watch sh /path/to/script.sh

It is should be working as expected now. But like I said in the beginning, we want a way to easily control this script’s behavior: to send or not to send. One idea is to save a value into a file, with a value representing a state, and make our script look up the value from this file and decide what to do.

Here is a modified script in Bash that does so:

STATEFILE="/path/to/statefile"

# If state file doesn't yet exist, create it.
if [ ! -f $STATEFILE ]; then
	touch $STATEFILE
fi

# Fetch the content of the state file
STATUS=$(<$STATEFILE)

# If the content matches our conditions, then send the query
if [ "$STATUS" == "1" ]; then
	goldendict -s "$query"
fi

I’m gonna be honest here, but I kinda suck at Bash and I don’t really want to bother myself crawling on Stack Overflow so I rewrote the script to Ruby instead:

#!/usr/bin/env ruby

STATE_FILE_PATH = "/tmp/kiku-status"

print_status = false
new_status = ""

# Create state file if not yet exists
unless File.exist? STATE_FILE_PATH
  File.new(STATE_FILE_PATH, "wx").close
end

# Load the initial status
status = File.read(STATE_FILE_PATH)

# Loop through each command line arguments and evaluate them
ARGV.each do |arg|
  case arg
  when "-0"
    # Disable
    new_status = "0"
  when "-1"
    # Enable
    new_status = "1"
  when "-t"
    # Toggle
    if status == "1"
      new_status = "0"
    else
      new_status = "1"
    end
  when "-s"
    print_status = true
  end
end

# If the user wants to assign new states, open up the
# file and do so.
unless new_status.empty?
  state_file = File.open(STATE_FILE_PATH, "w+")
  state_file.write(new_status)
  state_file.flush
  state_file.close
end

# Read the new status
status = File.read(STATE_FILE_PATH)

if print_status
  # Write your own conditions here
  puts "󰺂" if status == "1"
  puts "󰪗" unless status == "1"
else
  if status == "1"
    input = gets
    unless input == nil  # In case clipboard is empty
      IO.popen("goldendict -s #{input}")
    end
  end
end

Some explanation for the arguments here:

  • -0: Set state to 0 (disable)
  • -1: Set state to 1 (enable)
  • -t: Switch state between 0 and 1
  • -s: Prints an icon that indicates the state
  • nothing: Asks for input and send it to GoldenDict if state is 1

Our only problem left is to somehow find a way to edit that value. My idea is to use a panel, since it usually always stay on your screen, perhaps adding a toggle button like a tray icon is a good idea. I will be using Waybar as my panel. Let’s create our custom Waybar module and assign it to somewhere on your panel. I put it to the left of my system tray:

...

"modules-right": [
  ...
  "custom/goldendict",
  "tray",
  ...
],

...

"custom/goldendict": {
  "format": "{}",
  "exec": "ruby /path/to/script -s",
  "on-click": "ruby /path/to/script -t",
  "interval": "once",
},

Don’t forget the main part of the story: run wl-paste -w ruby /path/to/script to trigger the script on every clipboard changes (you should run this on boot). And your panel should now look something like this after a restart. Also try clicking the icon to switch the state and copy some text:

A book icon sitting next to the system tray

A popup for the Japanese word 現在

Allow sleep script

In addition to the clipboard watcher, I also have a script that allows my system to sleep after 15 minutes of idling. I will put it here as well so maybe you could have more ideas on what you could do.

In my LabWC autostart script:

swayidle -w \
         timeout 600 'gtklock -d' \
         timeout 900 'if [ "$(cat /tmp/mimis-status)" -eq "1" ]; then loginctl suspend; fi' \
         before-sleep "playerctl pause; gtklock -d" &

The script for Waybar:

#!/usr/bin/env ruby

STATE_FILE_PATH = "/tmp/mimis-status"

print_status = false
new_status = ""

# Create state file if not yet exists
unless File.exist? STATE_FILE_PATH
  File.new(STATE_FILE_PATH, "wx").close
  new_status = "1"
end

# Load the initial status
status = File.read(STATE_FILE_PATH)

# Loop through each command line arguments and evaluate them
ARGV.each do |arg|
  case arg
  when "-0"
    # Disable
    new_status = "0"
  when "-1"
    # Enable
    new_status = "1"
  when "-t"
    # Toggle
    if status == "1"
      new_status = "0"
    else
      new_status = "1"
    end
  when "-s"
    print_status = true
  end
end

# If the user wants to assign new states, open up the
# file and do so.
unless new_status.empty?
  state_file = File.open(STATE_FILE_PATH, "w+")
  state_file.write(new_status)
  state_file.flush
  state_file.close
end

# Read the new status
status = File.read(STATE_FILE_PATH)

if print_status
  # Write your own conditions here
  puts "󰒲" if status == "1"
  puts "󰒳" unless status == "1"
end