Telephone Ring

This is a little thing I wrote that simulates a telephone ring via the Linux console. The underlying console is used so that it can be used for Tcl and/or Tk.

I'd rather that this work via an open'ed FD on /dev/console but I can't seem to get that to work. It will ring ok, but doesn't change the frequency as it does via echo. But it does open a door to a lot of simple sound possibilities via the PC speaker, and without any extensions. Alarm clocks, fancy alerts, whatever...

This is not perfected yet. If run under a user's desktop it needs to be run by root (su root),

 # ring proc by Mike Tuxford
 proc ring {} {
   #                       duration (ms)
   exec /bin/echo -e '\\33\[11\;40]' > /dev/console
   for {set i 0} {$i < 8} {incr i} {
     #                       freq. (HZ)
     exec /bin/echo -e '\\33\[10\;770]' > /dev/console
     exec /bin/echo -e '\\a' > /dev/console
     after 40
     exec /bin/echo -e '\\33\[10\;870]' > /dev/console
     exec /bin/echo -e '\\a' > /dev/console
     if {$i == 3} {
       after 200
     } else {
       after 40
     }
   }
   # From the kernel console.c
   # Here is the default bell parameters: 750HZ, 1/8th of a second
   # reset the defaults
   exec /bin/echo -e '\\33\[10\;750\]\\33\[11\;125\]' > /dev/console
   return
 }
 ring

rmax - As it is, this program does funny things on the console. Mind you, that single quotes don't have a special meaning in Tcl. To quote the escape sequences, the single quotes should become pairs of braces, the single backslashes can go, and the double backslashes become single ones. Also echo should get a -n switch to avoid printing lots of newlines to the console. So the echos will look like this:

 exec /bin/echo -en {\33[11;40]} > /dev/console

A version that doesn't need the external echo could look like this:

 proc ring {} {
    set fd [open /dev/console w]
    fconfigure $fd -buffering none
    #                       duration (ms)
    puts -nonewline $fd "\033\[11;40\]"
    for {set i 0} {$i < 8} {incr i} {
        #                       freq. (HZ)
        puts -nonewline $fd "\033\[10;770\]\a"
        after 40
        puts -nonewline $fd "\033\[10;870\]\a"
        if {$i == 3} {
            after 200
        } else {
            after 40
        }
    }
    # From the kernel console.c
    # Here is the default bell parameters: 750HZ, 1/8th of a second
    # reset the defaults
    puts -nonewline $fd "\033\[10;750\]\033\[11;125\]"
    close $fd
    return
 }
 ring

MT Great additions rmax! This still requires root. Under an X desktop it won't work on an xterm pts/<n> but will work on the tty<n> that the X server was launched from without requiring root access. Getting the origin tty<n> for the server is still an issue. There could be multiple instances of X, and the tty<n> may be several levels of parent processes higher depending on how the X server/desktop/terminal were launched.


MT Ok, a version that can be used by a user rather than root, even under an X desktop we hope. It still won't work for gdm, xdm, etc and those kinds of desktops that have no underlying tty.

 proc getTTY {} {
   if {[string match tty* [exec /bin/ps h -p [pid] -o tty]]} {
     # it's a tty shell
     return [exec /bin/ps h -p [pid] -o tty]
   }
   # try to find parent tty
   foreach cmd {xinit startx startkde gnome-session} {
     if {[catch {exec /bin/ps h -C $cmd -o tty} t]} {
       # no such process so skip it
       continue
     }
     if {[string match tty* $t]} {
       return $t
     }
   }
   return "none"
 }
 proc ring {} {
   set tty [getTTY]
   if {$tty == "none"} {
     return 0
   } else {
     set tty [file join /dev $tty]
   }
   set fd [open $tty r+]
   fconfigure $fd -buffering none
   puts -nonewline $fd "\033\[11;40\]"
   for {set i 0} {$i < 8} {incr i} {
     puts -nonewline $fd "\033\[10\;770]\a"
     after 40
     puts -nonewline $fd "\033\[10\;870]\a"
     if {$i == 3} {
       after 200
     } else {
       after 40
     }
   }
   puts -nonewline $fd "\033\[11;125\]\033\[10\;750]"
   close $fd
   return 1
 }
 # starter
 if {![ring]} {
   # do something else here
   puts "can't use ring"
 }