Testing FPS of terminal

I was just curious how fast can a terminal window (in X) get a refresh draw. So I wrote a script to test, termfps.sh. It prints characters to fill up the whole window by default, then reset the cursor to home using ANSI escape code. Print again, doing so for 100 times by default. 100 / elapsed time is the FPS.

I ran several tests using ./termfps.sh 1000 80 25. I used 80 by 25 because that's my VT's terminal size, and I maximize window before I run the test. Here are the results:

urxvtc
For 80x25 1000 frames, elapsed time: 9.126 seconds
Frames per second: 109.574

urxvt
For 80x25 1000 frames, elapsed time: 9.140 seconds
Frames per second: 109.401

urxvtc + tmux -2
For 80x25 1000 frames, elapsed time: 11.214 seconds
Frames per second: 89.173

urxvtc + tmux
For 80x25 1000 frames, elapsed time: 10.546 seconds
Frames per second: 94.813

xterm
For 80x25 1000 frames, elapsed time: 16.487 seconds
Frames per second: 60.653

urxvtc, no .Xdefaults
For 80x25 1000 frames, elapsed time: 8.882 seconds
Frames per second: 112.580

urxvtc + tmux -2, in right panel of two
For 80x25 1000 frames, elapsed time: 10.568 seconds
Frames per second: 94.616

vt1
For 80x25 1000 frames, elapsed time: 54.984 seconds
Frames per second: 18.187

lxterminal
For 80x25 1000 frames, elapsed time: 39.211 seconds
Frames per second: 25.502

The slowest one is vt1, I didn't test framebuffer. urxvt is my terminal, but I also have xterm installed. I installed lxterminal for vte-based terminal test. My normal urxvt uses Rxvt.font: xft:Envy Code R:style=Regular:size=9:antialias=false. I ran more on urxvtc, invoked without .Xdefaults, so I could test without changes I made. Since I use tmux, I tested tmux invoked with 256 colors or not, running in a panel.

This script can't test the real FPS since Bash script takes some time to process, but the results aren't really much lower and it does show the significant difference between terminals, or those FPS should all be capped around a same number. As you can see, urxvt runs fastest, then xterm, then lxterminal. Though there are some configuration differences, say the fonts, but it's quite conclusive from the numbers I see.

For maximized urxvt+tmux terminal window I normally use in one screen with video played in another screen, here is the result:

For 239x65 100 frames, elapsed time: 18.567 seconds
Frames per second: 5.385

CPU utilization calculation in Bash

I learned how to calculate CPU utilization when I started to use dzen from dzen's gcpubar source.

Later, I read the man proc and knew more about it:

 /proc/stat
        kernel/system statistics.   Varies  with  architecture.   Common
        entries include:

        cpu  3357 0 4313 1362393
               The   amount  of  time,  measured  in  units  of  USER_HZ
               (1/100ths  of  a  second  on  most   architectures,   use
               sysconf(_SC_CLK_TCK) to obtain the right value), that the
               system spent in user mode, user mode  with  low  priority
               (nice),  system  mode,  and  the idle task, respectively.
               The last value should be USER_HZ times the  second  entry
               in the uptime pseudo-file.

               In Linux 2.6 this line includes three additional columns:
               iowait - time waiting for I/O to complete (since 2.5.41);
               irq  -  time  servicing  interrupts  (since 2.6.0-test4);
               softirq - time servicing softirqs (since 2.6.0-test4).

               Since Linux 2.6.11, there is an eighth  column,  steal  -
               stolen  time,  which is the time spent in other operating
               systems when running in a virtualized environment

               Since Linux 2.6.24, there is a ninth column, guest, which
               is the time spent running a virtual CPU for guest operat
               ing systems under the control of the Linux kernel.

It said the number is measured in units of USER_HZ and I assume that also applies on columns after fourth column. The easy way is to sum of all difference of two consecutive samples of those numbers. That sum represents the processed time between two sampling time. In which, the difference of column four is the idling time. If sum minus this idling time, that is the CPU utilization, divide it by sum, then it's the percentage of CPU utilization.

