(hello

‘world)

What I’ve been up to this month.

So I’ve noticed that I have 23 posts for January (I need a life), and only 4 for February. What have I been up to?

Mostly delving more into Scheme(see footnote), nearly to the exclusion of Common Lisp, other than the occasional tweaking of my StumpWM config.

A few years ago when I was still doing security (of the rent-a-cop sort) I was reading some Scheme book or another (I don’t remember which) in PDF format on my old Palm T3 running LispME. I miss my T3, I thought the format was just about perfect, and it was well built. I wouldn’t have given it away if I knew it would finally be figured out how to get Linux on it. Oh well, I have a laptop now. Anyway, I don’t really know why I never got more into it… I loved it, but I probably got distracted by other things after I moved (like trying to find a fucking job -_-). I don’t really know how it caught my interest again either (along with Common Lisp this time), but I figure it was inevitable with nothing else really going on.

So now I spend large portions of my time coding Scheme, and I’ll talk about a couple of the things I’ve been working on, the things I plan to work on, and some things I might eventually work on.

I wrote an IRC bot in Common Lisp as I always do when learning a new language. It was mostly a matter of the quote given in my earlier post on Scheme vs. Common Lisp, “Most newcomers eventually (and independently) decide the same thing: Scheme is a better language, but Common Lisp is the right choice for production work. CL has more libraries, and the implementations are somewhat more compatible than Scheme implementations, particularly with respect to macros.“.

But I decided, fuck that, it is the better language, and reimplemented it in Scheme. But I wasn’t happy, there wasn’t enough separation of layers, and the code to make the network interface bits portable across different Scheme implementations was a gigantic cond expression requiring the user (I always (try to) code with the assumption that other people will have to use/read it) to set a variable to the name of their implementation. All that complexity just to open a socket is bullshit, and doesn’t belong in the bot’s code.

I knew I must have been reinventing wheels, so I started looking around, and I found what I needed, Snow. It’s not perfect, but it’s still a fairly new project and pretty much does the job. And more importantly, it already has things like a portable TCP/IP package, among many others I’ve also found useful, and works on pretty much all of the major implementations. Documentation for specific packages can be sparse at times, but they *do* always at least tell you the procedures they make available and what args they expect. And one of the nice things about Lisp in general is that it’s not generally a bitch to figure out someone else’s code, should you need to.

With that, I set about writing an IRC library for Scheme using the Snow framework, to eventually use to rewrite my bot. And I dare say it’s nearing completion. The only thing currently lacking is DCC support, which will come at some later time. The IRC client protocol itself and CTCP support are pretty much there however. It was originally inspired by cl-irc but so much of it wound up being based around the ideas of the IRC “egg” for Chicken that I consider it a derivitive thereof (and is happily BSD licensed anyway). While it does have its issues (such as not checking whether a nick change actually worked…), its general design, which I thought was quite simply fucking brilliant, is obviously far superior to that of cl-irc’s (especially in how hooks/handlers and CTCP are handled). It seems I learned about as much Scheme from reading that code as I have reading any books. I only hope that my IRC code may be useful to Chicken’s, as mine likely wouldn’t exist as it does without it, so I’m going to wind up trying to write a couple patches for the parts of the egg I found lacking. Open source is grand, isn’t it?

Once that’s all done, I’ll be using it to (re-re-)implement my IRC bot in Scheme. And I think, for once, I’ll finally have the bot I always really wanted instead of a hack job that has to get around the limitations of the language it’s written in.

One of the things the bot does is retrieve information from Last.FM, so I’ll need to write an implementation of the API for Scheme too (obviously, based on the excellently written cl-audioscrobbler).

And then, I’ll get back to work on my MUD based on the ideas in Mooix NG. I’ve got a basic framework done making heavy use of Common Lisp’s CLOS, as an object system seemed the obvious tool for the job, and CLOS is simply sexy to work with. But working with Common Lisp just didn’t feel quite right, and I wound up looking more into Scheme(see other footnote).

I’m also vaguely entertaining the thought of writing Snow packages for the 9P and Freenet protocols. But what I’d really like to see is a widely supported SRFI for Foreign Function Interfaces(FFI). That would make the others (and other cool shit like POSIX) nearly trivial to do portably across implementations. Although threading at least has a couple of its own SRFI’s anyway. But in the mean time, a lot of Scheme implementations do support FFI to one extent of another… maybe someday when I’ve got more experience I’ll see about writing a Snow package to smooth over their differences, as CFFI does for Common Lisp.

So yeah. Do I need to get laid, or what?

(footnote) As it breaks compatibility both literally and ideologically to previous Scheme standards, I do not refer to R6RS as Scheme. When I say Scheme, assume R5RS.

(other footnote) Separate namespaces making me care whether it’s a variable or if I need to use #’, having to remember to use gensym in macros, should it be defvar or defparameter, … I don’t want to give a shit about the language, I just want to write what I’m trying to write, and when trying to write a MUD it was getting pretty old. Sure, I could abstract all that away. Lisp is GREAT for that sort of thing, and I started to do just that. But then I thought, why should I make Common Lisp be Scheme-like, when there’s already Scheme? If the language I really want is already there… use it.

February 25, 2008 Posted by | cl-audioscrobbler, Common Lisp, IRC, Last.FM, Meta, Programming, Scheme, StumpWM | Leave a Comment

.stumpwmrc 0.7


; -*-lisp-*-

;;; 0.7

;; 2008/02/13

;; Replaced my MusicPD code with Patzy's as it's far more complete.

