Writing a Win32 Application in SBCL Common Lisp
The sucess of dynamic languages such as Python and Ruby in the web has created a renewed interest in Lisp. After all, Lisp is the grandfather of all dynamic languages.
For Windows users, however, it has been traditionally difficult to find good implementations of Common Lisp. The main implementations of Lisp have some difficulties that make them unsuitable to use on Windows. For example, CMU-CL runs only on UN*X systems. Allegro Lisp (from Franz Inc.) runs well on Windows, but is not free. CLisp runs on Windows using cygwin, but it is not well integrated on Windows, and is an interpreted Common Lisp.
Recently, though, SBCL, a variant of CMU-CL has been ported to Windows. There are several things that make SBCL attractive as a development compiler.
- It is really fast
- It is stable, with a large community of dedicated Lisp users
- Generates executables, so you can distribute your program without saying in which language it was created — good if you want to sell your programs.
To find out if SBCL is really suitable for general development on Windows, I decided to create a graphical Win32 application. If that works, then it should be safe to use SBCL to write Windows applications. In this article, I will explain how to use SBCL to write such a Win32 application.
Installing SBCL
The first step is to install the SBCL sytem. You can get it from http://www.sbcl.org/ and use the installer. The installer will even put the executable directory of sbcl in your path.
To create windows applications, however, you will need a windows calling library, created by Alastair Bridgewater. With this little piece of glue code, you can directly call the Win32 API functions. You can also create a callback function in Common Lisp, and have it passed into the Windows functions. This is how a window procedure looks like:
(defun window-procedure (hwnd imsg wparam lparam)
(cond
((= imsg wm_paint) (on-window-paint hwnd lparam) 0) ((= imsg wm_lbuttondown) (messagebox hwnd "test" "gedit" 0) 0) ((= imsg wm_lbuttondown) (on-mouse-down hwnd lparam) 0) ((= imsg wm_destroy) (postquitmessage 0) 0) (t (defwindowproc hwnd imsg wparam lparam))))
The Drawing Application
The application I created is a simple drawing program. It just draws a line whenever we click, extending from the previous mouse location to the current mouse location. Here is the portion of code that captures the mouse location:
(defvar *point1* (make-my-point :x 10 :y 10)) (defvar *point2* (make-my-point :x 100 :y 100))
(defun on-mouse-down (hwnd lparam) ; save current point (setf (my-point-x *point1*) (my-point-x *point2*)) (setf (my-point-y *point1*) (my-point-y *point2*)) ; set second point (set-point-from-windows-msg lparam *point2*) (invalidaterect hwnd nil t)))Notice that we receive the mouse coodinates in the lparam argument, which is in encoded format (as usual in the Win32 API). We need to define the function set-point-from-windows-msg to get the real position of the new point. This function can be defined as follows:
(defun set-point-from-windows-msg (lparam point) "lparam is a 32 bit value encoding two 16 bit words. we create a point from lparam using these two words." (let ((s 16)) (setf (my-point-x point) (mod lparam #x10000)) (setf (my-point-y point) (ash lparam (- s)))))Here we are using two Common Lisp arithmetic functions:
- mod returns the module of the division of the first number by the second number. In this case, it returns the low bytes in a 32 bit word.
- ash shifts the integer value by the number of positions in the second argument. In this case, it shifts lparam 16 positions to the right, and returns the high bytes in a 32 bit word.
(defun paint-line (hdc hwnd rect rect-addr) (moveto hdc (my-point-x *point1*) (my-point-y *point1*)) (lineto hdc (my-point-x *point2*) (my-point-y *point2*))) (defun on-window-paint (hwnd lparam) (with-alien ((ps (struct paintstruct)) (rect (struct rect))) (let ((hdc (beginpaint hwnd (addr ps)))) (paint-line hdc hwnd rect (addr rect)) (endpaint hwnd (addr ps)))))
Running the Application
You can download the application from here, including the win32 glue files. To run the application, you just need to call
sbcl geditor.lisp
The program is simple, but it shows that SBCL can handle most features required to write windows programs. The SBCL port for Windows is, however, still experimental (notice the warning printed when sbcl is run). I expect, however, that it will evolve into a great option for Lisp programmers that need to access win32 features.
Similar Posts:
About the Author
Carlos Oliveira holds a PhD in Systems Engineering and Optimization from University of Florida. He works as a software engineer, with more than 10 years of experience in developing high performance, commercial and scientific applications in C++, Java, and Objective-C. His most Recent Book is Practical C++ Financial Programming.
Hi,
Cide still working well on SBCL 1.0.53 on windows 7 64.
Thanks for the code.
Keith
By Keith on Dec 1, 2011
I tried to access the link to Alastair Bridgewater’s code but the web page is not available. Is this glue code still available somewhere?
By Darren on Nov 1, 2014
Hi Darren, his page is gone, it seems, but the code is part of my repository: https://github.com/coliveira/cl-geditor. Check the file callback-hacking.lisp.
By coliveira on Nov 21, 2014
Hi Carlos, Thanks for this informative article. Can you tell me what IDE/editor did you use to write SBCL on Windows?
By Akshay C on Jan 2, 2022
Hi, I use VIM, which is the editor I use for most programming. It has basic support for Lisp code.
By coliveira on May 1, 2022