From 2a29eca2a9aaf0c405cc6c5088e90c60b69a5e5f Mon Sep 17 00:00:00 2001 From: ram <ram> Date: Mon, 26 Jul 1993 15:53:55 +0000 Subject: [PATCH] Initial revision --- docs/interface/internals.doc | 380 +++++++++++++++++++++++++++++++++++ docs/interface/toolkit.doc | 350 ++++++++++++++++++++++++++++++++ 2 files changed, 730 insertions(+) create mode 100644 docs/interface/internals.doc create mode 100644 docs/interface/toolkit.doc diff --git a/docs/interface/internals.doc b/docs/interface/internals.doc new file mode 100644 index 000000000..016511718 --- /dev/null +++ b/docs/interface/internals.doc @@ -0,0 +1,380 @@ + -*- Mode: Text,Fill -*- + + + General Design Notes on the Motif Toolkit Interface + --------------------------------------------------- + + + + +Data Transport -- +----------------- + +Packet format - + + Header: + 32 bits ... serial number | + 16 bits ... sequence position | This header + 16 bits ... sequence length | takes 12 bytes + 32 bits ... packet length (including header) | + Data: + (packet_length - 12) bytes of information + + - Packets have a fixed maximum size (4k). + - Packets a grouped together to form random length messages. The +sequence length refers to how many packets comprise the message, and each +packet is tagged with its position in that sequence. + - All packets in the same message have the same serial number. + - Messages are built up as their constituent packets arrive. It should +be possible to interleave the packets of different messages and still have +the individual messages be constructued properly. + - It is tacitly assumed that packets arrive in their proper sequence order. + - A packet with a sequence position/length field denoting [0 of 0] is a +cancellation packet. The message having that serial number should be +discarded. + +Data format - + + - Each data entry in a message is represented as: + 8 bits ... type tag + 24 bits ... immediate data + rest ... other data (if necessary) + + + + +Greeting Protocol -- +------------------- + + When a Lisp process first establishes a connection to the server, it +sends a 16 bit quantity which represents "1" to it. The server using this +to decide whether to byte swap words when sending them to Lisp. The +general policy is that all data is presented to the Lisp process in the +order that Lisp uses. + + Following the byte swapping information, the Lisp process sends an +initial message which contains: + + - A string giving the target X display name + - A string for the application name + - A string for the application class + + + +Request Protocol -- +------------------- + + Request format: + 16 bits ... request opcode + 8 bits ... request flags (0=nothing, 1=require confirm) + 8 bits ... argument count (unused) + + At the moment, the request flags field is used only to indicate whether +the Lisp client desires a confirmation message when the request is finished +processing. If the request returns any values, this counts as the +confirmation. Otherwise, an empty confirmation message will be sent. + + Server reply format: + 32 bits ... response tag + rest ... return data (if any) + + The response tag can have the following values: + - CONFIRM_REPLY ... confirmation (for synchronization) + - VALUES_REPLY ... return values from a request + - CALLBACK_REPLY ... a widget callback has been invoked + - EVENT_REPLY ... an X event handler has been invoked + - ERROR_REPLY ... an error has occurred + - WARNING_REPLY ... a non-fatal problem has occurred + - PROTOCOL_REPLY ... a protocol callback has been invoked + + + +Object Representations -- +------------------------- + +Data format in message - + + Accelerators 32 bit integer ID + Atom 32 bit Atom ID + Boolean 24 bit immediate data + Color 24 bit immediate data (Red value) followed by + 2 16 bit words for Green and Blue + Colormap 32 bit Colormap XID + Compound Strings 32 bit address + Cursor 32 bit Cursor XID + Enumeration 24 bit immediate integer + Font 32 bit Font XID + Font List 32 bit integer ID + Function 24 bit immediate token + Int 32 bit integer + List 24 bit immediate data (length) followed by each element + recorded in order + Pixmap 32 bit Pixmap XID + Short 24 bit immediate integer + Strings 24 bit immediate data (length of string including \0) + followed by string data padded to end on a word boundary + ... or ... + Strings 24 bit immediate token (for common strings) + Translations 32 bit integer ID + Widgets 32 bit integer ID + Window 32 bit Window XID + + + - For objects such as translations, widgets, accelerators, font lists, +and compound strings, the 32 bit ID is just the address of the object in +the C server process. They are represented in Lisp by structures which +encapsulate their ID's and provide them with Lisp data types (other than +simply INTEGER). + +Information in widget structure - + + - integer ID for identifying the widget to the C server + - widget class keyword (eg. :FORM, :PUSH-BUTTON-GADGET, :UNKNOWN) + - parent widget + - list of (knowen) children + - USER-DATA slot for programmer use + - list of active callback lists | These are for internal use + - list of active protocol lists | in cleaning up Lisp state + - list of active event handlers | on widget destruction + + + +Callback handlers -- +-------------------- + + - A callback handler is defined as: + + (defun handler (widget call-data &rest client-data) ....) + + - The WIDGET argument is the widget for which the callback is being invoked. + - The CLIENT-DATA &rest argument allows the programmer to pass an +arbitrary number of Lisp objects to the callback procedure. [Note: this +deviates from CLM and Motif in C]. + - The CALL-DATA argument provides the information passed by Motif +regarding the reason for the callback and any other relevant information. + - The XEvent which generated the event may be accessed by: + + (with-callback-event (event call-data) + ....) + + - Action procedures are used in translation tables as: + + <Key> q: Lisp(SOME-PACKAGE:MY-FUNCTION)\n + + - Action procedures may access their event information by: + + (with-action-event (event call-data) + ....) + + - Where callback data is passed in structures, XEvents are represented +as aliens. This is because XEvents are rather large. This saves the +consing of large structures for each event processed. + + - Actions to be taken after the callback handler terminates the server's +callback loop can be registered by: + + (with-callback-deferred-actions <forms>) + + + +Structure of the Server -- +---------------------------------- + + When the server process is started, it establishes standard sockets for +clients to connect to it and waits for incoming connections. When a client +connects to the server, the server will fork a new process (unless +"-nofork" was specified on the command line) to deal with incoming requests +from the client. The result of this is that each logical application has +its own dedicated request server. This prevents event handling in one +application from blocking event dispatching in another. + + Each request server is essentially an event loop. It waits for an event +to occur, and dispatchs that event to the appropriate handlers. If the +event represents input available on the client connection, it reads the +message off the stream and executes the corresponding request. If the +event is an X event or a Motif callback, relevant information about that +event is packed into a message and sent to the Lisp client. After sending +the event notification, the server will enter a callback event loop to +allow processing of requests from the client's callback procedure. +However, during the callback event loop, only input events from the client +will be processed; all other events will be deferred until the callback is +terminated. + + The server supports a standard means for reading and writing data +objects into messages for communication with the Lisp client. For every +available type of data which may be transported there are reader and writer +functions. For instance, WIDGET is a valid type for argument data. Two +functions are defined in the server: message_read_widget() and +message_write_widget(). To allow for a more generalized interface to +argument passing, the server defines the functions toolkit_write_value() +and toolkit_read_value(). These functions are passed data and a type +identifier; it is their job to look up the correct reader/writer function. +Clearly, if the type of an argument is known at compile time then it is +best to use the specific reader/writer functions. However, if such type +information is not known at compile time, as is the case with arbitrary +resource lists, the higher level toolkit_xxx_value() functions are the only +available options. + + + +Structure of the Client -- +-------------------------- + + + + + +Adding New Requests to the System -- +------------------------------------ + + In order to add a new function to the toolkit interface, this new +function must be declared in both C and Lisp. + + Lisp provides a convenient macro interface for writing the necessary RPC +stub. The form of this definition is: + + (def-toolkit-request <C name> <Lisp name> <:confirm|:no-confirm> + "Documentation string" + (<arguments>) + (<return-values>) + <optional forms>) + +Entries in the argument list should be of the form (<name> <type>). The +return value list is simply a list of types of the return value(s). Any +forms supplied at the end will be executed in a context where the arguments +are bound to the given names and the return value is bound to RESULT (if +there was only one) or FIRST, SECOND, ..., FOURTH (for up to 4 return +values). At the moment, the interface does not support any more than 4 +return values. You must also specify a value for the confirmation option +(:CONFIRM or :NO-CONFIRM). If you expect return values, you must specify +:CONFIRM in order to receive them. Otherwise, you may specify :NO-CONFIRM. +Use of :NO-CONFIRM allows for increased efficiency since the client will +issue a request but not wait for any response. All function prototypes +should be placed in the prototypes.lisp file. A few examples of request +prototypes: + + (def-toolkit-request "XtSetSensitive" set-sensitive :no-confirm + "Sets the event sensitivity of the given widget." + ;; + ;; Takes two arguments: widget and sensitivep + ((widget widget) (sensitivep (member t nil))) + ;; + ;; No return values expected + ()) + + (def-toolkit-request "XtIsManaged" is-managed :confirm + "Returns a value indicating whether the specified widget is managed." + ;; + ;; Takes one argument: widget + ((widget widget)) + ;; + ;; Expects one return value (which is a boolean) + ((member t nil))) + + (def-toolkit-request "XmSelectionBoxGetChild" selection-box-get-child + :confirm + "Accesses a child component of a SelectionBox widget." + ;; + ;; Takes two arguments: w and child + ((w widget) (child keyword)) + ;; + ;; Expects a return value which is a widget + (widget) + ;; + ;; Now we execute some code to maintain the state of the world. + ;; Given that this widget may be one we don't know about, we must + ;; register it as the child of one we do know about. + (widget-add-child w result) + (setf (widget-type result) :unknown)) + + + After adding a request prototype in Lisp, you must add the actual code +to process the request to the C server code. The general form of the +request function should be: + + int R<name>(message_t message) + { + int arg; + ... + toolkit_read_value(message,&arg,XtRInt); + ... + } + +Where <name> is the C name given in the request prototype above. You must +also add an entry for this function in the functions.h file. An example of +a standard request function is: + + int RXtCreateWidget(message_t message) + { + String name; + WidgetClass class; + Widget w,parent; + ResourceList resources; + + toolkit_read_value(message,&name,XtRString); + toolkit_read_value(message,&class,XtRWidgetClass); + toolkit_read_value(message,&parent,XtRWidget); + + resources.class = class; + resources.parent = parent; + toolkit_read_value(message,&resources,ExtRResourceList); + + w = XtCreateWidget(name,class,parent, + resources.args,resources.length); + reply_with_widget(message,w); + } + +Certain standard functions for returning arguments are provided in the file +requests.c; reply_with_widget() is an example of these. + + + +Summary of differences with CLM -- +---------------------------------- + + - X objects (eg. windows, fonts, pixmaps) are represented as CLX objects +rather than the home-brewed representations of CLM. As a consequence, this +requires that CLX be present in the core. If this were to cause +unacceptable core bloat, a skeletal CLX could be built which only supported +the required functionality. + + - Stricter naming conventions are used, in particular for enumerated +types. A value named XmFOO_BAR in C will be called :foo-bar in Lisp, +consistently. Abbreviations such as :form (for :attach-form) are not +allowed since they are often ambiguous. Where CLM abbreviates callback +names (eg. XmNactivateCallback becomes :activate), we do not (eg. +:activate-callback). + + - Some differently named functions which can be resolved without undo +hassle. + + - Passing of information to callbacks and event handlers. In CLM, +callback handlers are defined as: + + (defun handler (widget client-data &rest call-data) .... ) + +The client-data argument is some arbitrary data which was stashed with the +callback when it was registered by the application. The call-data +represents the call-data information provided by Motif to the callback +handler. Each data item of the callback information is passed as a +separate argument. In our world, callback handlers are defined as: + + (defun handler (widget call-data &rest client-data) .... ) + +The call-data is packaged into a structure and passed as a single argument +and the user is allowed to register any number of items to be passed to the +callback as client-data. Being able to pass several items of client-data +is more convenient for the programmer and the packaging of the call-data +information is more appealing than splitting it apart into separate +arguments. Also, CLM only transports a limited subset of the available +callback information. We transport all information. Event handlers differ +in the same way. The client-data is the &rest arg and the event info is +packaged as a single object. Accessing the generating event in a callback +handler is done in the following manner: + + (defun handler (widget call-data &rest client-data) + (with-callback-event (event call-data) + ;; Access slots of event such as: + ;; (event-window event) or + ;; (button-event-x event) + )) diff --git a/docs/interface/toolkit.doc b/docs/interface/toolkit.doc new file mode 100644 index 000000000..a72bae669 --- /dev/null +++ b/docs/interface/toolkit.doc @@ -0,0 +1,350 @@ +-*- Mode: Text, Fill -*- + + + +Naming conventions -- +--------------------- + + In general, names in the Lisp Motif interface are derived directly from +the C original. The following rules apply: + + 1) Drop "Xt" and "Xm" (also "XmN","XmC", etc.) prefixes + 2) Separate words by dashes ("-") rather than capitalization + 3) Resource names and enumeration values are given as keywords + 4) Replace underscores ("_") with dashes ("-") + + Examples: + + - XtCreateWidget --> create-widget + - XmNlabelString --> :label-string + - XmVERTICAL --> :vertical + + Some exceptions: + + - Compound string functions (XmString...) are prefixed by +"compound-string-" rather than "string-" in Lisp. + + Functions or resources, with the exception of the compound-string-xxx +functions, which require compound string arguments, may be given Lisp +SIMPLE-STRINGs instead. + + The arguments to functions are typically the same as the C Motif +equivalents. Some exceptions are: + + - Widget creation functions have a &rest arg for resource values. + - Functions which take a string table/length pair in C only take a +list of strings in Lisp. + - Registering functions such as ADD-CALLBACK use a &rest arg for +registering an arbitrary number of client-data items. + + + +Starting things up -- +--------------------- + + The Motif toolkit interface is divided into two parts. First, there is +a server process written in C which provides an RPC interface to Motif +functions. The other half is a Lisp package which connects to the server +and makes requests on the user's behalf. The Motif interface is exported +from the TOOLKIT (or XT) package. + + + Variables controlling connections: + + *DEFAULT-SERVER-HOST* - a string naming the machine where the Motif +server is to be found. The default is NIL, which causes a connection to +be made using a Unix domain socket on the local machine. Any other name +must be a valid machine name, and the client will connect using Internet +domain sockets. + + *DEFAULT-DISPLAY* - determines the display on which to open windows. +The default value of NIL instructs the system to consult the DISPLAY +environment variable. Any other value must be a string naming a valid X +display. + + *DEFAULT-TIMEOUT-INTERVAL* - an integer specifying how many seconds the +Lisp process will wait for input before assuming that the connection to the +server has timed out. + + + Handling Connections: + + OPEN-MOTIF-CONNECTION (hostname xdisplay-name app-name app-class) -- +Opens a connection to a server on the named host and opens a display +connection to the named X display. The app-name and app-class are for +defining the application name and class for use in resource specifications. +An optional process-id argument can be passed if a local server process has +already been created. This returns a MOTIF-CONNECTION object. + + CLOSE-MOTIF-CONNECTION (connection) -- This closes a toolkit +connection which was created by OPEN-MOTIF-CONNECTION. + + *MOTIF-CONNECTION* -- Bound in contexts such as callback handlers to +the currently active toolkit connection. + + *X-DISPLAY* -- Bound in contexts such as callback handlers to the +currently active CLX display. + + WITH-MOTIF-CONNECTION ((connection) &body forms) -- This macro +establishes the necessary context for invoking toolkit functions outside of +callback/event handlers. + + WITH-CLX-REQUESTS (&body forms) -- Macro that ensures that all CLX +requests made within its body will be flushed to the X server before +proceeding so that Motif functions may use the results. + + RUN-MOTIF-APPLICATION (init-function) -- This is the standard CLM +entry point for creating a Motif application. The init-function argument +will be called to create and realize the interface. It returns the created +MOTIF-CONNECTION object. Available keyword arguments are: + :init-args -- list of arguments to pass to init-function + :application-class -- application class (default "Lisp") + :application-name -- application name (default "lisp") + :server-host -- name of Motif server to connect to + :display -- name of X display to connect to + + QUIT-APPLICATION () -- This is the standard function for closing down a +Motif application. You can call it within your callbacks to terminate the +application. + + + +The Server -- +------------- + + The C server is run by the "motifd" program. This will create both Inet +and Unix sockets for the Lisp client to connect to. By default, the Inet +and Unix sockets will be specific to the user. + + When a Lisp client connects to the server, it forks a copy of itself. +Thus each Lisp application has an exclusive connection to a single C server +process. To terminate the server, just ^C it. + + Switches to change behavior: + + -global ... Sockets created for use by everyone rather than + being user-specific. + -local ... No Inet socket is created and the Unix socket is + process-specific + -noinet ... Instructs the server not to create an Inet socket. + -nounix ... Instructs the server not to create a Unix socket. + -nofork ... Will keep the server from forking when connections are + made. This is useful when debugging the server or when + you want the server to die when the application terminates. + -trace ... Will spit out lots of stuff about what the server is + doing. This is only for debugging purposes. + + Typically, users do not need to be concerned with server switches since, +by default, servers are created automatically by your Lisp process. +However, if you wish to share servers, or use servers across the network, +you will need to run the server manually. + + + +Widget creation -- +------------------ + + CREATE-APPLICATION-SHELL (&rest resources) -- Creates the +applicationShell widget for a new Motif application. + + CREATE-WIDGET + CREATE-MANAGED-WIDGET (name class parent &rest resources) -- These +create new widgets. CREATE-WIDGET does not automatically manage the +created widget, while CREATE-MANAGED-WIDGET does. + + CREATE-<widget_class> (parent name &rest resources) -- Convenience +function which creates a new widget of class <widget_class>. For instance, +CREATE-FORM will create a new XmForm widget. + + *CONVENIENCE-AUTO-MANAGE* -- Controls whether convenience functions +automatically manage the widgets they create. The default is NIL. + + + +Callbacks -- +------------ + + Callbacks are registered with the ADD-CALLBACK function. Unlike Motif +in C, an arbitrary number of client-data items can be registered with the +callback. Callback functions should be defined as: + + (defun callback-handler (widget call-data &rest client-data) ... ) + + The passed widget is that in which the callback has occurred, and the +call-data is a structure which provides more detailed information on the +callback. Client-data is some number of arguments which have been +registered with the callback handler. The slots of the call-data structure +can be derived from the C structure name using the standard name conversion +rules. For example, the call-data structure for button presses has the +following slot (aside from the standard ones): click-count. + + To access the X event which generated the callback, use the following: + + (defun handler (widget call-data &rest client-data) + (with-callback-event (event call-data) + ;; Use event structure here + )) + + Since callback procedures are processed synchronously, the Motif server +will remain blocked to event handling until the callback finishes. This +can be potentially troublesome, but there are two ways of dealing with this +problem. The first alternative is the function UPDATE-DISPLAY. Invoking +this function during your callback function will force the server to +process any pending redraw events before continuing. The other (slightly +more general) method is to register deferred actions with the callback +handling mechanism. Deferred actions will be invoked after the server is +released to process other events and the callback is officially terminated. +Deferred actions are not invoked if the current application was destroyed +as a result of the callback, since any requests to the server would refer +to an application context which was no longer valid. The syntax for their +usage is: + + (with-callback-deferred-actions <forms>) + + You may register only one set of deferred actions within the body of any +particular callback procedure, as well as within event handlers and action +procedures. Registering a second (or more) set of deferred actions will +overwrite all previous ones. + + When using deferred action procedures, care must be taken to avoid +referencing invalid data. Some information available within callbacks is +only valid within the body of that callback and is discarded after the +callback terminates. For instance, events can only be retrieved from the +call-data structure within the callback procedure. Thus the code + + (with-callback-deferred-actions + (with-callback-event (event call-data) + (event-type event))) + +is incorrect since the event will be fetched after the callback is +terminated, at which point the event information will be unavailable. +However, the code + + (with-callback-event (event call-data) + (with-callback-deferred-actions + (event-type event))) + +is perfectly legitimate. The event will be fetched during the callback and +will be closed over in the deferred action procedure. + + + +Action procedures -- +-------------------- + + Action procedures can be registerd in translation tables as in the +following example: + + <Key> q: Lisp(SOME-PACKAGE:MY-FUNCTION)\n + +The generating X event can be accessed within the action handler using: + + (with-action-event (event call-data) + ... use event here ... + ) + + + +Event handlers -- +----------------- + + X events are also represented as structured objects with slot names +which are directly translated from the C equivalent. The accessor +functions are named by <event name>-<slot name>. Some examples: + + (event-window event) ... This applies to all events + (event-type event) ... So does this + + (button-event-x event) | Some button event + (button-event-button event) | accessors + + At the moment, XClientMessage and XKeyMap events are not supported (they +will be in the not too distant future). + + + +Provided conveniences -- +------------------------ + + Since Motif requires the use of font lists for building non-trivial +compound strings, there are some Lisp functions to ease the pain of building +them: + BUILD-SIMPLE-FONT-LIST (name font-spec) -- Returns a font list of +with the given name associated with the given font. For example, + (build-simple-font-list "MyFont" "8x13") + + BUILD-FONT-LIST (flist-spec) -- This allows for the construction of +font lists with more than one font. An example: + (build-font-list `(("EntryFont" ,entry-font-name) + ("HeaderFont" ,header-font-name) + ("ItalicFont" ,italic-font-name))) + + There are certain callbacks which are of general use, and standard ones +are provided for the programmer's convenience. For all callbacks except +QUIT-APPLICATION-CALLBACK, you register some number of widgets with +ADD-CALLBACK. These will be the widgets acted upon by the callback: + + QUIT-APPLICATION-CALLBACK () -- Callback to terminate the current +application. + + DESTROY-CALLBACK -- Destroys all the widgets passed to it. + MANAGE-CALLABCK -- Manages all the widgets passed to it. + UNMANAGE-CALLBACK -- Unmanages all the widgets passed to it. + POPUP-CALLBACK -- Calls popup on all widgets passed to it. + POPDOWN-CALLBACK -- Calls popdown on all widgets passed to it. + + + +Some random notes -- +-------------------- + + - When using functions such as REMOVE-CALLBACK, the client-data passed +must be EQUAL to the client-data passed to ADD-CALLBACK. + - When using REMOVE-CALLBACK, etc., the function may be supplied as +either 'FUNCTION or #'FUNCTION. However, they are considered different so +use the same one when adding and removing callbacks. + - You cannot directly access the XmNitems resources for List widgets and +relatives. Instead, use (SET-ITEMS <widget> ....) and (GET-ITEMS +<widget>). + + + +Things that are missing -- +-------------------------- + + 1) Real documentation + 2) Support for XClientMessage and XKeyMap events + 3) Callback return values (eg. XmTextCallback's) + 4) Ability to send strings longer than 4096 bytes. + + All these things should start appearing in the next few weeks. + + + +A brief example -- +------------------ + +(defun my-callback (widget call-data quit) + (format t "Got callback on ~A~%" widget) + (format t "Callback reason was ~A~%" (any-callback-reason call-data)) + (format t "Quit button is ~A~%" quit)) + +(defun test-init () + (let* ((shell (create-application-shell)) + (rc (create-row-column shell "rowColumn")) + (quit (create-push-button-gadget rc "quitButton" + :label-string "Quit")) + (button (create-push-button-gadget rc "button" + :highlight-on-enter t + :shadow-thickness 0 + :label-string "This is a button"))) + + (add-callback quit :activate-callback #'quit-application-callback) + (add-callback button :activate-callback 'my-callback quit) + + (manage-child rc) + (manage-children quit button) + (realize-widget shell))) + +(defun test () + (run-motif-application 'test-init)) -- GitLab