Window Procedures and Classes
Defining a window procedure
(win32:define-window-procedure my-procedure (window message wparam lparam)
"Count clicks, and close on Escape."
(cond ((= message win32:+wm-lbuttondown+)
(incf *clicks*)
0)
((and (= message win32:+wm-keydown+)
(eq (win32:virtual-key-name wparam) :escape))
(win32:destroy-window window)
0)
(t nil)))
define-window-procedure defines two things under the name:
- a Lisp function of the four arguments, with the body you wrote; and
- a foreign callback, which Windows calls and which calls the function.
The four arguments are integers: the window's handle, the message, and the
message's wparam and lparam. The body may start with a documentation
string and declarations.
Because the callback calls the function by name, redefining the procedure, for example from the REPL, takes effect at the next message, for windows that already exist.
What a procedure returns
| The body returns | Windows gets |
|---|---|
| an integer | that integer, the message's result |
nil |
the result of default-window-procedure, Windows' default handling |
| anything else | the default handling, and a type-error is signalled afterwards |
Return nil for every message you do not handle: the default handling is
what makes a window behave like a window. Some messages want a particular
result; each message's documentation in Windows says which. A few common
ones:
| Message | Return |
|---|---|
+wm-close+ |
0 if you handled it; nil destroys the window |
+wm-erasebkgnd+ |
1 if you draw the whole window yourself, so Windows does not paint over it |
+wm-xbuttondown+, +wm-xbuttonup+ |
1 |
+wm-paint+ |
0 after validate-window, if something else draws the window |
Errors and non-local exits
The body runs inside Windows' code. It must not leave by a non-local exit:
no throw, return-from or go to outside the body, and no invoking a
restart established outside it. An error is the exception: the
procedure catches it, gives the message the default handling, and the
error is signalled again from the cl-win32 call during which Windows called
the procedure. See Errors.
Registering a class
(win32:register-window-class "my-app" 'my-procedure) ; => an atom, an integer
(win32:register-window-class "my-tool" 'tool-procedure
:styles '(:double-clicks) :cursor nil)
register-window-class takes the class's name, a string, and the name of a
window procedure, a symbol. Its keys:
:styles: class styles, of the typeclass-style.:double-clickshas the class's windows get+wm-lbuttondblclk+and its kin;:own-dcgives each window a device context of its own;:horizontal-redrawand:vertical-redrawrepaint the whole window when its width or height changes;:no-closedisables Close in the window menu.:cursor::arrow, the default, shows the standard arrow over the class's windows.nilleaves the cursor to the window procedure, which sets it when it gets+wm-setcursor+.
A class name is registered once per program. Registering it again signals
a win32-error (error 1410, "Class already exists"). Unregister it with
unregister-window-class once all its windows are destroyed.
Window classes belong to the program, not to a thread: windows of one class may be created on several threads.