ported the documentation for the framework's test library -- also extended srcdoc.ss a little bit

svn: r9499
This commit is contained in:
Robby Findler 2008-04-27 02:55:21 +00:00
parent 6ffcad639c
commit 6a53f96e06
4 changed files with 357 additions and 317 deletions

View File

@ -821,84 +821,10 @@
(define test:mouse-click mouse-click) (define test:mouse-click mouse-click)
(define test:new-window new-window) (define test:new-window new-window)
#;
(provide/doc (provide/doc
(proc-doc (proc-doc/names
test:number-pending-actions test:button-push
(-> number?) (-> (or/c (λ (str)
@{Returns the number of pending events (those that haven't completed yet)}))
(provide/contract/docs
(test:number-pending-actions
(-> number?)
()
"Returns the number of pending events (those that haven't completed yet)")
(test:run-interval
(case->
(number? . -> . void?)
(-> number?))
((msec) ())
"See also"
"\\hyperref{Actions and completeness}{Actions and completeness, section~}{}{fw:actions-completeness}."
"The first case in the case-lambda sets"
"the run interval to \\var{msec} milliseconds and the second"
"returns the current setting.")
(test:reraise-error
(-> void?)
()
"See also"
"\\hyperref{Errors}{Errors, section~}{}{fw:test:errors}.")
(test:run-one
((-> void?) . -> . void?)
(f)
"Runs the function \\var{f} as if it was a simulated event. See also"
"\\hyperref{the test section}{section ~}{}{fw:test}.")
(test:current-get-eventspaces
(case->
((-> (listof eventspace?)) . -> . void?)
(-> (-> (listof eventspace?))))
((func) ())
"This parameter that specifies which "
"\\hyperref{eventspaces}{eventspace (see section~}{)}{eventspaceinfo}"
"are considered when finding the frontmost frame."
"The first case"
"sets the parameter to \\var{func}. The procedure \\var{func} will be"
"invoked with no arguments to determine the eventspaces to consider"
"when finding the frontmost frame for simulated user events."
"The second case"
"returns the current value of the parameter. This will be a procedure"
"which, when invoked, returns a list of eventspaces.")
(test:close-top-level-window
((is-a?/c top-level-window<%>) . -> . void?)
(tlw)
"Use this function to simulate clicking on the close box of a frame."
"Closes \\var{tlw} with this expression:"
""
"\\begin{schemedisplay}"
"(when (send tlw can-close?)"
" (send tlw on-close)"
" (send tlw show #f))"
"\\end{schemedisplay}")
(test:top-level-focus-window-has?
(((is-a?/c area<%>) . -> . boolean?) . -> . boolean?)
(test)
"Calls \\var{test} for each child of the top-level-focus-frame"
"and returns \\scheme|#t| if \\var{test} ever does, otherwise"
"returns \\scheme|#f|. If there"
"is no top-level-focus-window, returns \\scheme|#f|.")
;; ((frame-has? p) f) =
;; f is a frame and it has a child (in it or a subpanel) that responds #t to p
(test:button-push
((or/c (λ (str)
(and (string? str) (and (string? str)
(test:top-level-focus-window-has? (test:top-level-focus-window-has?
(λ (c) (λ (c)
@ -914,94 +840,102 @@
(λ (btn) (λ (btn)
(test:top-level-focus-window-has? (test:top-level-focus-window-has?
(λ (c) (eq? c btn)))))) (λ (c) (eq? c btn))))))
. -> .
void?) void?)
(button) (button)
"Simulates pushing \\var{button}. If a string is supplied, the" @{Simulates pushing @scheme[button]. If a string is supplied, the
"primitive searches for a button labelled with that string in the" primitive searches for a button labelled with that string in the
"active frame. Otherwise, it pushes the button argument.") active frame. Otherwise, it pushes the button argument.})
(test:set-radio-box! (proc-doc/names
((or/c string? (is-a?/c radio-box%)) (or/c string? number?) . -> . void?) test:set-radio-box!
(-> (or/c string? (is-a?/c radio-box%)) (or/c string? number?) void?)
(radio-box state) (radio-box state)
"Sets the radio-box to \\var{state}. If \\var{state} is a" @{Sets the radio-box to @scheme[state]. If @scheme[state] is a
"string, this function finds the choice with that label and" string, this function finds the choice with that label and
"if it is a number, it uses the number as an index into the" if it is a number, it uses the number as an index into the
"state. If the number is out of range or if the label isn't" state. If the number is out of range or if the label isn't
"in the radio box, an exception is raised." in the radio box, an exception is raised.
""
"If \\var{radio-box} is a string, this function searches for a"
"\\iscmclass{radio-box} with a label matching that string,"
"otherwise it uses \\var{radio-box} itself.")
(test:set-radio-box-item! If @scheme[radio-box] is a string, this function searches for a
(string? . -> . void?) @scheme[radio-box%] object with a label matching that string,
otherwise it uses @scheme[radio-box] itself.})
(proc-doc/names
test:set-radio-box-item!
(-> string? void?)
(entry) (entry)
"Finds a \\iscmclass{radio-box} that has a label \\var{entry}" @{Finds a @scheme[radio-box%] that has a label @scheme[entry]
"and sets the radio-box to \\var{entry}.") and sets the radio-box to @scheme[entry].})
(test:set-check-box!
((or/c string? (is-a?/c check-box%)) boolean? . -> . void?) (proc-doc/names
test:set-check-box!
(-> (or/c string? (is-a?/c check-box%)) boolean? void?)
(check-box state) (check-box state)
"Clears the \\iscmclass{check-box} item if \\var{state} is \\rawscm{\\#f}, and sets it" @{Clears the @scheme[check-box%] item if @scheme[state] is @scheme[#f], and sets it
"otherwise." otherwise.
""
"If \\var{check-box} is a string,"
"this function searches for a \\iscmclass{check-box} with a label matching that string,"
"otherwise it uses \\var{check-box} itself.")
(test:set-choice! If @scheme[check-box] is a string,
((or/c string? (is-a?/c choice%)) (or/c string? (and/c number? exact? integer? positive?)) . -> . void?) this function searches for a @scheme[check-box%] with a label matching that string,
otherwise it uses @scheme[check-box] itself.})
(proc-doc/names
test:set-choice!
(-> (or/c string? (is-a?/c choice%)) (or/c string? (and/c number? exact? integer? positive?))
void?)
(choice str) (choice str)
"Selects \\var{choice}'s item \\var{str}. If \\var{choice} is a string," @{Selects @scheme[choice]'s item @scheme[str]. If @scheme[choice] is a string,
"this function searches for a \\iscmclass{choice} with a label matching" this function searches for a @scheme[choice%] with a label matching
"that string, otherwise it uses \\var{choice} itself.") that string, otherwise it uses @scheme[choice] itself.})
(test:set-list-box! (proc-doc/names
((or/c string? (is-a?/c list-box%)) (or/c string? (and/c number? exact? integer? positive?)) . -> . void?) test:set-list-box!
(-> (or/c string? (is-a?/c list-box%)) (or/c string? (and/c number? exact? integer? positive?))
void?)
(choice str) (choice str)
"Selects \\var{list-box}'s item \\var{str}. If \\var{list-box} is a string," @{Selects @scheme[list-box]'s item @scheme[str]. If @scheme[list-box] is a string,
"this function searches for a \\iscmclass{list-box} with a label matching" this function searches for a @scheme[list-box%] with a label matching
"that string, otherwise it uses \\var{list-box} itself.") that string, otherwise it uses @scheme[list-box] itself.})
(test:keystroke (proc-doc/names
test:keystroke
(->* ((or/c char? symbol?)) (->* ((or/c char? symbol?))
((listof (symbols 'alt 'control 'meta 'shift 'noalt 'nocontrol 'nometea 'noshift))) ((listof (symbols 'alt 'control 'meta 'shift 'noalt 'nocontrol 'nometea 'noshift)))
void?) void?)
((key) ((key)
((modifier-list null))) ((modifier-list null)))
"This function simulates a user pressing a key. The argument, \\var{key}," @{This function simulates a user pressing a key. The argument, @scheme[key],
"is just like the argument to the" is just like the argument to the
"@link key-event get-key-code" @method[key-event% get-key-code]
"method of the" method of the @scheme[key-event%] class.
"@link key-event"
"class. "
""
"{\\it Note:}"
"To send the ``Enter'' key, use \\verb|#\return|,"
"not \\verb|#\newline|."
""
"The \\rawscm{'shift} or \\rawscm{'noshift} modifier is implicitly set from \\var{key},"
"but is overridden by the argument list. The \\rawscm{'shift} modifier is"
"set for any capitol alpha-numeric letters and any of the following characters:"
"\\begin{schemedisplay}"
"#\\? #\\: #\\~ #\\\\ #\\|"
"#\\< #\\> #\\{ #\\} #\\[ #\\] #\\( #\\)"
"#\\! #\\@ #\\# #\\$ #\\% #\\^ #\\& #\\* #\\_ #\\+"
"\\end{schemedisplay}"
""
"If conflicting modifiers are provided, the ones later in the list are used.")
(test:menu-select @italic{Note:}
To send the ``Enter'' key, use @scheme[#\return],
not @scheme[#\newline].
The @scheme['shift] or @scheme['noshift] modifier is implicitly set from @scheme[key],
but is overridden by the argument list. The @scheme['shift] modifier is
set for any capitol alpha-numeric letters and any of the following characters:
@schemeblock[
#\? #\: #\~ #\\ #\|
#\< #\> #\{ #\} #\[ #\] #\( #\)
#\! #\@ #\# #\$ #\% #\^ #\& #\* #\_ #\+
]
If conflicting modifiers are provided, the ones later in the list are used.})
(proc-doc/names
test:menu-select
(string? string? . -> . void?) (string? string? . -> . void?)
(menu item) (menu item)
"Selects the menu-item named \\var{item} in the menu named \\var{menu}." @{Selects the menu-item named @scheme[item] in the menu named @scheme[menu].
""
"{\\it Note:}"
"The string for the menu item does not include its keyboard equivalent."
"For example, to select ``New'' from the ``File'' menu, "
"use ``New'', not ``New Ctrl+m n''.")
(test:mouse-click @italic{Note:}
The string for the menu item does not include its keyboard equivalent.
For example, to select ``New'' from the ``File'' menu,
use ``New'', not ``New Ctrl+m n''.})
(proc-doc/names
test:mouse-click
(->* (->*
((symbols 'left 'middle 'right) ((symbols 'left 'middle 'right)
(and/c exact? integer?) (and/c exact? integer?)
@ -1010,27 +944,90 @@
void?) void?)
((button x y) ((button x y)
((modifiers null))) ((modifiers null)))
"Simulates a mouse click at the coordinate: $(x,y)$ in the currently" @{Simulates a mouse click at the coordinate (x,y) in the currently
"focused \\iscmintf{window}, assuming that it supports the " focused @scheme[window], assuming that it supports the
"@ilink canvas on-event" @method[canvas<%> on-event] method.
"method." Use @scheme[test:button-push] to click on a button.
"Use"
"@flink test:button-push"
"to click on a button."
""
"On the Macintosh, \\rawscm{'right} corresponds to holding down the command"
"modifier key while clicking and \\rawscm{'middle} cannot be generated."
""
"Under Windows, \\rawscm{'middle} can only be generated if the user has a"
"three button mouse."
""
"The modifiers later in the list \\var{modifiers} take precedence over"
"ones that appear earlier.")
(test:new-window On the Macintosh, @scheme['right] corresponds to holding down the command
((is-a?/c window<%>) . -> . void?) modifier key while clicking and @scheme['middle] cannot be generated.
Under Windows, @scheme['middle] can only be generated if the user has a
three button mouse.
The modifiers later in the list @scheme[modifiers] take precedence over
ones that appear earlier.})
(proc-doc/names
test:run-interval
(case->
(number? . -> . void?)
(-> number?))
((msec) ())
@{See also @secref{test:actions-completeness}.
The first case in the case-lambda sets
the run interval to @scheme[msec] milliseconds and the second
returns the current setting.})
(parameter-doc
test:current-get-eventspaces
(parameter/c (-> (listof eventspace?)))
func
@{This parameter that specifies which
\\hyperref{eventspaces}{eventspace see section~}{}{eventspaceinfo}
are considered when finding the frontmost frame.
The first case
sets the parameter to @scheme[func]. The procedure @scheme[func] will be
invoked with no arguments to determine the eventspaces to consider
when finding the frontmost frame for simulated user events.
The second case
returns the current value of the parameter. This will be a procedure
which, when invoked, returns a list of eventspaces.})
(proc-doc/names
test:new-window
(-> (is-a?/c window<%>) void?)
(window) (window)
"Moves the keyboard focus to a new window within the currently active" @{Moves the keyboard focus to a new window within the currently active
"frame. Unfortunately, neither this function nor any other function in" frame. Unfortunately, neither this function nor any other function in
"the test engine can cause the focus to move from the top-most (active)" the test engine can cause the focus to move from the top-most (active) frame.})
"frame. "))
(proc-doc/names
test:close-top-level-window
(-> (is-a?/c top-level-window<%>) void?)
(tlw)
@{Use this function to simulate clicking on the close box of a frame.
Closes @scheme[tlw] with this expression:
@schemeblock[
(when (send tlw can-close?)
(send tlw on-close)
(send tlw show #f))]})
(proc-doc/names
test:top-level-focus-window-has?
(-> (-> (is-a?/c area<%>) boolean?) boolean?)
(test)
@{Calls @scheme[test] for each child of the top-level-focus-frame
and returns @scheme[#t] if @scheme[test] ever does, otherwise
returns @scheme[#f]. If there
is no top-level-focus-window, returns @scheme[#f].})
(proc-doc
test:number-pending-actions
(-> number?)
@{Returns the number of pending events (those that haven't completed yet)})
(proc-doc
test:reraise-error
(-> void?)
@{See also @secref{test:errors}.})
(proc-doc/names
test:run-one
(-> (-> void?) void?)
(f)
@{Runs the function @scheme[f] as if it was a simulated event.}))

View File

@ -6,7 +6,9 @@
(provide require/doc (provide require/doc
provide/doc provide/doc
proc-doc) parameter-doc
proc-doc
proc-doc/names)
(define-syntax-rule (require/doc spec ...) (define-syntax-rule (require/doc spec ...)
(void (quote-syntax (require/doc spec ...)))) (void (quote-syntax (require/doc spec ...))))
@ -53,26 +55,18 @@
(lambda (stx) (lambda (stx)
(syntax-case stx () (syntax-case stx ()
[(_ id contract desc) [(_ id contract desc)
(with-syntax ([(arg ...) (with-syntax ([((arg ...) result)
(syntax-case #'contract (->d) (syntax-case #'contract (->d -> values)
[(->d (req ...) () result) [(->d (req ...) () (values [name res] ...))
#'(req ...)] #'((req ...) (values res ...))]
[(->d (req ...) () [name res])
#'((req ...) res)]
[(-> result)
#'(() result)]
[else [else
(raise-syntax-error (raise-syntax-error
#f #f
"unsupported procedure contract form (arguments)" "unsupported procedure contract form (no argument names)"
stx
#'contract)])]
[result
(syntax-case #'contract (->d)
[(->d reqs opts (values [name res] ...))
#'(values res ...)]
[(->d reqs opts [name res])
#'res]
[else
(raise-syntax-error
#f
"unsupported procedure contract form (arguments)"
stx stx
#'contract)])]) #'contract)])])
(values (values
@ -80,4 +74,58 @@
#'(defproc (id arg ...) result . desc) #'(defproc (id arg ...) result . desc)
#'(scribble/manual)))]))) #'(scribble/manual)))])))
(define-provide/doc-transformer proc-doc/names
(lambda (stx)
(syntax-case stx ()
[(_ id contract names desc)
(with-syntax ([header
(syntax-case #'(contract names) (->d -> values)
[((-> ctcs ... result) (arg-names ...))
(begin
(unless (= (length (syntax->list #'(ctcs ...)))
(length (syntax->list #'(arg-names ...))))
(raise-syntax-error #f "mismatched argument list and domain contract count" stx))
#'([(id (arg-names ctcs) ...) result]))]
[((->* (mandatory ...) (optional ...) result)
((mandatory-names ...)
((optional-names optional-default) ...)))
(begin
(unless (= (length (syntax->list #'(mandatory-names ...)))
(length (syntax->list #'(mandatory ...))))
(raise-syntax-error #f "mismatched mandatory argument list and domain contract count" stx))
(unless (= (length (syntax->list #'(optional-names ...)))
(length (syntax->list #'(optional ...))))
(raise-syntax-error #f "mismatched mandatory argument list and domain contract count" stx))
#'([(id (mandatory-names mandatory) ... (optional-names optional optional-default) ...)
result]))]
[((case-> (-> doms ... rng) ...)
((args ...) ...))
(begin
(for-each
(λ (doms args)
(unless (= (length (syntax->list doms))
(length (syntax->list args)))
(raise-syntax-error #f "mismatched case argument list and domain contract" stx)))
(syntax->list #'((doms ...) ...))
(syntax->list #'((args ...) ...)))
#'([(id (args doms) ...) rng] ...))]
[else
(raise-syntax-error
#f
"unsupported procedure contract form (no argument names)"
stx
#'contract)])])
(values
#'[id contract]
#'(defproc* header . desc)
#'(scribble/manual)))])))
(define-provide/doc-transformer parameter-doc
(lambda (stx)
(syntax-case stx (parameter/c)
[(_ id (parameter/c contract) arg-id desc)
(values
#'[id (parameter/c contract)]
#'(defparam id arg-id contract . desc)
#'(scribble/manual))])))

View File

@ -1,8 +1,118 @@
#lang scribble/doc #lang scribble/doc
@(require scribble/manual) @(require scribble/manual scribble/extract)
@(require (for-label framework/framework)) @(require (for-label framework/framework))
@(require (for-label scheme/gui)) @(require (for-label scheme/gui))
@title{Test} @title{Test}
@(require framework/framework-docs) @(require framework/framework-docs)
@(defmodule framework/test)
The framework provides several new primitive functions that simulate
user actions, which may be used to test applications. You use these
primitives and combine them just as regular MzScheme functions. For
example,
@schemeblock[
(test:keystroke #\A)
(test:menu-select "File" "Save")
]
sends a keystroke event to the window with the keyboard focus and invokes
the callback function for the ``Save'' menu item from the ``File'' menu.
This has the same effect as if the user typed the key ``A'', pulled
down the ``File'' menu and selected ``Save''.
It is possible to load this portion of the framework without loading
the rest of the framework. Use
@scheme[(require framework/test)].
Currently, the test engine has primitives for pushing
buttons, setting check-boxes and choices, sending keystrokes,
selecting menu items and clicking the mouse. Many functions
that are also useful in application testing, such as
traversing a tree of panels, getting the text from a canvas,
determining if a window is shown, and so on, exist in MrEd.
@section[#:tag "test:actions-completeness"]{Actions and completeness}
The actions associated with a testing primitive may not have finished
when the primitive returns to its caller.
Some actions may yield control before they can complete.
For example, selecting ``Save As...'' from the ``File'' menu
opens a dialog box and will not complete until the ``OK''
or ``Cancel'' button is pushed.
However, all testing functions wait at least a minimum interval
before returning to give the action a chance to finish.
This interval controls the speed at which the test suite runs,
and gives some slack time for events to complete.
The default interval is 100 milliseconds. The interval can be queried
or set with @scheme[test:run-interval].
A primitive action will not return until the run-interval has
expired and the action has finished, raised an error, or yielded.
The number of incomplete actions is given by
@scheme[test:number-pending-actions].
@italic{Note:}
Once a primitive action is started, it is not possible to undo it
or kill its remaining effect.
Thus, it is not possible to write a utility that flushes the
incomplete actions and resets number-pending-actions to zero.
However, actions which do not complete right away often provide a
way to cancel themselves.
For example, many dialog boxes have a ``Cancel'' button which will
terminate the action with no further effect.
But this is accomplished by sending an additional action
(the button push), not by undoing the original action.
@section[#:tag "test:errors"]{Errors}
Errors in the primitive actions (which necessarily run in the
handler thread) are caught and reraised in the calling thread.
However, the primitive actions can only guarantee that the action
has started, and they may return before the action has completed.
As a consequence, an action may raise an error long after the
function that started it has returned.
In this case, the error is saved and reraised at the first opportunity
(the next primitive action).
The test engine keeps a buffer for one error, saving only the
first error. Any subsequent errors are discarded.
Reraising an error empties the buffer, allowing the next error
to be saved.
The function @scheme[test:reraise-error]
reraises any pending errors.
@section{Technical Issues}
@subsection{Active Frame}
The Self Test primitive actions all implicitly apply to the
top-most (active) frame.
@subsection{Thread Issues}
The code started by the primitive actions must run in the handler
thread of the eventspace where the event takes place. As a result,
the test suite that invokes the primitive actions must @italic{not} run
in that handler thread (or else some actions will deadlock). See
\Mrhyperref{the eventspace section}{see section~}{}{eventspaceinfo}
for more info.
@subsection{Window Manager (Unix only)}
In order for the Self Tester to work correctly, the window manager
must set the keyboard focus to follow the active frame.
This is the default behavior in Microsoft Windows and MacOS,
but not in X windows.
In X windows, you must explicitly tell your window manager to set the
keyboard focus to the top-most frame, regardless of the position of the
actual mouse.
@section{Test Functions}
@(def-fw-procs test) @(def-fw-procs test)
@(include-extracted (lib "test.ss" "framework"))

View File

@ -74,121 +74,6 @@ The precise set of exported names is:
@scheme[preferences:restore-defaults]. @scheme[preferences:restore-defaults].
}} }}
@section{GUI Test Suite Utilities}
The framework provides several new primitive functions that simulate
user actions, which may be used to test applications. You use these
primitives and combine them just as regular MzScheme functions. For
example,
@schemeblock[
(test:keystroke #\A)
(test:menu-select "File" "Save")
]
sends a keystroke event to the window with the keyboard focus and invokes
the callback function for the ``Save'' menu item from the ``File'' menu.
This has the same effect as if the user typed the key ``A'', pulled
down the ``File'' menu and selected ``Save''.
It is possible to load this portion of the framework without loading
the rest of the framework. Use
@scheme[(require framework/test)].
Currently, the test engine has primitives for pushing
buttons, setting check-boxes and choices, sending keystrokes,
selecting menu items and clicking the mouse. Many functions
that are also useful in application testing, such as
traversing a tree of panels, getting the text from a canvas,
determining if a window is shown, and so on, exist in MrEd.
@subsection{Actions and completeness}
The actions associated with a testing primitive may not have finished
when the primitive returns to its caller.
Some actions may yield control before they can complete.
For example, selecting ``Save As...'' from the ``File'' menu
opens a dialog box and will not complete until the ``OK''
or ``Cancel'' button is pushed.
However, all testing functions wait at least a minimum interval
before returning to give the action a chance to finish.
This interval controls the speed at which the test suite runs,
and gives some slack time for events to complete.
The default interval is 100 milliseconds. The interval can be queried
or set with \iscmprocedure{test:run-interval}.
A primitive action will not return until the run-interval has
expired and the action has finished, raised an error, or yielded.
The number of incomplete actions is given by
\iscmprocedure{test:number-pending-actions}.
{\it Note:}
Once a primitive action is started, it is not possible to undo it
or kill its remaining effect.
Thus, it is not possible to write a utility that flushes the
incomplete actions and resets number-pending-actions to zero.
However, actions which do not complete right away often provide a
way to cancel themselves.
For example, many dialog boxes have a ``Cancel'' button which will
terminate the action with no further effect.
But this is accomplished by sending an additional action
(the button push), not by undoing the original action.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Errors}
\label{fw:test:errors}
Errors in the primitive actions (which necessarily run in the
handler thread) are caught and reraised in the calling thread.
However, the primitive actions can only guarantee that the action
has started, and they may return before the action has completed.
As a consequence, an action may raise an error long after the
function that started it has returned.
In this case, the error is saved and reraised at the first opportunity
(the next primitive action).
The test engine keeps a buffer for one error, saving only the
first error. Any subsequent errors are discarded.
Reraising an error empties the buffer, allowing the next error
to be saved.
The function
\iscmprocedure{test:reraise-error}
reraises any pending errors.
\subsection{Technical Issues}
{\bf Active Frame}
The Self Test primitive actions all implicitly apply to the
top-most (active) frame.
{\bf Thread Issues}
The code started by the primitive actions must run in the handler
thread of the eventspace where the event takes place. As a result,
the test suite that invokes the primitive actions must {\it not} run
in that handler thread (or else some actions will deadlock). See
\Mrhyperref{the eventspace section}{see section~}{}{eventspaceinfo}
for more info.
{\bf Window Manager (Unix only)}
In order for the Self Tester to work correctly, the window manager
must set the keyboard focus to follow the active frame.
This is the default behavior in Microsoft Windows and MacOS,
but not in X windows.
In X windows, you must explicitly tell your window manager to set the
keyboard focus to the top-most frame, regardless of the position of the
actual mouse. Some window managers may not implement such functionality.
You can obtain such an effect in Fvwm and Fvwm95 by using the option:
\begin{verbatim}
Style "*" ClickToFocus
\end{verbatim}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@section{Thanks} @section{Thanks}