Here is a sample of first few lines of /proc/stat

cpu  972478 47 394033 6711061 136593 2572 9139 0 0 0
cpu0 487126 17 199314 3294038 89582 2572 8539 0 0 0
cpu1 485352 29 194719 3417023 47011 0 599 0 0 0

If you count the columns, there is a tenth column, I have no idea what tenth column means. It wasn't mentioned in manpage.

Anyway, here is a sample code:

while :; do
    # 0 1:user 2:unice 3:sys 4:idle 5:iowait 6:irq 7:softirq 8:steal 9:guest 10:?
    ncpu=($(line</proc/stat))
    sum="${ncpu[@]:1}"

    cpu_total=$((${sum// /+}))
    cpu_maxval=$((cpu_total - ocpu_total))
    cpu_val=$((cpu_maxval - (ncpu[4]-ocpu[4])))
    cpu_percentage=$((100 * cpu_val / cpu_maxval))

    ocpu=("${ncpu[@]}")
    ocpu_total=$cpu_total
    echo "$cpu_percentage%"
    sleep 1
done

There are few tricky things, I used line to grab first line since I only need to know the total percentage of all process units. After that, it is fed to Bash array ncpu initialization. Pretty neat, isn't it?

Then, the code assigns sum with a string representation of ncpu of elements from second to last. The first one is "cpu", we don't need that. Replacing all spaces with plus sign, so it will read like 123+456+.... Feed it to arithmetic expansion, we get the sum of all numbers. Minus it with previous sum, that is sum of difference of two samples, cpu_maxval.

Then, minus the difference of idling time, it's the utilization time, cpu_val. Let cpu_val be divided by cpu_maxval, we get the percentage. Job is done.

My dzen status bar

Few days ago, I posted about using dzen for system status. Within these days, I made more changes. Now it looks like

2010-12-08--19:30:42

It updates the content 5 times/s, so the volume changes when you adjust. In my old Conky, I set it to update every 6 seconds, it's kind of weird after you press volume up button. Even before, 1 time/second, it's still too long. When battery runs too low, the icon will blink. What can I say, I love my scripts.

Let me tell you what's in it, from left to right:

  • The first two parts are CPU utilization and Memory usage. If you click on icon, you will get a popup window like in this screenshot. It lists processes use most resource first. I was wanting to add kill button with -9 and -15 options, but ^ca() only supported in title area. I didn't want to adjust positions, that would require too much work and no scrolling in title area. Those red or pink color are reflecting the resource usage, if it consumed more, it gets redder.
  • Next one is root filesystem / usage. Nothing special this one, clicking on the icon brings up output of df.
  • Then the network transfer rate, fixed with interface ppp0.
  • The temperature.
  • Battery remaining capacity. This one has more colors than others. Icon color indicates different states. Green is the battery is charged, using AC source. Yellow is for discharging, if capacity goes too low, it will blink with red color. Blue means the battery is charging.
  • The music note is for MPD. It shows artist name and track name. If the text is too long, it scrolls for you to read all text! If you click on icon, it brings up a window like this.
  • Sound volume.
  • Date and time. Clicking on the clock brings up a 3-month calendar.
  • The last information box. I stuff update, load average, processes count, and weather in a window, you will see after you click on that icon.

You can get my configurations.

I think I have everything I need, but I won't stop tweaking it.

Too many windows? Stuck in Alt-Tab Hell?

Ever felt why pressing Alt+Tab likes an endless loop, especially when your eyes and hands don't coordinate together well after you press one more time and get past to the window you really want.

I usually have only four windows on my screen: Terminal (urxvt + tmux), Firefox (Vimperator), SMPlayer (providing beats, or distraction?), and KeepNote. Just four and I often feel I can't switch to right window efficiently, I don't use mouse to select window most of time.

I wrote a script better-alt-tab.sh using wmctrl, so I can run something like

$ better-alt-tab.sh t

Then, terminal window will be selected. f for Firefox, m for SMPlayer or mplayer, and k for KeepNote. If runs without arguments, then it lists all windows using dzen, I don't use this function but Fluxbox's WorkspaceMenu.

Just bind it with window manager's keys, in my case, the keys configuration of Fluxbox.

Moving active window between screens

I have two monitors, one is laptop LCD, another is external monitor, both operate in 1680x1050 resolution. If I use xrandr to enable both, I would have 1680x2100 or 3360x1050 combined screen resolution, I am currently using 1680x2100. Since they are physically two separate two monitors/screens and they are not same size, 15.4" and 20", I would only focus on one of them. Sometimes, I want to move a window to another screen, I would have to drag and drop on another screen. There was always an issue, I didn't want to change the window's relative position to the left-top corner which the window is currently on. For example, win A's position is at 20, 1200, it's on the bottom screen. The relative position to the screnn is 20, 150 (1200-1050). I want to move it to top screen and stay at 20, 150. If I move it back, it will be at 20, 1200, again.

So, I wrote this script:

#!/bin/bash

# Can get from wmctrl -d
S_W=1680
S_H=1050

# Get info of current active window

win_id=$(xprop -root | sed '/_NET_ACTIVE_WINDOW(WINDOW)/ {s/.*\(0x[a-z0-9]\+\)/\1/;q} ; d')

read win_x win_y <<< "$(xwininfo -id $win_id | sed '/Absolute/ {s/[ :a-z-]//gi;p} ; d' | tr '\n' ' ')"
read win_w win_h <<< "$(xwininfo -id $win_id | sed '/\(Width\|Height\)/ {s/[ :a-z-]//gi;p} ; d' | tr '\n' ' ')"
read b_left _ b_top _ <<< "$(xprop -id $win_id | sed '/FRAME.*CARDINAL/ {s/[_=(),a-z]//gi;q} ; d')"

# Remove window decoration
((win_x-=b_left))
((win_y-=b_top))

case "$1" in
  u|up)
    ((win_y-=S_H))
    ((win_y < 0)) && exit 1
    ;;
  d|down)
    ((win_y+=S_H))
    ((win_y >= 2*S_H)) && exit 1
    ;;
  l|left)
    ((win_x-=S_W))
    ((win_x < 0)) && exit 1
    ;;
  r|right)
    ((win_x+=S_W))
    ((win_x >= 2*S_W)) && exit 1
    ;;
  *)
    exit 1
    ;;
