Project

General

Profile

unexist.dev

Assorted tidbits and projects

Writing sublets

Hello world

Starting a sublet from scratch is really easy, just create an empty sublet with sur: sur template hello

This will create a folder hello with two files:

Filename Description
hello.rb The sublet file with the minimal required code
hello.spec The specification file with basic information for sur about the sublet

Say hello

Per default, the new sublet will do nothing and we need to add something do the data field:

# Hello sublet file
# Created with sur-0.2
configure :hello do |s|
  s.interval = 60
end

on :run do |s|
  s.data = "Hello, world!" 
end

Using variables

Typically, a sublet just needs local and instance variables to store data across runs and/or to calculate intermediate values. Due to the DSL, instance variables that normally just need a to be prefixed with a just @ now need an owner to be tracked as instance variable. That can either be the first argument of every event block or self inside of a helper.

# Hello sublet file
# Created with sur-0.2
configure :hello do |s|
  s.interval = 60
  s.world    = "variable world" 
end

on :run do |s|
  s.data = "Hello, #{s.world}" 
end

Move code to helper

Sometimes it can be useful to move code into own functions to call it on several events. Instead of just copying the code let's move the code into a helper. A helper is just another block that can be used to add methods to the sublet itself.

# Hello sublet file
# Created with sur-0.2
configure :hello do |s|
  s.interval = 60
  s.world    = "variable world" 
end

helper do |s|
  def hello_time
    case Time.now.strftime("%H").to_i
      when  6..9  then "Good morning" 
      when 10..15 then "Good day" 
      when 16..18 then "Good evening" 
      when 19..5  then "Good night" 
    end
  end
end

on :run do |s|
  s.data = "%s, %s" % [ s.hello_time, s.world ]
end

Test your sublet

sur comes with a tester for sublet to ease the whole thing: @sur test hello/hello.rb

The output looks like this:

------------------
 hello
------------------
 @visible = true
 @interval = 60
 @config = {}
 @world = "variable world" 
 @data = Hello, variable world!
------------------
 (1) run
 (2) hello_time (helper)
 (0) Quit
------------------
>>> Select one method:
>>> 

The lines starting with @ show the defined instanced variables, the lines with (1) the callable methods.

Install and submit

Once you are satisfied with your work you need to build your sublet: sur build hello/hello.spec

This will create a hello-0.0.sublet file that can either be installed locally sur install ./hello-0.0.sublet or uploaded to the sur repository sur submit ./hello-0.0.sublet and installed from there.

Components

Configure

A sublet starts with the configure block, which tells subtle the name and does basic initialization like the interval time if it's used. Assignment of instance variables must be done like s.value = "something" or otherwise the DSL can't keep track of it.

configure :sublet do |s|
  s.interval = 60
  s.variable = "something" 
end

Generally there are two different types of Sublets:

The sublet data must be of type String, everything else will be ignored.

Helper

Custom methods need to be written inside of a helper block to be used a instance method. Inside of this helpers, instance variables must be accessed via the self keyword:

helper do
  def something(test)
    self.variable = test
    self.data     = test
  end
end

Events

Sublets are event driven, so there are some specific events only for sublets:

Name Description Arguments
:mouse_over Whenever the pointer is over the sublet s
:mouse_down Whenever the pointer is pressed on the sublet. s, x, y, button
:mouse_out Whenever the pointer leaves the sublet s
:run Whenever either the interval time is expired s
:watch Whenever the watched file is modified/socket has data ready s
:data Whenever subtle receives a SUBTLE_SUBLET_DATA client message for this sublet s
:unload Whenever the sublet is unloaded s
on :mouse_down do |s, x, y, button|
  puts "button %d at x=%d and y=%d" % [ x, y, button ]
end

Hooks

Sublets can use all hooks that are useable in the main config too, the syntax is almost the same:

on :client_create do |s, c|
  puts s.name #< Name of the sublet
  puts c.name #< Name of the client
end

Grabs

Since r2608 sublets can provide grabs identified by symbols, that can be used in the main config.

configure :grabby do |s|
  s.interval = 5
end

grab :GrabbyGrab do |s, c|
  puts "sublet name: %s" % [ s.name ]
  puts "pressed on : %s" % [ c.name ]
end

Please use reasonable names for the grabs, there is no check or enforcement of names.

Configuration

Configuration of a sublet can be done (since r2148) Subtle::Subtle#config, this returns a hash and can be used as in the following example:

# Config:
sublet :configured do
  interval    50
  some_string "#00ff00" 
end

# Sublet:
configure :configured do |s|
  s.interval    = s.config[:interval]    || 30
  s.some_string = s.config[:some_string] || "default" 

  # Works with colors too
  s.red = Subtlext::Color.new(s.config[:red] || "#ff0000")
end

on :run do |s|
  s.data = s.red + s.some_string
end

The specification contains an info field that can be displayed via sur config sublet, it's basically an array of hashes that contains information how to use it:

spec.config = [
  { :name => "format_string", :type => "string", :def_value => "%y/%m/%d %H:%M", :description => "Format of the clock (man date)" },
]

Customization

Colors

The color of the ouput of a Sublets can be changed in this way:

configure :colorful do |s|
  s.red        = Subtlext::Color.new("#ff0000")
  s.green      = Subtlext::Color.new("#00ff00")
  s.blue       = Subtlext::Color.new("#0000ff")
  s.background = "#303030" 
end

on :run do |s|
  s.data = s.red + "su" + s.green + "bt" + s.blue + "le" 
end

Icons

There is also a way to add a X bitmap to a sublet:

configure :iconized do |s|
  s.icon = Subtlext::Icon.new("/usr/share/icons/subtle.xbm")
end

on :run do |s|
  s.data = @icon + "subtle" 
end

A nice collection of this pixmap can be found here

Subtle will add a padding of 3px left and right of the pixmap, so keep that in mind when using the click hooks.

Examples

Below is the code of a shipped sublets that displays the time. It should be really straight forward:

configure :clock do |s|
  s.interval = 60 #< Set interval time
end

on :run do |s|
  s.data = Time.now.strftime("%d%m%y%H%M") #< Set data
end

Another example for the inotify sublet which is also included within subtle:

configure :notify do |s|
  s.file = "/tmp/watch" 
  s.watch(s.file)
end

on :watch do
  begin
    self.data = IO.readlines(@file).first.chop #< Read data and strip
  rescue => err #< Catch error
    puts err
    self.data = "subtle" 
  end
end

The watch command also works with Ruby sockets, but be aware of blocking I/O:

configure :socket do |s|
  s.socket = TCPSocket.open("localhost", 6600)
  s.watch(s.socket)
end

on :watch do |s|
  begin
    s.data = s.socket.readline.chop #< Read data and strip
  rescue => err #< Catch error
    puts err
    s.data = "subtle" 
  end
end

on :run do |s|
  #  Do nothing
end

Subtle will automatically set sockets to O_NONBLOCK.

And finally an example with a click callback:

configure :click do |s|
  s.interval = 999 #< Do nothing
end

on :mouse_down do |s, x, y, button|
  Subtlext::Client["xterm"].raise
  puts x, y, button
end

on :run do |s|
  # Do nothing here
end