OK, let's face it: you've seen the tedium of XLib, one has to use widgets in
order to get any programming done in XWindows. 'But this is assembly langauge',
the masochist might point out. 'Aren't widgets a little Visual-Basicy?'
Not in the slightest. A widget is simply a C++ class exported for use --much
like the windows API functions, only a little more object oriented...maybe a
good comparison would be MFC or VCL. Xt, or 'X toolkit Intrinsics', is the
interface that widget sets [such as Athena, Qt or GTK] use to interface with
XLib. The Xt include files are in /usr/X11R6/include/X11, its libraries are in
/usr/X11R6/lib, and its exported functions are all prefixed with "Xt".
For the following examples I will be using the Atehna widget set, which is
supplied with XFree86. The include files for Athena are in /usr/X11R6/include/Xaw
and the libraries are in /usr/X11R6/lib.
A barebones Xt/Athena app in C would run as follows:
//====================================================================-xthell.c
#include
#include
#include
void Quit(w, client_data, call_data) //CallBack function
Widget w;
XtPointer client_data, call_data;
{
exit(0);
}
main(argc,argv)
int argc;
char **argv;
{
XtAppContext app_context;
Widget ShellWidge, ButtnWidge;
ShellWidge = XtVaAppInitialize( &app_context, "toplevel", NULL, 0, &argc, \
argv, NULL, NULL);
ButtnWidge = XtVaCreateManagedWidget("hellbutton", commandWidgetClass, \
ShellWidge, NULL);
XtAddCallback(ButtnWidge, XtNcallback, Quit, 0);
XtRealizeWidget(ShellWidge);
XtAppMainLoop(app_context);
}
// compile with cc -o xthell xthello.c -lXmu -lXaw -lXt -lX11 -L/usr/X11R6/lib
//=========================================================================-EOF
Pretty ugly, eh? This boils down to the following steps:
- Create the top-level 'Canvas' widget [the window]
ShellWidge = XtVaAppInitialize( &app_context, "toplevel", ..... )
- Create the button widget
ButtnWidge = XtVaCreateManagedWidget("hellbutton", ..... )
- Register a callback for the button
XtAddCallback(ButtnWidge, XtNcallback, Quit, 0);
- Show the top-level widget
XtRealizeWidget(ShellWidge);
- Transfer control to the Xt message loop
XtAppMainLoop(app_context);
The most interesting thing about Xt programming is in fact the callbacks.
Instead of writing a message processing loop, you register a callback function
for each widget and then pass control to Xt, which processes the messages for
you and dispatches each message to the appropriate callback function. The call-
back receives a pointer to the widget that sent the message [the same argument
as passed to XtAddCallback], a client_data pointer [the last argument passed to
XtAddCallback, used to pass data from the main routine to the callback], and a
call_data pointer, which contains information from the message [such as cursor
or scrollbar position].
The calls themselves are pretty straightforward:
XtVaAppInitialize initializes [sic] the X app and takes as its arguments a
pointer to an XtAppContext structure, the class name of the application,
application-specific command line options {args 3 and 4], argc, argv, a default
resource-settings file, and a NULL to terminate the arguments list [the
XtVaAppInitialize function actually takes a number of different parameters]; it
returns a handle to the 'canvas' or 'top-level' widget, on which all other
widgets will be painted.
XtVaCreateManagedWidget is used to create any of the Xt widgets [Athena, GTK,
etc], and takes as its parameters the instance name, the widget class, the
parent widget, and a NULL to terminate the arguments list; it returns a pointer
to the created widget.
XtAddCallback is used to register a callback function with a specific widget;
it takes as its parameters a pointer to the Widget, the callback type, the
function being registered, and a pointer to client_data which will be passed to
the callback.
XtRealizeWidget is simply used like ShowWindow in Windows; it takes a single
parameter which is the widget to 'show'; it displays that widget and its
children.
XtAppMainLoop takes the current application context [which was filled with the
XtVaAppInitialize call] and turns control over to the Xt message processing
loop. Note that the program does not have to return; in this example, the exit
call is placed in the callback function.
Here is the same application written for NASM:
;===================================================================-xthell.asm
BITS 32
GLOBAL main
GLOBAL bail
EXTERN XtVaAppInitialize
EXTERN XtVaCreateManagedWidget
EXTERN XtAddCallback
EXTERN XtRealizeWidget
EXTERN XtAppMainLoop
EXTERN commandWidgetClass
EXTERN exit
SECTION .data
AppContext DD 0
ShellWidge DD 0
ButtnWidge DD 0
ARGC times 128 DB 0
ClassName DB "toplevel",0
ButtnName DB "hellbutton",0
XtNcallback DB "callback",0 ;XtNcallback
SECTION .text
bail:
pop eax ; Xt_Pointer call_data
pop ebx ; Xt_Pointer client_data
pop ecx ; Xt_Pointer widget
push dword 0
call exit
;-------------------------- main
main:
mov eax, esp
push dword 0 ;Number of Args
push dword 0 ;Args
push dword 0 ;Fallback Resources
push dword 0 ;argv
push dword ARGC ;&argc
push dword 0 ;Number of Options
push dword 0 ;Options Array
push dword ClassName ;Class Name (String)
push dword AppContext ;Application Context (Ptr)
call XtVaAppInitialize
add esp, 36
mov [ShellWidge], eax
push dword 0
push eax ;Button parent (ShellWidge)
push dword [commandWidgetClass] ;Button widget type
push dword ButtnName ;Button class name
call XtVaCreateManagedWidget
add esp, 16
mov [ButtnWidge], eax
push dword 0 ; client_data
push dword bail ;CallBack function
push dword XtNcallback ; callback type
push eax ;CallBack widget (ButtonWidge)
call XtAddCallback
add esp, 16
push dword [ShellWidge] ;Widget Handle
call XtRealizeWidget
add esp, 4
push dword [AppContext]
call XtAppMainLoop
add esp, 4
ret
;==========================================================================-EOF
This can be compiled with the following commands:
nasm -f elf xthell.asm
gcc -o xthell xthell.o -lXaw -lXt -lX11 -L/usr/X11R6/lib
Most of the operation is the same as the C file; naturally you must push dword
0's instead of NULLs...and do not forget to push the arguments in reverse
order and to clean up the stack afterwards; this is C after all and not stdcall
is used in Windows.
You will have to study up on Athena to learn what the names of the various
widgets are ... I found it helpful to use
grep extern /usr/X11R6/include/Xaw/*
for a general overview. Note that the class names are strings in assembly; also
each of the various 'handles' [widgets, contexts, etc] is simply defined with a
DD 0 -- your generic 32-bit variable. The Callback type turned out to be a
string defined in the Xt header files; I simply recreated it above.
Another interesting gemis the need to call 'exit' rather than simply using a
'ret' as you would in console mode; the latter causes segmentation faults, most
likely due to the XtAppMainLoop call. In addition you must provide a pointer
to ARGC whether you check the command line or not; hence the 'ARGC: DB 128'.
In case you didn't notice, the Xt asm example is huge and clunky, with a lot of
not-so-obvious variable definitions. Having included a lengthy introduction to
NASM macros in this issue, I took the opportunity to create an xt.mac file
which will take some of the burden off of experimenting with small Xt apps. The
InitXt and RegisterCallback macros probably are not ready for prime-time just
yet, but they will do for testing purposes.
;=======================================================================-xt.mac
%macro CLASS 2
%1: DB %2,0
%endmacro
%macro WDGTPTR 1
%1: DD 0
%endmacro
%macro CONTEXT 1
%1: DD 0
%endmacro
%macro CHARSTR 2
%1: DB %2,0
%endmacro
%define WIDGET EXTERN
%define XLibAPI EXTERN
%define XtAPI EXTERN
%define PUBLIC GLOBAL
%define NULL dword 0
%define TERM_VARARGS dword 0
%macro InitXt 2
SECTION .data
CONTEXT AppContext
CLASS XtShell, "XtShell"
SECTION .text
EXTERN XtVaAppInitialize
push dword 0 ;Number of Args
push dword 0 ;Args
push dword 0 ;Fallback Resources
push dword 0 ;argv
push dword %2 ;&argc
push dword 0 ;Number of Options
push dword 0 ;Options Array
push dword XtShell ;Class Name (String)
push dword AppContext ;Application Context (Ptr)
call XtVaAppInitialize
add esp, 36
mov [%1], eax
%endmacro
%macro XtMsgLoop 0
EXTERN XtAppMainLoop
push dword [AppContext]
call XtAppMainLoop
add esp, 4
%endmacro
%macro RegisterCallback 1
SECTION .data
CBType: DB "callback",0
SECTION .code
push NULL ;
push dword %1 ;CallBack function
push dword CBType ;
push eax ;CallBack parent (ButtonWidge)
call XtAddCallback
add esp, 16
%endmacro
%macro CALLBACK 1
SECTION .data
Call_Data_%1: DD 0
Client_Data_%1: DD 0
Widget_%1: DD 0
GLOBAL %1
SECTION .text
%1:
pop eax
mov [Call_Data_%1], eax
pop ebx
mov [Client_Data_%1], ebx
pop ecx
mov [Widget_%1], ecx
%endmacro
%define ENDCALLBACK nop
%macro ENTRYPOINT 1
GLOBAL %1
%1:
%endmacro
;==========================================================================-EOF
Most of the macro file should be readily apparent if you are familiar with the
NASM macro facility. I did take the opportunity to clean up the callback
function, so that the parameters to the callback are saved in variables, but for
the most part it does the same as the equivalent code in the preceding asm
example.
Now the xthell.asm sample will look as follows:
;===================================================================-xthell.asm
BITS 32
%INCLUDE "xt.mac"
;========================================================XTRN=====
XtAPI XtVaCreateManagedWidget
XtAPI XtAddCallback
XtAPI XtRealizeWidget
WIDGET commandWidgetClass
EXTERN exit
;========================================================DATA=====
SECTION .data
;------------
WDGTPTR ptrShell
WDGTPTR ptrButton
CLASS XHELL, "XHell"
CLASS HellButton, "HellButton"
CallbackType DB "callback",0 ;XtNcallback
ARGC times 128 DB 0
;========================================================CODE=====
SECTION .text
;------------
CALLBACK bail
push dword 0
call exit
ENDCALLBACK
ENTRYPOINT main
InitXt ptrShell, ARGC
push TERM_VARARGS
push eax ;Button parent (ShellWidge)
push dword [commandWidgetClass] ;Button widget type
push dword HellButton ;Button class name
call XtVaCreateManagedWidget
add esp, 16
mov [ptrButton], eax
RegisterCallback bail
push dword [ptrShell] ;Widget Handle
call XtRealizeWidget
add esp, 4
XtMsgLoop
ret
;==========================================================================-EOF
Much prettier and hey, only twice as long as the C version! ;)
Next issue I will dwell on Xt/Athena a little longer and come up with some more
practical methods of automating the coding process.
|