esac

# Somehow, the window gets taller in Fluxbox after moves, has to supply height instead of -1
wmctrl -i -r $win_id -e "0,$win_x,$win_y,-1,$win_h"

I bind Win+Up and Win+Down in my ~/.fluxbox/keys:

Mod4 Up :exec move-active-win.sh u
Mod4 Down :exec move-active-win.sh d

Percent-encoding in pure Bash

Since I started to code for some websevice's authentication method, it's common that you need to use Percent-encoding somewhere. I used to use Perl do this such job:

$ echo -n $'Encoded string has 中文\n' | perl -p -e 's/([^A-Za-z0-9-._~])/sprintf("%%%02X", ord($1))/seg' ; echo
Encoded%20string%20has%20%E4%B8%AD%E6%96%87%0A

If you want to do it in pure Bash, there are two issues:

  1. To be able to feed Bash builtin printf with argument is same as ord($1) as shown above, and
  2. Commonly, Bash supports Unicode, which implies that you get character by character not byte character. A Unicode character could be multi-byte.

The first key is to have ord() in Bash. If you search for bash chr ord on Internet, you will find something like this:

$ printf "%%%02X" "'A" ; echo
%41

I took looks at few pages but it seems that many just copied from here and there, no explanations on single quote usage in "'A". I found the answer from info coreutils 'printf invocation':

  • If the leading character of a numeric argument is " or ' then its value is the numeric value of the immediately following character. Any remaining characters are silently ignored if the POSIXLY_CORRECT environment variable is set; otherwise, a warning is printed. For example, printf "%d" "'a" outputs 97 on hosts that use the ASCII character set, since a has the numeric value 97 in ASCII.

Second key is to get byte character. Amazingly, it's fairly simple, you just change locale, e.g. LANG=en_US.ISO8859-1.

