ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version

About event-handling in Common Graphics

Event-handling is a critical part of any visual application, where users communicate with the application through dialogs and other windows. Events are handled differently for controls than for general windows. Events on controls are discussed in the IDE User Guide, chapter 8 (Chapter 8 of the IDE User Guide). Here we introduce the more general type of event-handling for "regular" windows (non-controls), and point to a set of functions that can be looked up for more detailed information. A simple application may need to supply event-handling code only for controls, while a more complex application will probably need to supply code for non-control windows as well.

Event-Handling for Non-Control Windows

The operating system sends messages to individual Common Graphics windows (either in the IDE or in a standalone application) when events such as mouse clicks and focus movement occur. To respond to these events, an application can supply code that will be called whenever these messages are received.

The type of code to be supplied depends on whether the message is being received by a control or by a regular window. A message to a control is usually handled by an event-handler function that is written especially for a particular control instance. These event-handlers for controls (which are discussed in IDE User Guide, chapter 8) appear on the Events tab of the inspector when a control is being inspected, and skeleton code for an event handler can be generated interactively by clicking on the extended editor button to the right of the event-handler in the inspector.

For non-control windows, on the other hand, messages are handled by methods that typically apply to a whole window class rather than to an individual window instance. The IDE does not have an interface for adding these methods interactively (like the inspector's list of event-handlers for a control), and so you must know what generic function to modify in order to handle a particular message, and write a method from scratch. Normally an application should first define its own subclass of basic-pane (or one of its subclasses), and then define an event-handling method for that subclass. The method will then be called by Common Graphics whenever a window of that subclass receives the corresponding message from the operating system. Here is a list of many of the event-handling generic functions for non-control windows:

For mouse movement:

For mouse clicks:

For non-client-area (window border) mouse movement and clicks:

For mouse wheel movement

For keypresses:

For menus:

Miscellaneous:

Application-Callable:

The generic functions in the Application-Callable section above may also be called programmatically by an application, in addition to being called by Common Graphics when an interactive event occurs. For example, set-focus may be called by an application to move the keyboard focus to a window, and is also called by Common Graphics when the focus is otherwise moved to a window, such as by clicking in it. An application should keep in mind that when its method for one of these generic functions is called, that it may be due to an interactive gesture by the user or due to a programmatic call.

Any primary methods added to these generic functions should call (call-next-method) unless you are sure that you want to override the default Common Graphics behavior for the event.

Here's an example that handles the virtual-key-down event, since its arguments are a little tricky. This code will create a window that will change its size when the user types control-L, control-semicolon, control-shift-L, or control-shift-semicolon. The buttons argument is some subset of the values of the constants control-key, shift-key, and alt-key logior'ed together (each one is a bit flag). The data argument is an integer for the key that was pressed, expressed either as the char-int of the character that is printed on the key or as the value of one of the "vk-..." constants that are the value of the constant key-names.

(in-package :cg-user)

(defclass my-window (frame-window)())

(defmethod virtual-key-down ((window my-window) buttons data)
   (case buttons
     (#.control-key
      (case data
        (#.vk-semicolon
         (incf (width window) 50))
        (#.(char-int #\L)
         (decf (width window) 50))
        (t (call-next-method))))
     (#.(logior control-key shift-key)
      (case data
        (#.vk-semicolon
         (incf (height window) 50))
        (#.(char-int #\L)
         (decf (height window) 50))
        (t (call-next-method))))
     (t (call-next-method))))

(make-window 'herbert :device 'my-window 
             :parent (screen *system*))

Note that if the example window above were created on (main-development-window *system*) instead of on the screen, then the virtual-key-down method would not get called for the defined keystrokes, because the keystrokes would be overridden by IDE menubar shortcuts. In general, a menubar shortcut will override a virtual-key-down method, and a custom virtual-key-down method will override a comtab binding (since comtab events are implemented as a virtual-key-down method on the general comtab-mixin class).

Handling Low-Level WinAPI Messages

The Common Graphics event-handling generic functions listed above provide a high-level interface to many of the messages sent by the operating system. But since an application may still need to handle low-level Windows events, generic functions also exist for each Windows API message that is handled at all by Common Graphics, and an application may add methods to these functions to handle raw Windows messages.

The name of each generic function is the same as the Windows API constant for the message being received. The functions are in the windows package, and their parameters are

(window wparam lparam)

The arguments are what would be received by a standard Windows "window procedure", except without a parameter for the message name itself, since we have a generic function for each message. We do not document these WinAPI symbols, and provide these generic functions only for programmers who are familiar with the Windows API and can consult Microsoft's documentation on their use. Any primary methods added to these generic functions should call (call-next-method) so that the default method will call the DefaultWindowProc, unless you are sure that you really want to override the operating system's own handling of the event. Here is an example method that would be called whenever an application is activated:

(in-package :cg-user)

(defmethod win:WM_ACTIVATEAPP 
           ((window my-top-level-window-class) wparam lparam)
   (declare (ignore lparam))
   (call-next-method)
   (when (eq wparam win:TRUE)
      (beep)
      (lisp-message "The user has returned to my application!")))

Event-Handling for Controls

The generic functions listed above for non-control windows are not called for controls, as is sometimes expected. There are a few reasons for this:

For example, the on-change property of a control holds a function that is called whenever the value of the control has changed. Such properties are called event-handlers, and are accessible in the inspector on the Events tab. When an event-handler is selected in the inspector, pressing F1 (to invoke the Help | Help On Selected Symbol command) will display help on that event-handler (rather than on its current value, as would happen in the Internals tab).

(To be precise, there are also exported generic functions for a few of the events received by controls. These are click-event, double-click-event, set-focus-event, and kill-focus-event. The default method for each of these generic functions will call the corresponding event-handler function of the individual widget, but an application could override the default method to define behavior for a whole control subclass. Usually it is preferable to stick with individual event-handlers.)


Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page was not revised from the 10.0 page.
Created 2019.8.20.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version