;; Using external 'unclutter' again.
;; (run-with-timer 5 5 'banish-pointer) became too annoying.

;; A few cleanups, mostly involving variable names.

(in-package :stumpwm)

;; Include Patzy's MPD code.
(load "~/.stumpwm/contrib/mpd.lisp")

;;; Internal variable definitions.

(defparameter *foreground-color* "darkcyan")
(defparameter *background-color* "black")
(defparameter *border-color* *foreground-color*)

(setf *format-time-string-default* "%a %b %e %k:%M")

(setf *mpd-port* 2100)
(setf *mpd-volume-step* 10)

;;; Internal function definitions.

;; Found at:
;; http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(defun cat (&rest strings) "A shortcut for (concatenate 'string foo bar)."
  (apply 'concatenate 'string strings))

;;; Theme.

;; Window border colors.
(set-focus-color *foreground-color*)
(set-unfocus-color *background-color*)

;; Input box colors.
(set-fg-color *foreground-color*)
(set-bg-color *background-color*)
(set-border-color *border-color*)

;; Modeline colors.
(setf *mode-line-foreground-color* *foreground-color*)
(setf *mode-line-background-color* *background-color*)
(setf *mode-line-border-color* *border-color*)

;; Background.
(run-shell-command (cat "xsetroot -solid " *background-color*))

;;; Init stuff.

;; Make frames and windows 1-indexed.
;; See: http://lists.gnu.org/archive/html/stumpwm-devel/2006-08/msg00002.html
;; Found at: http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(setf *frame-number-map* "1234567890")
(setf *window-number-map* "1234567890") ; This doesn't actually do anything.

;; Rename the first group to Browse and create the other groups.
;; Found at: http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(setf (group-name (first (screen-groups (current-screen)))) "Browse")
(run-commands "gnewbg Edit" "gnewbg Term" "gnewbg Comms" "gnewbg Misc")

;; Change the prefix key
;; keycode 115 = F20 in ~/.xmodmaprc, 115 being the left "windows" key.
(set-prefix-key (kbd "F20"))

;; Set up X cursor and colors.
(run-shell-command (cat "xsetroot -cursor_name left_ptr -fg " *background-color* ; Cursor body
                                                      " -bg " *border-color*)) ; Cursor outline

;; Keep the X cursor out of the way.
(run-shell-command "unclutter -idle 5 -jitter 5 -root")

;; Configure and start the modeline. Colors are handled above.
(setf *mode-line-border-width* 1)
(setf *mode-line-pad-x* 1)
(setf *mode-line-pad-y* 1)
(setf *mode-line-position* :bottom)
(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.
(setf *screen-mode-line-format* (list "((%n %w) (" ; Current group and frames
                                      `(:eval (format-time-string))
                                      ") (%m))")) ; Patzy's MPD code.

;; Switch mode-line on only if needed. Found at:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc
(if (not (head-mode-line (current-head)))
     (toggle-mode-line (current-screen) (current-head)))

;; Found this tidbit browsing the source. Defaults to :ignore
(setf *mouse-focus-policy* :click) ; I'm fucking lame.

;;; Keyboard shortcuts.

;; Fluxbox-style Alt-F# virtual desktop (group in StumpWM-speak) switching. Modified from:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc
(dotimes (i 13)
  (unless (eq i 0) ; F0 is non-existant and will error.
    (define-key *top-map* (kbd (format nil "M-F~a" i)) (format nil "gselect ~a" i))))

;; Applications.
(define-key *root-map* (kbd "b") "exec firefox ")
(define-key *root-map* (kbd "e") "exec xemacs ")
(define-key *root-map* (kbd "c") (cat "exec urxvt -fg " *foreground-color*
                                                " -bg " *background-color*
                                                " -pr " *foreground-color*
                                                " +sb "))

;; Audio controls, uses un-numlocked keypad.
;; Some keys duplicated, not sure which I prefer yet.
(define-key *top-map* (kbd "KP_Up")       "mpd-volume-up")
(define-key *top-map* (kbd "KP_Add")      "mpd-volume-up") ; Redundant
(define-key *top-map* (kbd "KP_Down")     "mpd-volume-down")
(define-key *top-map* (kbd "KP_Subtract") "mpd-volume-down") ; Redundant
(define-key *top-map* (kbd "KP_Left")     "mpd-prev")
(define-key *top-map* (kbd "KP_Right")    "mpd-next")
(define-key *top-map* (kbd "Num_Lock")    "mpd-toggle-pause") ; The light indicates play/pause ;) 

February 13, 2008 Posted by | Common Lisp, StumpWM | Leave a Comment

A really cool StumpWM config for MPD

[ Update: Last I checked, this was included with the StumpWM sources under /contrib ]

Alas, it would seem I poorly reinvented a wheel. Piss poorly, compared to this. I was looking at the change history for StumpWM’s Wiki which led me to http://appart.kicks-ass.net/patzy/files/mpd.lisp.
Unlike my extremely primitive couple of functions, this is essentially a complete MPD client that just happens to use StumpWM as its UI, written by someone with obviously far more Lisp experience than I. I reproduce its current contents below:

;;; MPD client & formatters for stumpwm
;;;
;;; Copyright 2007 Morgan Veyret.
;;;
;;; This module is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;;
;;; This module is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this software; see the file COPYING.  If not, write to
;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;; Boston, MA 02111-1307 USA
;;;

;;; USAGE:
;;;
;;; Put:
;;;
;;;     (load "/path/to/mpd.lisp")
;;;
;;; In your ~/.stumpwmrc
;;;
;;; Then you can use "%m" in your mode line format.
;;; You can also use the various commands defined at the end of the file
;;;
;;; NOTES:
;;; see http://mpd.wikia.com/wiki/Protocol_Reference for full protocol
(in-package :stumpwm)

;;mpd client
(defparameter *mpd-socket* nil)
(defparameter *mpd-server* "localhost")
(defparameter *mpd-port* 6600)

(defun mpd-send (command)
  "Send command to stream ending with newline"
  (with-mpd-connection
   (ext:write-char-sequence
    (concatenate  'string command (string #\Newline))
    *mpd-socket*)))

(defun mpd-termination-p (str)
  (or (mpd-error-p str)
      (mpd-ok-p str)))

(defun mpd-error-p (str)
  (when (>= (length str) 3)
    (equal (subseq str 0 3) "ACK")))

(defun mpd-ok-p (str)
  (equal str "OK"))

(defun mpd-tokenize (str)
  (let ((pos (position #\: str)))
    (list (read-from-string (concatenate 'string
                                         ":"
                                         (subseq str 0 pos)))
          (subseq str (+ pos 2)))))

(defun assoc-value (name list)
  (cadr (assoc name list)))

(defun mpd-receive ()
  "Returns a list containing all data sent by mpd."
  (with-mpd-connection
   (loop for i = (read-line *mpd-socket*)
         when (mpd-error-p i)
         do (message "Error sent back by mpd: ~a" i)
         until (mpd-termination-p i)
         collect (mpd-tokenize i))))

(defun mpd-connect ()
  "Connect to mpd server"
  (setf *mpd-socket*
        (handler-case (socket:socket-connect *mpd-port* *mpd-server*
                                             :element-type 'character)
                      ((or system::simple-os-error error)
                       (err)
                       (format t  "Error connecting to mpd: ~a~%" err))))
  (when *mpd-socket*
    (read-line *mpd-socket*)))

(defun mpd-disconnect ()
  "Disconnect from mpd server"
  (with-mpd-connection
   (close *mpd-socket*)
   (setf *mpd-socket* nil)))

(defun mpd-send-command (cmd)
  (mpd-send cmd)
  (mpd-receive))

(defmacro with-mpd-connection (&body body)
  `(if *mpd-socket*
       (handler-case (progn ,@body)
                     (error (c) (progn
                                  (message "Error with mpd connection: ~a" c)
                                  (setf *mpd-socket* nil))))
     (message "Error: not connected to mpd~%")))

;;mpd formatter
(dolist (a '((#\m fmt-mpd-status)))
  (push a *screen-mode-line-formatters*))

(defparameter *mpd-current-song* nil)
(defparameter *mpd-status* nil)

(defun mpd-update-current-song ()
  (setf *mpd-current-song* (mpd-send-command "currentsong")))
(defun mpd-update-status ()
  (setf *mpd-status* (mpd-send-command "status")))

(defun format-mpd-current-song (current-song)
  (let ((artist (assoc-value :artist current-song))
        (album (assoc-value :album current-song))
        (title (assoc-value :title current-song))
        (file (assoc-value :file current-song)))
    (if (or (null artist)
            (null album)
            (null title))
        (format nil "~a" file)
      (format nil "~a (~a): ~a"
              artist
              album
              title))))

(defun format-mpd-status (status)
  (let ((mpd-state (assoc-value :state status)))
     (cond
       ((equal mpd-state "play")
        (format nil "Playing [~a;~a] (~a%)"
                (if (equal (assoc-value :random *mpd-status*)
                           "0")
                    "_"
                  "S")
                (if (equal (assoc-value :repeat *mpd-status*)
                           "0")
                    "_"
                  "R")
                (assoc-value :volume *mpd-status*)))
       ((equal mpd-state "pause")
        (format nil "Paused [~a;~a]"
                (if (equal (assoc-value :random *mpd-status*)
                           "0")
                    "_"
                  "S")
                (if (equal (assoc-value :repeat *mpd-status*)
                           "0")
                    "_"
                  "R")))
       ((equal mpd-state "stop")
        (format nil "Stopped  [~a;~a]"
                (if (equal (assoc-value :random *mpd-status*)
                           "0")
                    "_"
                  "S")
                (if (equal (assoc-value :repeat *mpd-status*)
                           "0")
                    "_"
                  "R"))))))

;;FIXME: update status based on last current song time
;;FIXME: this means updating status on commands calls
(defun fmt-mpd-status (ml)
  (declare (ignore ml))
  (if *mpd-socket*
      (with-mpd-connection
       (mpd-update-status)
       (if (equal "stop" (assoc-value :state *mpd-status*))
           (format nil "~a"
                   (format-mpd-status *mpd-status*))
         (progn (mpd-update-current-song)
                (format nil "~a: ~a"
                        (format-mpd-status *mpd-status*)
                        (format-mpd-current-song *mpd-current-song*)))))
    "Not connected"))

;;mpd commands
(defparameter *mpd-volume-step* 5)

(define-stumpwm-command "mpd-select-song-in-playlist" ()
  (let ((status (mpd-send-command "status")))
    (labels ((pick (options)
                   (let ((selection
                          (select-from-menu (current-screen) options
                                            "Play song in playlist"
                                            (if (equal
                                                 (assoc-value :state status)
                                                 "play")
                                                (parse-integer
                                                 (assoc-value :song status))
                                              0))))
                     (cond
                      ((null selection)
                       (throw 'stumpwm::error "Abort."))
                      (t selection)))))
      (let* ((response (mpd-send-command "playlistinfo"))
             (result (mapcar #'cadr (remove-if (lambda (item)
                                                 (not (equal :file
                                                             (first item))))
                                               response))))
          (let* ((choice (pick result))
               (song-number (position choice result)))
          (mpd-send-command (format nil "play ~d" song-number)))))))

(define-stumpwm-command "mpd-connect" ()
  (message "~a" (mpd-connect)))

(define-stumpwm-command "mpd-disconnect" ()
  (mpd-disconnect))

(define-stumpwm-command "mpd-send-command" ((cmd :rest "Send mpd: "))
  (mpd-send cmd)
  (mpd-receive))

(define-stumpwm-command "mpd-toggle-pause" ()
  (let ((status (mpd-send-command "status")))
    (cond
     ((equal (assoc-value :state status)
             "play") (mpd-send-command "pause 1"))
     ((equal (assoc-value :state status)
             "pause") (mpd-send-command "pause 0")))))

(define-stumpwm-command "mpd-toggle-random" ()
  (let ((status (mpd-send-command "status")))
    (cond
     ((equal (assoc-value :random status) "0") (mpd-send-command "random 1"))
     ((equal (assoc-value :random status) "1") (mpd-send-command "random 0")))))

(define-stumpwm-command "mpd-toggle-repeat" ()
  (let ((status (mpd-send-command "status")))
    (cond
     ((equal (assoc-value :repeat status) "0") (mpd-send-command "repeat 1"))
     ((equal (assoc-value :repeat status) "1") (mpd-send-command "repeat 0")))))

(define-stumpwm-command "mpd-play" ()
  (mpd-send-command "play"))

(define-stumpwm-command "mpd-stop" ()
  (mpd-send-command "stop"))

(define-stumpwm-command "mpd-next" ()
  (mpd-send-command "next"))

(define-stumpwm-command "mpd-prev" ()
  (mpd-send-command "previous"))

(define-stumpwm-command "mpd-set-volume" ((vol :rest "Set volume to: "))
  (mpd-send-command (format nil "setvol ~a" vol)))

(define-stumpwm-command "mpd-volume-up" ()
  (let* ((status (mpd-send-command "status"))
         (vol (read-from-string (assoc-value :volume status))))
    (mpd-send-command (format nil "setvol ~a" (+ vol *mpd-volume-step*)))))

(define-stumpwm-command "mpd-volume-down" ()
  (let* ((status (mpd-send-command "status"))
         (vol (read-from-string (assoc-value :volume status))))
    (mpd-send-command (format nil "setvol ~a" (- vol *mpd-volume-step*)))))

(define-stumpwm-command "mpd-clear" ()
  (mpd-send-command "clear"))

(define-stumpwm-command "mpd-update" ()
  (message "~a" (mpd-send-command "update")))

(define-stumpwm-command "mpd-current-song" ()
  (message "~a" (format-mpd-current-song (mpd-send-command "currentsong"))))

(define-stumpwm-command "mpd-playlist" ()
  (let* ((response (mpd-send-command "playlistinfo"))
         (result (mapcar #'cadr (remove-if (lambda (item)
                                             (not (equal :file
                                                         (first item))))
                                           response))))
    (if (< (length result) 80)
        (message "Current playlist (~a): ~%^7*~{~a~%~}"
                 (length result)
                 result)
      (message "~a files in playlist" (length result)))))

(define-stumpwm-command "mpd-add-file" ((file :rest "Add file to playlist: "))
  (mpd-send-command (format nil "add \"~a\"" file)))
(define-stumpwm-command "mpd-search-and-add-artist"
  ((what :rest "Search & add artist to playlist: "))
  (let* ((response (mpd-send-command (format nil "search artist \"~a\"" what)))
         (result (mapcar #'cadr (remove-if (lambda (item)
                                             (not (equal :file
                                                         (first item))))
                                           response))))
    (loop for i in result
          do (mpd-send-command (format nil "add \"~a\"" i)))
    (if (< (length result) 80)
        (message "Added ~a files: ~%^7*~{~a~%~}"
                 (length result)
                 result)
      (message "~a files added" (length result)))))

