[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
An event is an object that encapsulates information about an interesting occurrence in the operating system. Events are generated either by user action, direct (e.g. typing on the keyboard or moving the mouse) or indirect (moving another window, thereby generating an expose event on an Emacs frame), or as a result of some other typically asynchronous action happening, such as output from a subprocess being ready or a timer expiring. Events come into the system in an asynchronous fashion (typically through a callback being called) and are converted into a synchronous event queue (first-in, first-out) in a process that we will call collection.
Note that each application has its own event queue. (It is immaterial whether the collection process directly puts the events in the proper application’s queue, or puts them into a single system queue, which is later split up.)
The most basic level of event collection is done by the operating system or window system. Typically, XEmacs does its own event collection as well. Often there are multiple layers of collection in XEmacs, with events from various sources being collected into a queue, which is then combined with other sources to go into another queue (i.e. a second level of collection), with perhaps another level on top of this, etc.
XEmacs has its own types of events (called Emacs events), which provides an abstract layer on top of the system-dependent nature of the most basic events that are received. Part of the complex nature of the XEmacs event collection process involves converting from the operating-system events into the proper Emacs events—there may not be a one-to-one correspondence.
Emacs events are documented in ‘events.h’; I’ll discuss them later.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The command loop is the top-level loop that the editor is always
running. It loops endlessly, calling next-event
to retrieve an
event and dispatch-event
to execute it. dispatch-event
does
the appropriate thing with non-user events (process, timeout,
magic, eval, mouse motion); this involves calling a Lisp handler
function, redrawing a newly-exposed part of a frame, reading
subprocess output, etc. For user events, dispatch-event
looks up the event in relevant keymaps or menubars; when a
full key sequence or menubar selection is reached, the appropriate
function is executed. dispatch-event
may have to keep state
across calls; this is done in the “command-builder” structure
associated with each console (remember, there’s usually only
one console), and the engine that looks up keystrokes and
constructs full key sequences is called the command builder.
This is documented elsewhere.
The guts of the command loop are in command_loop_1()
. This
function doesn’t catch errors, though—that’s the job of
command_loop_2()
, which is a condition-case (i.e. error-trapping)
wrapper around command_loop_1()
. command_loop_1()
never
returns, but may get thrown out of.
When an error occurs, cmd_error()
is called, which usually
invokes the Lisp error handler in command-error
; however, a
default error handler is provided if command-error
is nil
(e.g. during startup). The purpose of the error handler is simply to
display the error message and do associated cleanup; it does not need to
throw anywhere. When the error handler finishes, the condition-case in
command_loop_2()
will finish and command_loop_2()
will
reinvoke command_loop_1()
.
command_loop_2()
is invoked from three places: from
initial_command_loop()
(called from main()
at the end of
internal initialization), from the Lisp function recursive-edit
,
and from call_command_loop()
.
call_command_loop()
is called when a macro is started and when
the minibuffer is entered; normal termination of the macro or minibuffer
causes a throw out of the recursive command loop. (To
execute-kbd-macro
for macros and exit
for minibuffers.
Note also that the low-level minibuffer-entering function,
read-minibuffer-internal
, provides its own error handling and
does not need command_loop_2()
’s error encapsulation; so it tells
call_command_loop()
to invoke command_loop_1()
directly.)
Note that both read-minibuffer-internal and recursive-edit set up a
catch for exit
; this is why abort-recursive-edit
, which
throws to this catch, exits out of either one.
initial_command_loop()
, called from main()
, sets up a
catch for top-level
when invoking command_loop_2()
,
allowing functions to throw all the way to the top level if they really
need to. Before invoking command_loop_2()
,
initial_command_loop()
calls top_level_1()
, which handles
all of the startup stuff (creating the initial frame, handling the
command-line options, loading the user’s ‘.emacs’ file, etc.). The
function that actually does this is in Lisp and is pointed to by the
variable top-level
; normally this function is
normal-top-level
. top_level_1()
is just an error-handling
wrapper similar to command_loop_2()
. Note also that
initial_command_loop()
sets up a catch for top-level
when
invoking top_level_1()
, just like when it invokes
command_loop_2()
.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Here is an approximate diagram of the collection processes at work in XEmacs, under TTY’s (TTY’s are simpler than X so we’ll look at this first):
asynch. asynch. asynch. asynch. [Collectors in kbd events kbd events process process the OS] | | output output | | | | | | | | SIGINT, [signal handlers | | | | SIGQUIT, in XEmacs] V V V V SIGWINCH, file file file file SIGALRM desc. desc. desc. desc. | (TTY) (TTY) (pipe) (pipe) | | | | | fake timeouts | | | | file | | | | | desc. | | | | | (pipe) | | | | | | | | | | | | | | | | | | | V V V V V V ------>-----------<----------------<---------------- | | | [collected using |
Notice the separation between TTY-specific and generic event mechanism. When using the Xt-based event loop, the TTY-specific stuff is replaced but the rest stays the same.
It’s also important to realize that only one different kind of system-specific event loop can be operating at a time, and must be able to receive all kinds of events simultaneously. For the two existing event loops (implemented in ‘event-tty.c’ and ‘event-Xt.c’, respectively), the TTY event loop only handles TTY consoles, while the Xt event loop handles both TTY and X consoles. This situation is different from all of the output handlers, where you simply have one per console type.
Here’s the Xt Event Loop Diagram (notice that below a certain point, it’s the same as the above diagram):
asynch. asynch. asynch. asynch. [Collectors in kbd kbd process process the OS] events events output output | | | | | | | | asynch. asynch. [Collectors in the | | | | X X OS and X Window System] | | | | events events | | | | | | | | | | | | | | | | | | SIGINT, [signal handlers | | | | | | SIGQUIT, in XEmacs] | | | | | | SIGWINCH, | | | | | | SIGALRM | | | | | | | | | | | | | | | | | | | | | timeouts | | | | | | | | | | | | | | | | | | | | | | V | V V V V V V fake | file file file file file file file | desc. desc. desc. desc. desc. desc. desc. | (TTY) (TTY) (pipe) (pipe) (socket) (socket) (pipe) | | | | | | | | | | | | | | | | | | | | | | | | | V V V V V V V V --->----------------------------------------<---------<------ | | | | | |[collected using |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There are two event queues here – the command event queue (#### which should be called “deferred event queue” and is in my glyph ws) and the dispatch event queue. (MS Windows actually has an extra dispatch queue for non-user events and uses the generic one only for user events. This is because user and non-user events in Windows come through the same place – the window procedure – but under X, it’s possible to selectively process events such that we take all the user events before the non-user ones. #### In fact, given the way we now drain the queue, we might need two separate queues, like under Windows. Need to think carefully exactly how this works, and should certainly generalize the two different queues.
The dispatch queue (which used to occur duplicated inside of each event
implementation) is used for events that have been read from the
window-system event queue(s) and not yet process by
next_event_internal()
. It exists for two reasons: (1) because in many
implementations, events often come from the window system by way of
callbacks, and need to push the event to be returned onto a queue; (2)
in order to handle QUIT in a guaranteed correct fashion without
resorting to weird implementation-specific hacks that may or may not
work well, we need to drain the window-system event queues and then look
through to see if there’s an event matching quit-char (usually ^G). the
drained events need to go onto a queue. (There are other, similar cases
where we need to drain the pending events so we can look ahead – for
example, checking for pending expose events under X to avoid excessive
server activity.)
The command event queue is used AFTER an event has been read from
next_event_internal()
, when it needs to be pushed back. This
includes, for example, accept-process-output
, sleep-for
and wait_delaying_user_input()
. Eval events and the like,
generated by enqueue-eval-event
,
enqueue_magic_eval_event()
, etc. are also pushed onto this queue.
Some events generated by callbacks are also pushed onto this queue, ####
although maybe shouldn’t be.
The command queue takes precedence over the dispatch queue.
#### It is worth investigating to see whether both queues are really
needed, and how exactly they should be used. enqueue-eval-event
,
for example, could certainly push onto the dispatch queue, and all
callbacks maybe should. wait_delaying_user_input()
seems to need
both queues, since it can take events from the dispatch queue and push
them onto the command queue; but it perhaps could be rewritten to avoid
this. #### In general we need to review the handling of these two
queues, figure out exactly what ought to be happening, and document it.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
There is one object called an event_stream. This object contains callback functions for doing the window-system-dependent operations that XEmacs requires.
If XEmacs is compiled with support for X11 and the X Toolkit, then this event_stream structure will contain functions that can cope with input on XEmacs windows on multiple displays, as well as input from dumb tty frames.
If it is desired to have XEmacs able to open frames on the displays of multiple heterogeneous machines, X11 and SunView, or X11 and NeXT, for example, then it will be necessary to construct an event_stream structure that can cope with the given types. Currently, the only implemented event_streams are for dumb-ttys, and for X11 plus dumb-ttys, and for mswindows.
To implement this for one window system is relatively simple. To implement this for multiple window systems is trickier and may not be possible in all situations, but it’s been done for X and TTY.
Note that these callbacks are NOT console methods; that’s because the routines are not specific to a particular console type but must be able to simultaneously cope with all allowable console types.
The slots of the event_stream structure:
next_event_cb
A function which fills in an XEmacs_event structure with the next event available. If there is no event available, then this should block.
IMPORTANT: timer events and especially process events *must not* be
returned if there are events of other types available; otherwise you can
end up with an infinite loop in Fdiscard_input()
.
event_pending_cb
A function which says whether there are events to be read. If called
with an argument of 0, then this should say whether calling the
next_event_cb
will block. If called with a non-zero argument,
then this should say whether there are that many user-generated events
pending (that is, keypresses, mouse-clicks, dialog-box selection events,
etc.). (This is used for redisplay optimization, among other things.)
The difference is that the former includes process events and timer
events, but the latter doesn’t.
If this function is not sure whether there are events to be read, it must return 0. Otherwise various undesirable effects will occur, such as redisplay not occurring until the next event occurs.
handle_magic_event_cb
XEmacs calls this with an event structure which contains window-system
dependent information that XEmacs doesn’t need to know about, but which
must happen in order. If the next_event_cb
never returns an
event of type “magic”, this will never be used.
format_magic_event_cb
Called with a magic event; print a representation of the innards of the event to PSTREAM.
compare_magic_event_cb
Called with two magic events; return non-zero if the innards of the two are equal, zero otherwise.
hash_magic_event_cb
Called with a magic event; return a hash of the innards of the event.
add_timeout_cb
Called with an EMACS_TIME, the absolute time at which a wakeup event should be generated; and a void *, which is an arbitrary value that will be returned in the timeout event. The timeouts generated by this function should be one-shots: they fire once and then disappear. This callback should return an int id-number which uniquely identifies this wakeup. If an implementation doesn’t have microseconds or millisecond granularity, it should round up to the closest value it can deal with.
remove_timeout_cb
Called with an int, the id number of a wakeup to discard. This id
number must have been returned by the add_timeout_cb
. If the given
wakeup has already expired, this should do nothing.
select_process_cb
unselect_process_cb
These callbacks tell the underlying implementation to add or remove a file descriptor from the list of fds which are polled for inferior-process input. When input becomes available on the given process connection, an event of type “process” should be generated.
select_console_cb
unselect_console_cb
These callbacks tell the underlying implementation to add or remove a console from the list of consoles which are polled for user-input.
select_device_cb
unselect_device_cb
These callbacks are used by Unixoid event loops (those that use select()
and file descriptors and have a separate input fd per device).
create_io_streams_cb
delete_io_streams_cb
These callbacks are called by process code to create the input and output lstreams which are used for subprocess I/O.
quitp_cb
A handler function called from the QUIT
macro which should check
whether the quit character has been typed. On systems with SIGIO, this
will not be called unless the sigio_happened
flag is true (it is set
from the SIGIO handler).
XEmacs has its own event structures, which are distinct from the event structures used by X or any other window system. It is the job of the event_stream layer to translate to this format.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
detect_input_pending()
and input-pending-p
look for
input by calling event_stream->event_pending_p
and looking in
[V]unread-command-event
and the command_event_queue
(they
do not check for an executing keyboard macro, though).
discard-input
cancels any command events pending (and any
keyboard macros currently executing), and puts the others onto the
command_event_queue
. There is a comment about a “race
condition”, which is not a good sign.
next-command-event
and read-char
are higher-level
interfaces to next-event
. next-command-event
gets the
next command event (i.e. keypress, mouse event, menu selection,
or scrollbar action), calling dispatch-event
on any others.
read-char
calls next-command-event
and uses
event_to_character()
to return the character equivalent. With
the right kind of input method support, it is possible for (read-char)
to return a Kanji character.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Since there are many possible processes/event loop combinations, the event code is responsible for creating an appropriate lstream type. The process implementation does not care about that implementation.
The Create stream pair function is passed two void* values, which identify process-dependent ’handles’. The process implementation uses these handles to communicate with child processes. The function must be prepared to receive handle types of any process implementation. Since only one process implementation exists in a particular XEmacs configuration, preprocessing is a means of compiling in the support for the code which deals with particular handle types.
For example, a unixoid type loop, which relies on file descriptors, may be
asked to create a pair of streams by a unix-style process implementation.
In this case, the handles passed are unix file descriptors, and the code
may deal with these directly. Although, the same code may be used on Win32
system with X-Windows. In this case, Win32 process implementation passes
handles of type HANDLE, and the create_io_streams
function must call
appropriate function to get file descriptors given HANDLEs, so that these
descriptors may be passed to XtAddInput
.
The handle given may have special denying value, in which case the corresponding lstream should not be created.
The return value of the function is a unique stream identifier. It is used by processes implementation, in its platform-independent part. There is the get_process_from_usid function, which returns process object given its USID. The event stream is responsible for converting its internal handle type into USID.
Example is the TTY event stream. When a file descriptor signals input, the event loop must determine process to which the input is destined. Thus, the implementation uses process input stream file descriptor as USID, by simply casting the fd value to USID type.
There are two special USID values. One, USID_ERROR
, indicates
that the stream pair cannot be created. The second,
USID_DONTHASH
, indicates that streams are created, but the event
stream does not wish to be able to find the process by its
USID. Specifically, if an event stream implementation never calls
get_process_from_usid
, this value should always be returned, to
prevent accumulating useless information on USID to process
relationship.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
character_to_event()
, event_to_character()
,
event-to-character
, and character-to-event
convert between
characters and keypress events corresponding to the characters. If the
event was not a keypress, event_to_character()
returns -1 and
event-to-character
returns nil
. These functions convert
between character representation and the split-up event representation
(keysym plus mod keys).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Not yet documented.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Ben’s capsule lecture on focus:
In GNU Emacs select-frame
never changes the window-manager frame
focus. All it does is change the “selected frame”. This is similar to
what happens when we call select-device
or select-console
.
Whenever an event comes in (including a keyboard event), its frame is
selected; therefore, evaluating select-frame
in ‘*scratch*’
won’t cause any effects because the next received event (in the same
frame) will cause a switch back to the frame displaying
‘*scratch*’.
Whenever a focus-change event is received from the window manager, it
generates a switch-frame
event, which causes the Lisp function
handle-switch-frame
to get run. This basically just runs
select-frame
(see below, however).
In GNU Emacs, if you want to have an operation run when a frame is
selected, you supply an event binding for switch-frame
(and then
maybe call handle-switch-frame
, or something ...).
In XEmacs, we do change the window-manager frame focus as a
result of select-frame
, but not until the next time an event is
received, so that a function that momentarily changes the selected frame
won’t cause WM focus flashing. (#### There’s something not quite right
here; this is causing the wrong-cursor-focus problems that you
occasionally see. But the general idea is correct.) This approach is
winning for people who use the explicit-focus model, but is trickier to
implement.
We also don’t make the switch-frame
event visible but instead have
select-frame-hook
, which is a better approach.
There is the problem of surrogate minibuffers, where when we enter the minibuffer, you essentially want to temporarily switch the WM focus to the frame with the minibuffer, and switch it back when you exit the minibuffer.
GNU Emacs solves this with the crockish redirect-frame-focus
,
which says “for keyboard events received from FRAME, act like they’re
coming from FOCUS-FRAME”. I think what this means is that, when a
keyboard event comes in and the event manager is about to select the
event’s frame, if that frame has its focus redirected, the redirected-to
frame is selected instead. That way, if you’re in a minibufferless
frame and enter the minibuffer, then all Lisp functions that run see the
selected frame as the minibuffer’s frame rather than the minibufferless
frame you came from, so that (e.g.) your typing actually appears in the
minibuffer’s frame and things behave sanely.
There’s also some weird logic that switches the redirected frame focus
from one frame to another if Lisp code explicitly calls
select-frame
(but not if handle-switch-frame
is called),
and saves and restores the frame focus in window configurations,
etc. etc. All of this logic is heavily #if 0
’d, with lots of
comments saying “No, this approach doesn’t seem to work, so I’m trying
this ... is it reasonable? Well, I’m not sure ...” that are a red flag
indicating crockishness.
Because of our way of doing things, we can avoid all this crock.
Keyboard events never cause a select-frame (who cares what frame they’re
associated with? They come from a console, only). We change the actual
WM focus to a surrogate minibuffer frame, so we don’t have to do any
internal redirection. In order to get the focus back, I took the
approach in ‘minibuf.el’ of just checking to see if the frame we moved to
is still the selected frame, and move back to the old one if so.
Conceivably we might have to do the weird "tracking" that GNU Emacs does
when select-frame
is called, but I don’t think so. If the
selected frame moved from the minibuffer frame, then we just leave it
there, figuring that someone knows what they’re doing. Because we don’t
have any redirection recorded anywhere, it’s safe to do this, and we
don’t end up with unwanted redirection.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
‘event-Xt.c’ ‘event-msw.c’ ‘event-stream.c’ ‘event-tty.c’ ‘events-mod.h’ ‘gpmevent.c’ ‘gpmevent.h’ ‘events.c’ ‘events.h’ |
These implement the handling of events (user input and other system notifications).
‘events.c’ and ‘events.h’ define the event Lisp object type and primitives for manipulating it.
‘event-stream.c’ implements the basic functions for working with
event queues, dispatching an event by looking it up in relevant keymaps
and such, and handling timeouts; this includes the primitives
next-event
and dispatch-event
, as well as related
primitives such as sit-for
, sleep-for
, and
accept-process-output
. (‘event-stream.c’ is one of the
hairiest and trickiest modules in XEmacs. Beware! You can easily mess
things up here.)
‘event-Xt.c’ and ‘event-tty.c’ implement the low-level
interfaces onto retrieving events from Xt (the X toolkit) and from TTY’s
(using read()
and select()
), respectively. The event
interface enforces a clean separation between the specific code for
interfacing with the operating system and the generic code for working
with events, by defining an API of basic, low-level event methods;
‘event-Xt.c’ and ‘event-tty.c’ are two different
implementations of this API. To add support for a new operating system
(e.g. NeXTstep), one merely needs to provide another implementation of
those API functions.
Note that the choice of whether to use ‘event-Xt.c’ or ‘event-tty.c’ is made at compile time! Or at the very latest, it is made at startup time. ‘event-Xt.c’ handles events for both X and TTY frames; ‘event-tty.c’ is only used when X support is not compiled into XEmacs. The reason for this is that there is only one event loop in XEmacs: thus, it needs to be able to receive events from all different kinds of frames.
‘keymap.c’ ‘keymap.h’ |
‘keymap.c’ and ‘keymap.h’ define the keymap Lisp object
type and associated methods and primitives. (Remember that keymaps are
objects that associate event descriptions with functions to be called to
“execute” those events; dispatch-event
looks up events in the
relevant keymaps.)
‘cmdloop.c’ |
‘cmdloop.c’ contains functions that implement the actual editor command loop—i.e. the event loop that cyclically retrieves and dispatches events. This code is also rather tricky, just like ‘event-stream.c’.
‘macros.c’ ‘macros.h’ |
These two modules contain the basic code for defining keyboard macros. These functions don’t actually do much; most of the code that handles keyboard macros is mixed in with the event-handling code in ‘event-stream.c’.
‘minibuf.c’ |
This contains some miscellaneous code related to the minibuffer (most of the minibuffer code was moved into Lisp by Richard Mlynarik). This includes the primitives for completion (although filename completion is in ‘dired.c’), the lowest-level interface to the minibuffer (if the command loop were cleaned up, this too could be in Lisp), and code for dealing with the echo area (this, too, was mostly moved into Lisp, and the only code remaining is code to call out to Lisp or provide simple bootstrapping implementations early in temacs, before the echo-area Lisp code is loaded).
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Aidan Kehoe on December 27, 2016 using texi2html 1.82.