The example client program creates and displays a simple pop-up menu consisting of a column of strings--a title string followed by selectable menu item strings. The implementation uses one window to represent the entire menu, plus a set of subwindows, one for each menu item. Here is the definition of a structure which represents such a menu.
(defstruct (menu) "A simple menu of text strings." (title "Choose an item:") item-alist ;((item-window item-string)) window gcontext width title-width item-width item-height (geometry-changed-p t)) ;nil if unchanged since displayed
The window
slot will contain the window object that
represents the menu. The item-
alist
represents the relationship between the menu items and their associated
subwindows. Each entry in item-alist
is a list whose first
element is a (sub)window object and whose second element is the
corresponding item string. A
window object is an instance of a CLX-defined data type which
represents X windows. A window
object actually carries two pieces of information: an X window
ID integer and a display
object. A display
is another CLX-defined data type that represents a connection to a
specific X display server. The gcontext
slot contains an
instance of a CLX data type known as a graphics context.
A graphics context is a set of display attribute values, such as
foreground color, fill style, line style, text font, and so forth. Each
X graphics request (and hence each CLX graphics function call) must
supply a graphics context to use in displaying the request. The
menu's gcontext
will thus hold all of the attribute
values used during menu display.
The first thing to do is make an instance of a menu
object:
(defun create-menu (parent-window text-color background-color text-font) (make-menu ;; Create menu graphics context :gcontext (CREATE-GCONTEXT :drawable parent-window :foreground text-color :background background-color :font text-font) ;; Create menu window :window (CREATE-WINDOW :parent parent-window :class :input-output :x 0 ;temporary value :y 0 ;temporary value :width 16 ;temporary value :height 16 ;temporary value :border-width 2 :border text-color :background background-color :save-under :on :override-redirect :on ;override window mgr when positioning :event-mask (MAKE-EVENT-MASK :leave-window :exposure))))
create-window is one of the most important CLX functions, since it creates and returns a window object. Several of its options are shown here. The default window class is :input-output, but X provides for :input-only windows, too. Every window must have a parent window, except for a system-defined root window, which represents an entire display screen. The :event-mask keyword value, a CLX event-mask data type, says that an input event will be received for the menu window when the window is exposed and also when the pointer cursor leaves the window. The window border is a pattern-filled or (as in this case) a solid-colored boundary which is maintained automatically by the X server; a client cannot draw in a window's border, since all graphics requests are relative to the origin (upper-left corner) of the window's interior and are clipped by the server to this inside region. Turning on the :save-under option is a hint to the X server that, when this window is made visible, it may be more efficient to save the pixels it obscures, rather than require several client programs to refresh their windows when the pop-up menu disappears. This is a way to work around X's client-managed refresh policy when only a small amount of screen space is needed temporarily.
Why is :override-redirect turned on for the menu window? This is actually a little unusual, because it prevents any window manager client from redirecting the position of the menu when it is popped up. Remember that the window manager represents the user's policy for controlling the positions of his windows, so this kind of redirection is ordinarily correct. However, in this case, as a favor to the user, the menu avoids redirection in order to pop up the menu at a very specific location; that is, under the pointer cursor.
What about the item subwindows? The menu-set-item-list
function in the following example creates them whenever the menu's
item list is changed. The upper-left x and y coordinates and the width
and height are not important yet, because they are computed just before
the menu is displayed. This function also calls create-window
, demonstrating the equal treatment of parent and children windows in
the X window hierarchy.
(defun menu-set-item-list (menu &rest item-strings) ;; Assume the new items will change the menu's width and height (setf (menu-geometry-changed-p menu) t) ;; Destroy any existing item windows (dolist (item (menu-item-alist menu)) (DESTROY-WINDOW (first item))) ;; Add (item-window item-string) elements to item-alist (setf (menu-item-alist menu) (let (alist) (dolist (item item-strings (nreverse alist)) (push (list (CREATE-WINDOW :parent (menu-window menu) :x 0 ;temporary value :y 0 ;temporary value :width 16 ;temporary value :height 16 ;temporary value :background (GCONTEXT-BACKGROUND (menu-gcontext menu)) :event-mask (MAKE-EVENT-MASK :enter-window :leave-window :button-press :button-release)) item) alist)))))