(define-stumpwm-command "mpd-search-artist" ((what :rest "Search artist: "))
  (mpd-send-command (format nil "search artist \"~a\"" what)))
(define-stumpwm-command "mpd-search-file" ((what :rest "Search file: "))
  (mpd-send-command (format nil "search file \"~a\"" what)))
(define-stumpwm-command "mpd-search-title" ((what :rest "Search title: "))
  (mpd-send-command (format nil "search title \"~a\"" what)))
(define-stumpwm-command "mpd-search-album" ((what :rest "Search album: "))
  (mpd-send-command (format nil "search album \"~a\"" what)))
(define-stumpwm-command "mpd-search-genre" ((what :rest "Search genre: "))
  (mpd-send-command (format nil "search genre \"~a\"" what)))

;;Key map
;;FIXME: maybe some inferior mode would be a good idea (see resize in user.lisp)
(setf *mpd-search-map*
      (let ((m (make-sparse-keymap)))
        (define-key m (kbd "a") "mpd-search-artist")
        (define-key m (kbd "A") "mpd-search-album")
        (define-key m (kbd "t") "mpd-search-title")
        (define-key m (kbd "f") "mpd-search-file")
        (define-key m (kbd "g") "mpd-search-genre")
        m))

(setf *mpd-map*
      (let ((m (make-sparse-keymap)))
        (define-key m (kbd "SPC") "mpd-toggle-pause")
        (define-key m (kbd "s") "mpd-toggle-random")
        (define-key m (kbd "r") "mpd-toggle-repeat")
        (define-key m (kbd "p") "mpd-play")
        (define-key m (kbd "o") "mpd-stop")
        (define-key m (kbd "m") "mpd-next")
        (define-key m (kbd "l") "mpd-prev")
        (define-key m (kbd "c") "mpd-clear")
        (define-key m (kbd "u") "mpd-update")
        (define-key m (kbd "a") "mpd-search-and-add-artist")
        (define-key m (kbd "q") "mpd-select-song-in-playlist")
        (define-key m (kbd "z") "mpd-playlist")
        (define-key m (kbd "v") "mpd-set-volume")
        (define-key m (kbd "e") "mpd-volume-up")
        (define-key m (kbd "d") "mpd-volume-down")
        (define-key m (kbd "S") *mpd-search-map*)
        m))