A complete code is:

pe () {
  local LANG=en_US.ISO8859-1 ch i
  for ((i=0;i<${#1};i++)); do
    ch="${1:i:1}"
    [[ $ch =~ [._~A-Za-z0-9-] ]] && echo -n "$ch" || printf "%%%02X" "'$ch"
  done
  }

pe $'Encoded string has 中文\n' ; echo

The performance isn't good, on my computer, the encoding rate is about 3.84 kbytes/second, Perl's rate is about 2.182 Mbytes/second. But it should be enough for general use since usually you won't feed it with a file but a simple string.

lf-submit.sh - Bash mplayer Last.fm scrobbler

Since I started to watch music videos on YouTube, I have always wanted to write a YouTube scrobbler but I never did and probably would never do, because it might be messy in someway. I also have downloaded some from YouTube so I could enjoy better performance in full-screen with mplayer. A couple of days ago, I started to ponder about a mplayer a wrapper, which enables users having more controls. Such as a notification capable, or something else. But the main purpose of this wrapper is for being used with a Last.fm scrobbler.

And lf-submit.sh was born, a set of pure Bash scripts including three parts:

  • lf-mplayer-wrapper.sh: A mplayer wrapper, it parses outputs of mplayer. It can be used with mplayer GUI frontend, such as SMPlayer.
  • lf-scrobble.sh: A scrobbler, tt's invoked by wrapper, it reads metadata of files and invokes lf-submit.sh to send an API request.
  • lf-submit.sh: Submitter, it can be a standalone script. It's the real script which communication with Last.fm.

The scripts support Now playing and Scrobbling. The dependencies are perl, md5sum, and few common shell scripting programs. The scripts are licensed under the BSD License.

How to Install

Download three scripts and make sure scrobbler and submitter is in search path. As for wrapper, you can put it anyway and run it. Before you start the wrapper, you must run submitter to configure your account, you will be asked for your Last.fm username and given a link, which you can authorize the scripts to submit scrobbles.

If you use SMPlayer as frontend, change MPlayer executable in preference dialog to wrapper script.

The configuration files are stored at $XDG_CONFIG_HOME/lf-submit.sh/config. If anything goes wrong, you can run lf-submit.sh -r to re-configure.

How it Works?

The first step is to run wrapper. For example:

# Use absolute path
/path/to/lf-mplayer-wrapper.sh foo_video.ogg

# If it's in search path
lf-mplayer-wrapper.sh foo_video.ogg

Calling wrapper directly might not be what you want. My search path, it's more like this PATH=/home/username/bin:/usr/bin. My ~/bin is first place to look up for a command. In my case, I use a symlink:

ln -s /path/to/lf-mplayer-wrapper.sh ~/bin/mplayer

Therefore, /usr/bin/mplayer is wrapped by the wrapper. The wrapper uses which to find the real mplayer program.

Once the wrapper brings up the mplayer and supply all arguments which wrapper is given to the mplayer, it starts to parse mplayer's output. It can detect file changes, then do proper action with scrobbler:

# When new file played, wrapper runs
lf-scrobble.sh -n - /path/to/file.ogg

# When new file played after the end of another, wrapper runs
lf-scrobble.sh -s <TIMESTAMP> /path/to/file.ogg

-n means now playing and -s means scrobble. When scrobbles a track, timestamp of start time must be sent to Last.fm.

Scrobbler takes over, it firstly checks if there is a file named /path/to/file.lfs. File with extension lfs is the metadata file. The scripts do not use central database, the metadata file must sit next to media file, and there is no automatic way to have metadata. The metadata files should have content like:

track=track name blah blah
artist=the artist's name

These two lines are required, you can put more in, please consult with parameters sections of track.scrobble and track.updateNowPlaying.

If scrobbler has everything it needs to make a request to Last.fm, it then invokes submitter. Submitter accepts similar arguments.

lf-submit.sh <-n|-s> 'key1=value1' 'key2=value2' ...

For a scrobble, it would look like

lf-submit.sh -s 'track=Track Name' 'artist=Artist Name' 'timestamp=123456789'

The order of key-value pairs is not important, submitter will make them in right order internally.

Feedback

The scripts are still in unstable stage. Basically, you won't see any error messages from these scripts and that makes debugging harder. If you encounter problems, please provide information such as file name, metadata, mplayer and frontend player versions. It's better that you create a bug report, please prefix [lf-submit.sh] in summary field.

Bye bye Conky! Hello dzen!

Less than a week ago, I was happily read about dzen and had made my mind that I would replace Conky with dzen, though they are for different purposes basically.

Hours later, I had finished very basic configuration and I wrote my code from scratch. Here is the files I have added or modified. And this is a screenshot:

2010-12-04--19:52:14

The dzen is the right half at bottom of screen, that music info box is also a dzen. I roughly calculated memory usage, dzen used around 5.9MB and Conky used 9.9MB, and used less CPU time. But, it provides much less information for now.

If you want to use my scrips, then you should download dzen/ into ~/.dzen and sm4tik's icons into ~/.dzen/icons. The main script is status.sh, you shall run it and start to fix it for your environment, I am pretty sure it's not so portable. There are some stuff are fixed in code, such as terminal emulator, I use urxvtc. Also, I use Fluxbox, that's why that commit has some Fluxbox files.

Almost all icons are clickable and give you some popup window. The most noticble is probably MPD info box, you can click on music note to bring it up, or it will be brought up automatically when song changes. It requires lf-playcount-image.sh, you can read my post about the configuration. It was written for Conky, but it can also work for dzen.

Also, I am using 0.9.5-svn (revision 271) of dzen, 0.8.5 doesn't support some stuff.

There are still a lot of stuff need to be added, but that's all for now!

Time to umemerge Conky!

Using xinput to disable keyboard, mouse, touchpad, etc

I recently found out you can use xinput to enable or disable an input device. I used to use synclient to disable my touchpad, or using this tricky way to disable my laptop keyboard.

The first step to get device name or id of the device:

$ xinput list
 Virtual core pointer                          id=2    [master pointer  (3)]
    Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
    USB Optical Mouse                         id=8    [slave  pointer  (2)]
    SynPS/2 Synaptics TouchPad                id=7    [slave  pointer  (2)]
 Virtual core keyboard                         id=3    [master keyboard (2)]
     Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
     Sleep Button                              id=9    [slave  keyboard (3)]
     Power Button                              id=10   [slave  keyboard (3)]
     Video Bus                                 id=11   [slave  keyboard (3)]
     AT Translated Set 2 keyboard              id=6    [slave  keyboard (3)]

For touchpad, the device name is 'SynPS/2 Synaptics TouchPad' and id is 7; for keyboard, they are 'AT Translated Set 2 keyboard' and 6. Next step is to know the properties of a device:

$ xinput list-props 'AT Translated Set 2 keyboard'
Device 'AT Translated Set 2 keyboard':
        Device Enabled (127):   1

This keyboard only has a property 'Device Enabled' whose value is 1, that means this keyboard is enabled. To test disabling:

sleep 0.1 ; xinput set-prop 'AT Translated Set 2 keyboard' 'Device Enabled' 0 ; sleep 5 ; xinput set-prop 'AT Translated Set 2 keyboard' 'Device Enabled' 1

The first sleep 0.1 is to prevent enter keypress being repeatedly sent somehow when you directly disable the keyboard, I am guessing when you hit the enter and the command is executed, meaning the keyboard is disabled, but the keyup event is not yet sent, so X still thinks the enter key is pressed down.

Another simple way is to use id, so you don't need long device name:

sleep 0.1 ; xinput set-prop 8 127 0 ; sleep 5 ; xinput set-prop 8 127 1

8 is id of this keyboard and 127 is the property id of 'Device Enabled'. When you list properties of device using list-props, the numbers after property names are the property ids. It seems that 'Device Enabled' always has id 127, but device id is not always the same. It depends on device attached time, who shows up first who gets next available id.

One more thing to note: I don't need root privilege to set property value.