(let (#

In a mood of procrastination I had a few thoughts on how to create simple GUIs with Common Lisp. For the leveleditor in Uxul World I use LTK - it is installable via QuickLisp meanwhile, and it seems to be the best and most portable possibility so far. The main disadvantage is that it runs the TCL shell in a separate process. Then there is CLIM of course, and though there seem to be a few quite interesting libraries around CLIM, the only one I have ever used is McCLIM (with Climplayer). McClim seems to have several backends, and a nice API for creating new ones, but in the past I could not manage to run it under MS Windows which was why I focused on LTK - and then never concentrated on CLIM.
But actually, I do not really think that GUIs are that important. I think webinterfaces are meanwhile sufficient for most purposes, they are simple to code, and they are portable (and I am not the only one who thinks this apparently). A problem with local webinterfaces is that I do not know any webbrowser that supports connecting to, say, unix domain sockets. Therefore, there are two possibilities of securing the interface from other users on the same computer: Using IPTables and a local shared secret - and I do not like both possibilities. Maybe someday, some browsers will be able to connect to unix domain sockets, projects like uzbl seem to be predestined for that. What is already possible is to let them open FIFOs. So I was doing a little experiment with it, using SBCL's posix-binding: For every link I create a FIFO and a Thread that tries to write on it. Opening a FIFO blocks until both sides of the FIFO are opened by default, thus, as long as the created thread is blocked, you know that the FIFO was not opened by the browser, as soon as it is not blocked anymore, you know that it was opened and can generate the content. So the basic handling mechanism looks like

(require :sb-posix)

(defun make-handle (f)
  (let
      ((fname (sb-posix:mktemp "/tmp/clgui.XXXXXX"))
       (fkeep t))
    (sb-thread:make-thread
     (lambda ()
       (loop while fkeep do
        (sb-posix:mkfifo fname #o700)
        (with-open-file (str fname :direction :output :if-exists :overwrite)
          (setf fkeep (funcall f str))
          (finish-output str))
        (delete-file fname))))
    fname))


It takes a function that itself takes a stream (the one sent to the browser) as an argument and returns a generalized boolean - if it returns true, then the fifo keeps being listened to, if nil, its deleted and not listened anymore. Then I define a few variables and two handlers that increment and decrement a variable:

(defparameter *ctr* -1)
(defparameter *incf* "")
(defparameter *decf* "")

(defun do-inc (str)
  (incf *ctr*)
  (format str "
<html><body><a href=~s>&lt;&lt;</a>~d<a href=~s>&gt;&gt;</a></body></html>
"
      *decf* *ctr* *incf*) T)

(defun do-dec (str)
  (decf *ctr*)
  (format str "
<html><body><a href=~s>&lt;&lt;</a>~d<a href=~s>&gt;&gt;</a></body></html>
"
      *decf* *ctr* *incf*) T)


Finally, I create the handles and set the appropriate links:

(setf *incf* (format nil "file:///~a" (make-handle #'do-inc)))
(setf *decf* (format nil "file:///~a" (make-handle #'do-dec)))
(format t "Now open ~a in your browser." *incf*)


Opening the link in the browser should show you two links and a number that can be incremented and decremented by these links. Be careful: Reading from a FIFO when no process writes to it may freeze Firefox.

There are a lot of drawbacks in this approach: A lot of files are created. And the amount of data sent back to the application is very limited. Formdata cannot be passed - this could be solved using JavaScript and additional protocol handlers for Firefox - which is very complicated.

After all, it was just a little experiment. In theory, user interfaces that only contain links but no text fields and checkboxes could be implemented with it. In practice, I would not do it, but anyway it would be great to make browsers be able to connect to unix domain sockets I think.