January 14, 2008 Posted by | Common Lisp, MPD, StumpWM | Leave a Comment

.stumpwmrc 0.6


; -*-lisp-*-

;;; 0.6

;; 2008/01/11

;; Renamed (mpc) to (mpd-command).
;; Wrote that custom (current-song-info), and changed its name to (mpd-info).
;; It uses CL-PPCRE but we don't need to :use it, StumpWM already does.
;; And neither of them are likely portable, though it would be trivial to make them so if anyone cared.
;; Would have done it sooner but I was too busy passing my NREMT-B license exam.

(in-package :stumpwm)

;;; Internal variable definitions.

(defparameter FOREGROUND-COLOR "green")
(defparameter BACKGROUND-COLOR "black")
(defparameter BORDER-COLOR FOREGROUND-COLOR)

(setf *format-time-string-default* "%a %b %e %k:%M")

(defparameter MPD-HOST "127.0.0.1")
(defparameter MPD-PORT 2100)
(defparameter MPD-PASS "")

;;; Internal function definitions.

;; Found at:
;; http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(defun cat (&rest strings) "A shortcut for (concatenate 'string foo bar)."
  (apply 'concatenate 'string strings))

;;; My MPD "client". It's pretty clunky, but it works.

;; These are used by mpd-info.
(defvar artist-scanner (cl-ppcre:create-scanner "^Artist:\\s(.*)"))
(defvar title-scanner (cl-ppcre:create-scanner "^Title:\\s(.*)"))

;; Returns "(Artist - Title)"
(defun mpd-info (host port pass) "Return current song information from MPD."
  (with-open-stream  (socket (socket:socket-connect port host)) ; Likely unportable from CLISP.
    (format socket "password ~a~%" pass)
    (format socket "currentsong~%")
    (cat (loop :for line = (read-line socket nil nil) :while line
           :do (if (cl-ppcre:scan artist-scanner line)
                  (return (subseq (cl-ppcre:scan-to-strings artist-scanner line) 8))))
         " - "
         (loop :for line = (read-line socket nil nil) :while line
           :do (if (cl-ppcre:scan title-scanner line)
                  (return (subseq (cl-ppcre:scan-to-strings title-scanner line) 7)))))))

;; This sends our vol up/down and prev/next song and pause/unpause commands.
(defun mpd-command (host port pass command) "Pass commands to MPD."
  (with-open-stream  (socket (socket:socket-connect port host :timeout 1)) ; Likely unportable from CLISP.
   (format socket "password ~a~%" pass)
   (format socket "~a~%" command)))

;;; Theme.

;; Window border colors.
(set-focus-color FOREGROUND-COLOR)
(set-unfocus-color BACKGROUND-COLOR)

;; Input box colors.
(set-fg-color FOREGROUND-COLOR)
(set-bg-color BACKGROUND-COLOR)
(set-border-color BORDER-COLOR)

;; Modeline colors.
(setf *mode-line-foreground-color* FOREGROUND-COLOR)
(setf *mode-line-background-color* BACKGROUND-COLOR)
(setf *mode-line-border-color* BORDER-COLOR)

;; Background.
(run-shell-command (cat "xsetroot -solid " BACKGROUND-COLOR))

;;; Init stuff.

;; Make frames 1-indexed.
;; See: http://lists.gnu.org/archive/html/stumpwm-devel/2006-08/msg00002.html
;; Found at: http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(setf *frame-number-map* "1234567890") ; Still doesn't seem to work.

;; Rename the first group to Browse and create the other groups.
;; Found at: http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(setf (group-name (first (screen-groups (current-screen)))) "Browse")
(run-commands "gnewbg Edit" "gnewbg Term" "gnewbg Comms" "gnewbg Misc")

;; Change the prefix key
;; keycode 115 = F20 in ~/.xmodmaprc, 115 being the left "windows" key.
(set-prefix-key (kbd "F20"))

;; Set up X cursor and colors.
(run-shell-command (cat "xsetroot -cursor_name left_ptr -fg " BACKGROUND-COLOR ; Cursor body
                                                      " -bg " BORDER-COLOR)) ; Cursor outline

