6 Getting Started
dxtr edited this page 2026-10-17 04:57:32 +02:00

Getting Started

Requirements

  • Windows 10 version 1607 or later, on x86-64. Per-monitor DPI awareness, set-thread-dpi-awareness, needs 1607.
  • SBCL. See Platforms and Testing.
  • cl-ffi, which is not in Quicklisp: with stift, cl-win32's stiftfile brings it in; without, clone it where ASDF finds it.

user32.dll and kernel32.dll are part of Windows; there is nothing else to install.

Loading

cl-win32 is not in Quicklisp. Clone it where ASDF can find it, for example under %USERPROFILE%\common-lisp\, and load it:

(asdf:load-system :cl-win32)

The package is cl-win32. Its symbols are short and general, such as create-window and key-state, so use a local nickname rather than :use:

(defpackage #:my-app
  (:use #:cl)
  (:local-nicknames (#:win32 #:cl-win32)))

All the examples in this wiki use the nickname win32.

A first window

(in-package #:my-app)

(win32:define-window-procedure my-procedure (window message wparam lparam)
  (declare (ignore lparam))
  (cond ((= message win32:+wm-size+)
         (format t "~&Resized: ~:[restored~;minimised~]~%"
                 (= wparam win32:+size-minimized+))
         0)
        ((= message win32:+wm-close+)
         (win32:destroy-window window)
         0)
        ((= message win32:+wm-destroy+)
         (win32:post-quit-message 0)
         0)
        (t nil)))

(win32:set-thread-dpi-awareness :per-monitor-aware-v2)
(win32:register-window-class "my-app" 'my-procedure)
(unwind-protect
     (let ((window (win32:create-window "my-app" :title "My app")))
       (multiple-value-bind (width height)
           (win32:window-size-for-client-size 640 400 :dpi (win32:window-dpi window))
         (win32:set-window-bounds window :width width :height height))
       (win32:show-window window)
       (win32:run-message-loop))
  (win32:unregister-window-class "my-app"))

Close the window and run-message-loop returns 0.

What each step does:

  1. define-window-procedure defines the window procedure: the function Windows calls with every message for a window, such as +wm-size+ or +wm-close+. It returns the message's result, an integer, or nil to leave the message to Windows' default handling.
  2. set-thread-dpi-awareness has Windows report sizes in pixels for the windows this thread creates, rather than scaling them up blurred. See DPI.
  3. register-window-class registers a window class: a name tied to the window procedure.
  4. create-window makes a window of the class. It is hidden until show-window. Its size is the whole window, title bar and borders included, so window-size-for-client-size works out the size that gives the inside, the client area, 640 by 400 pixels.
  5. run-message-loop hands the thread's messages to their window procedures until post-quit-message, and returns its code. Closing the window sends +wm-close+; the procedure destroys the window, which sends +wm-destroy+, which ends the loop.

Next

  • Concepts explains classes, procedures, messages and threads.
  • Recipes shows windows on a thread of their own, and more.