;; Keep the X cursor out of the way. Even when I want it.
(run-with-timer 5 5 'banish-pointer)

;; Configure and start the modeline. Colors are handled above.
(setf *mode-line-border-width* 1)
(setf *mode-line-pad-x* 1)
(setf *mode-line-pad-y* 1)
(setf *mode-line-position* :bottom)
(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.
(setf *screen-mode-line-format* (list "(%n %w) (" ; Current group and frames
                                      `(:eval (format-time-string))
                                      ") (" ; Just a spacer
                                      `(:eval (mpd-info MPD-HOST MPD-PORT MPD-PASS)) ; Defined above
                                      ")"))

;; Switch mode-line on only if needed. Found at:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc
(if (not (head-mode-line (current-head)))
     (toggle-mode-line (current-screen) (current-head)))

;; Found this tidbit browsing the source. Defaults to :ignore
(setf *mouse-focus-policy* :click) ; I'm fucking lame.

;;; Keyboard shortcuts.

;; Fluxbox-style Alt-F# virtual desktop (group in StumpWM-speak) switching. Modified from:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc
(dotimes (i 13)
  (unless (eq i 0) ; F0 is non-existant and will error.
    (define-key *top-map* (kbd (format nil "M-F~a" i)) (format nil "gselect ~a" i))))

;; Applications.
(define-key *root-map* (kbd "b") "exec firefox ")
(define-key *root-map* (kbd "e") "exec xemacs ")
(define-key *root-map* (kbd "c") (cat "exec urxvt -fg " FOREGROUND-COLOR
                                                " -bg " BACKGROUND-COLOR
                                                " -pr " FOREGROUND-COLOR
                                                " +sb "))

;; Audio controls, uses un-numlocked keypad.
;; Some keys duplicated, not sure which I prefer yet.
(define-key *top-map* (kbd "KP_Up")       "mpc-volume-up")
(define-key *top-map* (kbd "KP_Add")      "mpc-volume-up") ; Redundant
(define-key *top-map* (kbd "KP_Down")     "mpc-volume-down")
(define-key *top-map* (kbd "KP_Subtract") "mpc-volume-down") ; Redundant
(define-key *top-map* (kbd "KP_Left")     "mpc-song-prev")
(define-key *top-map* (kbd "KP_Right")    "mpc-song-next")
(define-key *top-map* (kbd "Num_Lock")    "mpc-pause") ; The light indicates play/pause ;) 

(define-stumpwm-command "mpc-volume-up" () "Increase MPD playback volume."
 (mpd-command MPD-HOST MPD-PORT MPD-PASS "volume +5"))

(define-stumpwm-command "mpc-volume-down" () "Decrease MPD playback volume."
 (mpd-command MPD-HOST MPD-PORT MPD-PASS "volume -5"))

(define-stumpwm-command "mpc-song-next" () "Switches MPD playback to next song."
 (mpd-command MPD-HOST MPD-PORT MPD-PASS "next"))

(define-stumpwm-command "mpc-song-prev" () "Switches MPD playback to previous song."
 (mpd-command MPD-HOST MPD-PORT MPD-PASS "previous"))

(define-stumpwm-command "mpc-pause" () "Pause/unpause MPD"
 (mpd-command MPD-HOST MPD-PORT MPD-PASS "pause"))

January 13, 2008 Posted by | Common Lisp, StumpWM | Leave a Comment

.stumpwmrc 0.5


; -*-lisp-*-

;;; 0.5

;; 2008/01/09

;; Moved all the audio commands to un-numlocked keypad. Who uses those keys anyway?
;; Don't need the prefix key this way either, much more efficient.

;; Added Fluxbox-style alt-F# virtual desktop (group in StumpWM speak) switch. Modified from:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc

;; Replaced call to (mode-line) with something more intelligent. Found at:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc

(in-package :stumpwm)

;;; Internal variable definitions.

(defparameter FOREGROUND-COLOR "green")
(defparameter BACKGROUND-COLOR "black")
(defparameter BORDER-COLOR FOREGROUND-COLOR)

(setf *format-time-string-default* "%a %b %e %k:%M")

(defparameter MPD-HOST "127.0.0.1")
(defparameter MPD-PORT 2100)
(defparameter MPD-PASS "")

;;; Internal function definitions.

;; Found at:
;; http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(defun cat (&rest strings) "A shortcut for (concatenate 'string foo bar)."
  (apply 'concatenate 'string strings))

;; My MPD "client".
(defun mpc (host port pass command) "Pass commands to MPD."
  (with-open-stream  (socket (socket:socket-connect port host :timeout 1))
   (format socket "password ~a~%" pass)
   (format socket "~a~%" command)))

;; To be replaced.
(defun current-song-info () "Return current song information from MPD."
  (run-shell-command "mpc | head -n 1 | tr -d '\\n'" t))

;;; Theme.

;; Window border colors.
(set-focus-color FOREGROUND-COLOR)
(set-unfocus-color BACKGROUND-COLOR)

;; Input box colors.
(set-fg-color FOREGROUND-COLOR)
(set-bg-color BACKGROUND-COLOR)
(set-border-color BORDER-COLOR)

;; Modeline colors.
(setf *mode-line-foreground-color* FOREGROUND-COLOR)
(setf *mode-line-background-color* BACKGROUND-COLOR)
(setf *mode-line-border-color* BORDER-COLOR)

;; Background.
(run-shell-command (cat "xsetroot -solid " BACKGROUND-COLOR))

;;; Init stuff.

;; Make frames 1-indexed.
;; See: http://lists.gnu.org/archive/html/stumpwm-devel/2006-08/msg00002.html
;; Found at: http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(setf *frame-number-map* "1234567890") ; Still doesn't seem to work.

;; Rename the first group to Browse and create the other groups.
;; Found at: http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(setf (group-name (first (screen-groups (current-screen)))) "Browse")
(run-commands "gnewbg Edit" "gnewbg Term" "gnewbg Comms" "gnewbg Misc")

;; Change the prefix key
;; keycode 115 = F20 in ~/.xmodmap, 115 being the left "windows" key.
(set-prefix-key (kbd "F20"))

;; Set up X cursor and colors.
(run-shell-command (cat "xsetroot -cursor_name left_ptr -fg " BACKGROUND-COLOR ; Cursor body
                                                      " -bg " BORDER-COLOR)) ; Cursor outline

;; Keep the X cursor out of the way.
(run-with-timer 5 5 'banish-pointer)

;; Configure and start the modeline. Colors are handled above.
(setf *mode-line-border-width* 1)
(setf *mode-line-pad-x* 1)
(setf *mode-line-pad-y* 1)
(setf *mode-line-position* :bottom)
(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.
(setf *screen-mode-line-format* (list "(%n %w) (" ; Current group and frames
                                      `(:eval (format-time-string))
                                      ") (" ; Just a spacer
                                      `(:eval (current-song-info)) ; Defined above
                                      ")"))

;; Switch mode-line on only if needed. Found at:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc
(if (not (head-mode-line (current-head)))
     (toggle-mode-line (current-screen) (current-head)))

;; Found this tidbit browsing the source. Defaults to :ignore
(setf *mouse-focus-policy* :click) ; I'm fucking lame.

;;; Keyboard shortcuts.

;; Fluxbox-style Alt-F# virtual desktop (group in StumpWM-speak) switching. Modified from:
;; http://hcl-club.lu/svn/development/lisp/.stumpwmrc
(dotimes (i 13)
  (unless (eq i 0)
    (define-key *top-map* (kbd (format nil "M-F~a" i)) (format nil "gselect ~a" i))))

;; Applications.
(define-key *root-map* (kbd "b") "exec firefox ")
(define-key *root-map* (kbd "e") "exec xemacs ")
(define-key *root-map* (kbd "c") (cat "exec urxvt -fg " FOREGROUND-COLOR
                                                " -bg " BACKGROUND-COLOR
                                                " -pr " FOREGROUND-COLOR
                                                " +sb "))

;; Audio controls, uses un-numlocked keypad.
;; Some keys duplicated, not sure which I prefer yet.
(define-key *top-map* (kbd "KP_Up") "mpc-volume-up")
(define-key *top-map* (kbd "KP_Down") "mpc-volume-down")
(define-key *top-map* (kbd "KP_Add") "mpc-volume-up") ; Redundant
(define-key *top-map* (kbd "KP_Subtract") "mpc-volume-down") ; Redundant
(define-key *top-map* (kbd "KP_Left") "mpc-song-prev")
(define-key *top-map* (kbd "KP_Right") "mpc-song-next")
(define-key *top-map* (kbd "Num_Lock") "mpc-pause") ; The light indicates play/pause ;) 
(define-key *top-map* (kbd "KP_Enter") "mpc-status")
(define-key *top-map* (kbd "KP_Insert") "mpc-status") ; Redundant

(define-stumpwm-command "mpc-status" () "Shows current MPD status and song info. MPC's default output."
 (run-shell-command "mpc" t))

(define-stumpwm-command "mpc-volume-up" () "Increase MPD playback volume."
 (mpc MPD-HOST MPD-PORT MPD-PASS "volume +10"))

(define-stumpwm-command "mpc-volume-down" () "Decrease MPD playback volume."
 (mpc MPD-HOST MPD-PORT MPD-PASS "volume -10"))

(define-stumpwm-command "mpc-song-next" () "Switches MPD playback to next song."
 (mpc MPD-HOST MPD-PORT MPD-PASS "next"))

(define-stumpwm-command "mpc-song-prev" () "Switches MPD playback to previous song."
 (mpc MPD-HOST MPD-PORT MPD-PASS "previous"))

(define-stumpwm-command "mpc-pause" () "Pause/unpause MPD"
 (mpc MPD-HOST MPD-PORT MPD-PASS "pause"))

January 9, 2008 Posted by | Common Lisp, StumpWM | Leave a Comment

.stumpwmrc 0.4

Current .stumpwmrc with entire changelog will now be kept available here.

— cut ——————————————————————————————-


; -*-lisp-*-

;;; 0.4

;; 2008/01/09

;; Axed my custom (current-date). Set *format-time-string-default* and call (format-time-string) for the
;; modeline instead. Besides saving the overhead that an external application implies, it keeps it
;; from disappearing now. (current-song-info) will simply call (mpc-status) once it's rewritten.

;; All (mpc-*) functions other than (mpc-status) now use a direct socket connection to MPD instead of
;; using an external client. It could easily enough be made portable but right now it's CLISP specific.
;; Will write a custom (mpc-status) later. Yes, my StumpWM config is now its own MPD client. ;) 

;; 2008/01/08

;; Note that CLISP seems to have a problem with my modeline. The exec portions stop getting run
;; seemingly at random, and sometimes comes back. Appears to be related to the fact that they
;; both use (run-shell-command). Attempting to use an MPD-affecting command makes it throw an
;; error regarding no child process and fails to have an effect on MPD, but restores the modeline
;; after which subsequent MPD keybindings work as expected. No, disabling current-song-info
;; doesn't fix it. Yes, it was happening before the previous two changes. No, it doesn't happen
;; with SBCL. May attempt to debug in a couple days.

;; Replace the monstrocity that (current-song-info) had become with its previous version.
;; Add keybinding to display mpc's default output instead.

;; Replace call to external 'unclutter' application with (run-with-timer 5 5 'banish-pointer).
;; Although, it goes off every 5 seconds regardless of mouse activity now. But I'm OK with that.
;; Fixing it would likely require a hook to mouse activity to reset the timer. I may look into it later.

(in-package :stumpwm)

;;; Internal variable definitions.

(defparameter FOREGROUND-COLOR "green")
(defparameter BACKGROUND-COLOR "black")
(defparameter BORDER-COLOR FOREGROUND-COLOR)

(setf *format-time-string-default* "%a %b %e %k:%M")

(defparameter MPD-HOST "127.0.0.1")
(defparameter MPD-PORT 2100)
(defparameter MPD-PASS "")

;;; Internal function definitions.

;; Taken from http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(defun cat (&rest strings) "A shortcut for (concatenate 'string foo bar)."
  (apply 'concatenate 'string strings))

;; My MPD "client".
(defun mpc (host port pass command) "Pass commands to MPD."
  (with-open-stream  (socket (socket:socket-connect port host :timeout 1))
   (format socket "password ~a~%" pass)
   (format socket "~a~%" command)))

;; To be replaced.
(defun current-song-info () "Return current song information from MPD."
  (run-shell-command "mpc | head -n 1 | tr -d '\\n'" t))

;;; Theme.

;; Window border colors.
(set-focus-color FOREGROUND-COLOR)
(set-unfocus-color BACKGROUND-COLOR)

;; Input box colors.
(set-bg-color BACKGROUND-COLOR)
(set-fg-color FOREGROUND-COLOR)
(set-border-color BORDER-COLOR)

;; Modeline colors.
(setf *mode-line-background-color* BACKGROUND-COLOR)
(setf *mode-line-border-color* BORDER-COLOR)
(setf *mode-line-foreground-color* FOREGROUND-COLOR)

;; Background.
(run-shell-command (cat "xsetroot -solid " BACKGROUND-COLOR))

;;; Init stuff.

;; Make frames 1-indexed.
;; See: http://lists.gnu.org/archive/html/stumpwm-devel/2006-08/msg00002.html
(setf *frame-number-map* "1234567890")

;; Rename the first group to Browse.
(setf (group-name (first (screen-groups (current-screen)))) "Browse")
;; Create the other groups.
(run-commands "gnewbg Edit" "gnewbg Term" "gnewbg Comms")

;; Change the prefix key
;; keycode 115 = F20 in ~/.xmodmap, 115 being the left "windows" key.
(set-prefix-key (kbd "F20"))

;; Set up X cursor and colors.
(run-shell-command (cat "xsetroot -cursor_name left_ptr -fg " BACKGROUND-COLOR " -bg " BORDER-COLOR)) ; Colors swapped here.

;; Keep the X cursor out of the way.
(run-with-timer 5 5 'banish-pointer)

;; Configure and start the modeline. Colors are handled above.
(setf *mode-line-border-width* 1)
(setf *mode-line-pad-x* 1)
(setf *mode-line-pad-y* 1)
(setf *mode-line-position* :bottom)
(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.
(setf *screen-mode-line-format* (list "(%n %w) (" ; Current group and frames
                                      `(:eval (format-time-string))
                                      ") (" ; Just a spacer
                                      `(:eval (current-song-info)) ; Defined above
                                      ")"))
(run-commands "mode-line")

;; Found this tidbit browsing the source. Defaults to :ignore
(setf *mouse-focus-policy* :click) ; I'm fucking lame.

;;; Keyboard shortcuts.

;; Applications.
(define-key *root-map* (kbd "b") "exec firefox ")
(define-key *root-map* (kbd "e") "exec xemacs ")
(define-key *root-map* (kbd "c") (cat "exec urxvt -fg " FOREGROUND-COLOR
                                                " -bg " BACKGROUND-COLOR
                                                " -pr " FOREGROUND-COLOR
                                                " +sb "))

;; Audio controls, uses its own keymap.
(defvar *audio-map* nil
  "The keymap that audio related key bindings sit on.")

(when (null *audio-map*)
  (setf *audio-map*
        (let ((m (make-sparse-keymap)))
	  (define-key m (kbd "a") "mpc-status")
          (define-key m (kbd "Up") "mpc-volume-up")
          (define-key m (kbd "Down") "mpc-volume-down")
          (define-key m (kbd "Left") "mpc-song-prev")
          (define-key m (kbd "Right") "mpc-song-next")
          (define-key m (kbd "P") "mpc-pause")
          (define-key m (kbd "?") "help-audio")
          m)))

(define-key *root-map* (kbd "a") *audio-map*)

(define-stumpwm-command "mpc-status" () "Shows current MPD status and song info. MPC's default output."
 (run-shell-command "mpc" t))

(define-stumpwm-command "mpc-volume-up" () "Increase MPD playback volume."
 (mpc MPD-HOST MPD-PORT MPD-PASS "volume +10"))

(define-stumpwm-command "mpc-volume-down" () "Decrease MPD playback volume."
 (mpc MPD-HOST MPD-PORT MPD-PASS "volume -10"))

(define-stumpwm-command "mpc-song-next" () "Switches MPD playback to next song."
 (mpc MPD-HOST MPD-PORT MPD-PASS "next"))

(define-stumpwm-command "mpc-song-prev" () "Switches MPD playback to previous song."
 (mpc MPD-HOST MPD-PORT MPD-PASS "previous"))

(define-stumpwm-command "mpc-pause" () "Pause/unpause MPD"
 (mpc MPD-HOST MPD-PORT MPD-PASS "pause"))

(define-stumpwm-command "help-audio" () "Show what keybindings are configured for *audio-map*"
 (display-keybinding '*audio-map*))

January 9, 2008 Posted by | Common Lisp, StumpWM | Leave a Comment

.stumpwmrc 0.3

Moved all the audio controls to their own keymap,
*screen-mode-line-format* now calls functions for its display instead of running the commands right there,
the current song portion of which became big and hairy,
now have click-to-focus,
and general cleanups.

— cut ———————————————————————————————

; -*-lisp-*-;;; .stumpwmrc 0.3

(in-package :stumpwm)

;;; Internal variable definitions.

(defparameter FOREGROUND-COLOR "green")

(defparameter BACKGROUND-COLOR "black")

(defparameter BORDER-COLOR FOREGROUND-COLOR)

;;; Internal function definitions.

;; Taken from http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc

(defun cat (&rest strings) "A shortcut for (concatenate 'string foo bar)."

  (apply 'concatenate 'string strings))

;; This function turns:

;;

;; Wumpscut - Cannibal Anthem

;; [playing] #1506/1750   3:53/4:00 (97%)

;; volume: 70%   repeat: off   random: on

;;

;; into:

;;

;; Wumpscut - Cannibal Anthem (playing (97%)) (vol (70%))

;;

;; It is, however, convoluted. I'm considering whether to call an external script

;; or just open a socket to MPD from here.

(defun current-song-info () "Return current song information from MPD."

  (cat (run-shell-command "mpc | tee .tmpmpc | head -n 1 | tr -d '\\n'" t)

       " ("

       (run-shell-command "cat .tmpmpc | head -n 2 | tail -n 1 | tr -s ' ' | cut -d\\  -f 1,4 | tr -d []'\\n'" t)

       ") (vol ("

       (run-shell-command "cat .tmpmpc | tail -n 1 | cut -d : -f 2 | cut -c 1-4 | tr -d ' ''\\n' && rm .tmpmpc" t)

       "))"))

(defun current-date () "Returns the date formatted as: Mon Jan 7 02:27"

  (run-shell-command "date '+%a %b %e %H:%M' | tr -d '\\n'" t)) ; Formatted as: Mon Jan 7 02:27

;;; Theme.

;; Window border colors.

(set-focus-color FOREGROUND-COLOR)

(set-unfocus-color BACKGROUND-COLOR)

;; Input box colors.

(set-bg-color BACKGROUND-COLOR)

(set-fg-color FOREGROUND-COLOR)

(set-border-color BORDER-COLOR)

;; Modeline colors.

(setf *mode-line-background-color* BACKGROUND-COLOR)

(setf *mode-line-border-color* BORDER-COLOR)

(setf *mode-line-foreground-color* FOREGROUND-COLOR)

;; Background.

(run-shell-command (cat "xsetroot -solid " BACKGROUND-COLOR))

;;; Init stuff.

;; Make frames 1-indexed.

;; See: http://lists.gnu.org/archive/html/stumpwm-devel/2006-08/msg00002.html

(setf *frame-number-map* "1234567890")

;; Rename the first group to Browse.

(setf (group-name (first (screen-groups (current-screen)))) "Browse")

;; Create the other groups.

(run-commands "gnewbg Edit" "gnewbg Term" "gnewbg Comms")

;; Change the prefix key

;; keycode 115 = F20 in ~/.xmodmap, 115 being the left "windows" key.

(set-prefix-key (kbd "F20"))

;; Set up X cursor and colors.

(run-shell-command (cat "xsetroot -cursor_name left_ptr -fg " BACKGROUND-COLOR " -bg " BORDER-COLOR)) ; Colors swapped here.

;; Keep the X cursor out of the way.

(run-shell-command "unclutter -idle 5 -jitter 5 -root")

;; Configure and start the modeline. Colors are handled above.

(setf *mode-line-border-width* 1)

(setf *mode-line-pad-x* 1)

(setf *mode-line-pad-y* 1)

(setf *mode-line-position* :bottom)

(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.

(setf *screen-mode-line-format* (list "(%n %w) (" ; Current group and frames

                                      `(:eval (current-date)) ; Defined above

                                      ") (" ; Just a spacer

                                      `(:eval (current-song-info)) ; Defined above

                                      ")"))

(run-commands "mode-line")

;; Found this tidbit browsing the source. Defaults to :ignore

(setf *mouse-focus-policy* :click) ; I'm fucking lame.

;;; Keyboard shortcuts.

;; Applications.

(define-key *root-map* (kbd "b") "exec firefox ")

(define-key *root-map* (kbd "e") "exec xemacs ")

(define-key *root-map* (kbd "c") (cat "exec urxvt -fg " FOREGROUND-COLOR

                                                " -bg " BACKGROUND-COLOR

                                                " -pr " FOREGROUND-COLOR

                                                " +sb "))

;; Audio controls, uses its own keymap.

(defvar *audio-map* nil

  "The keymap that audio related key bindings sit on.")

(when (null *audio-map*)

  (setf *audio-map*

        (let ((m (make-sparse-keymap)))

          (define-key m (kbd "Up") "mpc-volume-up")

          (define-key m (kbd "Down") "mpc-volume-down")

          (define-key m (kbd "Left") "mpc-song-prev")

          (define-key m (kbd "Right") "mpc-song-next")

          (define-key m (kbd "P") "mpc-toggle")

          (define-key m (kbd "?") "help-audio")

          m)))

(define-key *root-map* (kbd "a") *audio-map*)

(define-stumpwm-command "mpc-volume-up" () "Increase MPD playback volume."

 (run-shell-command "mpc volume +10" t))

(define-stumpwm-command "mpc-volume-down" () "Decrease MPD playback volume."

 (run-shell-command "mpc volume -10"t ))

(define-stumpwm-command "mpc-song-next" () "Switches MPD playback to next song."

 (run-shell-command "mpc next" t))

(define-stumpwm-command "mpc-song-prev" () "Switches MPD playback to previous song."

 (run-shell-command "mpc prev" t))

(define-stumpwm-command "mpc-toggle" () "Pause/unpause MPD"

 (run-shell-command "mpc toggle" t))

(define-stumpwm-command "help-audio" () "Show what keybindings are configured for *audio-map*"

 (display-keybinding '*audio-map*))

January 7, 2008 Posted by | Common Lisp, StumpWM | Leave a Comment

I’m the new bitch.

* Prael got bored, http://stumpwm.antidesktop.net/cgi-bin/wiki?action=history;id=FAQ
<male> Congratulations, you're the new Wiki bitch.
<male> Erm. I mean, Wiki editor.
<Prael> lol

January 4, 2008 Posted by | humor, IRC, StumpWM | Leave a Comment

.stumpwmrc 0.2

I’ve broken out the color values, added currently playing song info to the mode-line, got rid of the trailing newline from the date, and added some keybindings to control music playback. Note: Upon pasting it here indentation information is lost.

— cut ————————————————————-

; -*-lisp-*-

;;; .stumpwmrc 0.2

(in-package :stumpwm)

;;; Variable definitions.

(defparameter FOREGROUND-COLOR “green”)
(defparameter BACKGROUND-COLOR “black”)
(defparameter BORDER-COLOR FOREGROUND-COLOR)

;;; Function definitions (used internally).

;;; Taken from http://en.wikipedia.org/wiki/User:Gwern/.stumpwmrc
(defun cat (&rest strings) “A shortcut for (concatenate ‘string foo bar).”
(apply ‘concatenate ‘string strings))

;;; Colors.

;;; Window border colors.
(set-focus-color FOREGROUND-COLOR)
(set-unfocus-color BACKGROUND-COLOR)

;;; Input box colors.
(set-bg-color BACKGROUND-COLOR)
(set-fg-color FOREGROUND-COLOR)
(set-border-color BORDER-COLOR)

;;; Modeline colors.
(setf *mode-line-background-color* BACKGROUND-COLOR)
(setf *mode-line-border-color* BORDER-COLOR)
(setf *mode-line-foreground-color* FOREGROUND-COLOR)

;;; Background.
(run-shell-command (cat “xsetroot -solid ” BACKGROUND-COLOR))

;;; Init stuff.

;;; Make frames 1-indexed.
;;; See: http://lists.gnu.org/archive/html/stumpwm-devel/2006-08/msg00002.html
(setf *frame-number-map* “1234567890″)

;;; Rename the first group to “Browse”.
(setf (group-name (first (screen-groups (current-screen)))) “Browse”)
;;; Create the other groups.
(run-commands “gnewbg Edit” “gnewbg Term” “gnewbg Chat”)

;;; Change the prefix key
(set-prefix-key (kbd “C-z”))

;;; Set up X cursor and colors.
(run-shell-command (cat “xsetroot -cursor_name left_ptr -fg ” BACKGROUND-COLOR ” -bg ” BORDER-COLOR)) ; Colors swapped here.

;;; Keep the mouse pointer out of the way.
(run-shell-command “unclutter -idle 5 -jitter 5 -root”)

;;; Configure and start the modeline. Colors are handled above.
(setf *mode-line-border-width* 1)
(setf *mode-line-pad-x* 1)
(setf *mode-line-pad-y* 1)
(setf *mode-line-position* :bottom)
(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.
(setf *screen-mode-line-format* (list “[%n] [%w] [" ; Current group and frames
`(:eval (run-shell-command "mpc | head -n 1 | tr -d '\\n'" t))
"] [" ; Just a spacer
`(:eval (run-shell-command "date '+%a %b %e %H:%M' | tr -d '\\n'" t))
"]“))
(run-commands “mode-line”)

;;; Keyboard shortcuts.

;;; Applications.
(define-key *root-map* (kbd “b”) “exec firefox “)
(define-key *root-map* (kbd “e”) “exec xemacs “)
(define-key *root-map* (kbd “c”) (cat “exec xterm -fg ” FOREGROUND-COLOR ” -bg ” BACKGROUND-COLOR ” +cm “))

;;; MPD control.
(define-key *root-map* (kbd “Up”) “exec mpc volume +10″)
(define-key *root-map* (kbd “Down”) “exec mpc volume -10″)
(define-key *root-map* (kbd “Left”) “exec mpc prev”)
(define-key *root-map* (kbd “Right”) “exec mpc next”)
(define-key *root-map* (kbd “P”) “exec mpc pause”)

January 3, 2008 Posted by | StumpWM | Leave a Comment

I’ve finally found a real Window Manager.

I’ve always used Fluxbox, as it’s small, clean, and efficient.

Once in a great while, I’ll try something else. XFCE, WindowMaker, IceWM, E16/E17, other Blackbox forks, etc…, but eventually, I invariably wind up going back to good ‘ol Fluxbox, which is now at 1.0.

None of them ever seemed quite right though. Not even Fluxbox. My first experience with Unix was Slackware 3.6 at Votech, soon after which I installed Slackware 4.0 on my computer at home and had no GUI for 6 months as XWindows did not support my video chipset at the time.

After the first week, I didn’t miss it. I had become a commandline junkie. Everything I wanted to do I could do just fine, and kept everything organized with virtual terminals and screen and suchlike, and it was vastly quicker to move around and actually *get things done*. The only thing missing were images in webpages, and I didn’t care as they’re usually ads anyway and this was long before the whole “Web 2.0″ craze and lynx/links/w3m/etc… were fine.

But a GUI is damn convenient for some things. Text mode browsers just don’t cut it anymore. There’s perfectly usable commandline IM and IRC and mail clients, but their GUI counterparts like Pidgin and XChat and Sylpheed are just more efficient for me (though, I’ve yet to find a GUI music player that isn’t complete trash). So I’ve always wanted a happy medium.

I’ve tried various other window managers that seemed like they could approximate that blissful commandline experience like Ion, Ratpoison, wmii, dwm, XMonad, etc… but they all had their problems. They all seemed too static, too basic, and/or too hard to customize. XMonad, written in Haskell, particularly looks like I’d imagine Perl should look while one is on heavy doses of acid, and I don’t much care for “purely” functional languages anyway.

The last few months off and on, I’ve also been attempting to learn Common Lisp and in the process of such, I came across StumpWM. A heavily customizable tiling keyboard driven window manager written entirely in Common Lisp, appropriately enough written by the same guys as Ratpoison. And I think I’ve finally found a new love. The best part is there’s basically 0 learning curve if you’ve used screen before. The quickest way to get an idea of what I’m talking about is to just watch the video. Archive.org managed to break the flash version so just download the Ogg Theora version, the video’s probably smaller than your current GUI anyway.

http://www.nongnu.org/stumpwm/
http://stumpwm.antidesktop.net/
http://en.wikipedia.org/wiki/Stumpwm

With that, I’ll include my noobish configuration for StumpWM that so far doesn’t do a whole lot more than give it a nifty monochrome look. There are much more interesting examples at the above sites, and I have many ideas that I intend to implement. You’ll notice of course, that this is in fact executable Lisp code. That’s how StumpWM rolls.

— cut —————————————————————–

; -*-lisp-*-

(in-package :stumpwm)

;;; Colors.

;;; Window border colors.
(set-focus-color “green”)
(set-unfocus-color “black”)

;;; Input box colors.
(set-bg-color “black”)
(set-fg-color “green”)
(set-border-color “green”)

;;; Modeline colors.
(setf *mode-line-background-color* “black”)
(setf *mode-line-border-color* “green”)
(setf *mode-line-foreground-color* “green”)

;;; Background.
(run-shell-command “xsetroot -solid black”)

;;; Init stuff.

;;; Make frames 1-indexed.
(setf *frame-number-map* “1234567890″)

;;; Rename the first group to “Browse”.
(setf (group-name (first (screen-groups (current-screen)))) “Browse”)

;;; Create the other groups.
(run-commands “gnewbg Edit” “gnewbg Terms” “gnewbg IRC” “gnewbg IM” “gnewbg Misc”) ; 6 “desktops” total.

;;; Change the prefix key
(set-prefix-key (kbd “C-z”))

;;; Set up X cursor and its colors.
(run-shell-command “xsetroot -cursor_name left_ptr -fg black -bg green”)

;;; Keep the mouse pointer out of the way.
(run-shell-command “unclutter -idle 5 -jitter 5 -root”)

;;; Configure and start the modeline. Colors are handled above.
(setf *mode-line-border-width* 1)
(setf *mode-line-pad-x* 1)
(setf *mode-line-pad-y* 1)
(setf *mode-line-position* :bottom)
(setf *mode-line-timeout* 10) ; Update every 10 seconds if nothing else has triggered it already.
(setf *screen-mode-line-format* (list “%g | %w | “
`(:eval (stumpwm:run-shell-command “date ‘+%a %b %e %H:%M’” t))))
(run-commands “mode-line”)

;;; Keyboard shortcuts.

(define-key *root-map* (kbd “b”) “exec firefox “)
(define-key *root-map* (kbd “B”) “exec dillo “)
(define-key *root-map* (kbd “e”) “exec xemacs “)
(define-key *root-map* (kbd “c”) “exec xterm -fg green -bg black +cm “)

January 3, 2008 Posted by | StumpWM | Leave a Comment

   

Follow

Get every new post delivered to your Inbox.