Using Menus in Xt - THE.UNIX.WORLD
Pubblicato da mammon_ il 06/1999
Livello intermedio
Introduzione
/* Questo articolo e' stato tratto dall'Assembly Programming Journal numero 4 */
::/ \::::::. :/___\:::::::. /| \::::::::. :| _/\:::::::::. :| _|\ \::::::::::. :::\_____\:::::::::::............................................THE.UNIX.WORLD
Iniziamo
A simple one-button application will not get you very far in the X world. A good application must have multiple components, including menus, dialog boxes, application windows, and accelerators. In this article I will present the use of Xt menus in assembly language as a first step towards producing a complete application; future articles will cover the integration of forms, dialog boxes, and other resources into a complete application.
I presented some Xt macros for Nasm last issue, but have revised them after a bit more testing. The CALLBACK macros have been rewritten, and an InitXtApp macro has been written as well to take care of the generic Xt application startup code.
;=======================================================================-xt.inc
%macro InitXtApp 5
;------Usage: InitXtApp TopLevelWidget, Context, ClassName, ARGC, ARGV
EXTERN XtVaAppInitialize
push dword 0
push dword 0
push dword 0
push dword %5
push dword %4
push dword 0
push dword 0
push dword %3
push dword %2
call XtVaAppInitialize
add esp, 36
mov [%1], eax
%endmacro
%macro CALLBACK 1
;------Usage: CALLBACK name
GLOBAL %1
%1:
%define CallData [ebp+8]
%define ClientData [ebp+12]
%define Widget [ebp+16]
push ebp
mov ebp, esp
%endmacro
%macro CALLBACK_RETURN 0
;------Usage: CALLBACK_RETURN
mov esp, ebp
pop ebp
ret
%endmacro
%macro ShowXtWidget 1
;------Usage: ShowXtWidget Widget
EXTERN XtRealizeWidget
push dword [%1]
call XtRealizeWidget
add esp, 4
%endmacro
%macro XtAppLoop 1
;------Usage: XtAppLoop Context
EXTERN exit
EXTERN XtAppMainLoop
push dword [%1]
call XtAppMainLoop
add esp, 4
push dword 0
call exit
%endmacro
;==============================================================================
The first thing that must be done in creating an application menu is to create a "menubar" that will contain the component menu buttons. This often takes the place of a Box or Form widget that resides on the top or left edge of an application. The menu bar will later be "filled" with a button for each menu [e.g. File, Edit, Options, Help].
;=======================================================================-xt.inc
%macro CreateMenuBar 3
;------Usage: CreateMenuBar ParentWidget, ClassName, MenubarWidget
EXTERN boxWidgetClass
EXTERN XtCreateManagedWidget
push dword 0
push dword 0
push dword [%1]
push dword [boxWidgetClass]
push dword %2
call XtCreateManagedWidget
mov [%3], eax
add esp, 20
%endmacro
;==============================================================================
Following this, a button must be created for each menu as a component of the Box or menubar widget. Each button is then associated with a Menu widget through the XtCreatePopupShell call.
;=======================================================================-xt.inc
%macro AddMenu 4
;------Usage: AddMenu MenubarWidget, MenuText, MenuButtonWidget, MenuWidget
%ifndef _menu_classname
EXTERN menuButtonWidgetClass
EXTERN XtCreateManagedWidget
EXTERN simpleMenuWidgetClass
EXTERN XtCreatePopupShell
[section .data]
_menu_class db "menu",0
[section .text]
%define _menu_classname
%endif
push dword 0
push dword 0
push dword [%1]
push dword [menuButtonWidgetClass]
push dword %2
call XtCreateManagedWidget
mov [%3], eax
add esp, 20
push dword 0
push dword 0
push dword [%3]
push dword [simpleMenuWidgetClass]
push dword _menu_class
call XtCreatePopupShell
mov [%4], eax
add esp, 20
%endmacro
;==============================================================================
Once the menu is created, it must be filled with menu items. The standard menu item is an smeBSBObject, meaning "simple menu entry:
;=======================================================================-xt.inc
%macro AddMenuItem 4
;------Usage: AddMenuItem MenuWidget, MenuItemText, MenuItemID, Callback
%ifndef menu_entry_ptr
EXTERN smeBSBObjectClass
EXTERN XtCreateManagedWidget
EXTERN XtAddCallback
[section .data]
MenuEntry:
.ptr dd 0
_callback_type db "callback",0
[section .text]
%define menu_entry_ptr
%endif
push dword 0
push dword 0
push dword [%1.ptr]
push dword [smeBSBObjectClass]
push dword %2
call XtCreateManagedWidget
mov [MenuEntry.ptr], eax
add esp, 20
push dword %3
push dword %4
push dword _callback_type
push dword [MenuEntry.ptr]
call XtAddCallback
add esp, 16
%endmacro
;==============================================================================
Note that the pointer to the Menu Item Entry widget is needed only to install the callback, and can then be discarded.
;=======================================================================-xt.inc
%macro AddMenuSeparator 1
;------Usage: AddMenuSeparator MenuWidget
%ifndef _szseperator
EXTERN smeLineObjectClass
EXTERN XtCreateManagedWidget
[section .data]
_szSep db "line",0
[section .text]
%define _szseperator
%endif
push dword 0
push dword 0
push dword [%1.ptr]
push dword [smeLineObjectClass]
push dword _szSep
call XtCreateManagedWidget
mov [MenuEntry.ptr], eax
add esp, 20
%endmacro
;==============================================================================
Why so many macros? To ease the tedium. The Xt code presented below was many pages longer before I converted the menu creation routines --which run 10 to 20 lines apiece-- into macros. Also, looking at the sample application, you will see that all of the "generic" Xt code has been compressed to a few lines, leaving the bulk of the "real code" in the callback routine and in the resource definitions.
WidgetInstance:
.ptr dd 0
.name db 'name',0
This makes things easier, for the widget pointer can now be referred to as [WidgetInstance.ptr], and the widget name can be referred to as WidgetInstance.name. Notice that all widget pointers must be referenced in brackets; the typical C use of a pointer is to pass the address contained in the pointer, not the address of the pointer itself [unless the pointer is dereferenced by a "&", in which case the brackets should not be used in the assembly language equivalent].
MenuName:
.ptr
.name
.button
.Entryname
.EntrynameID
This allows the pointer for each menu button to be stored along with the rest of the menu data, and also allows menu entries to be referenced as "members" of the menu "structure", e.g. MenuName.Entryname would be the text of the menu item, and MenuName.EntrynameID would be the integer ID for the menu item.
;===================================================================-xtmenu.asm
BITS 32
EXTERN exit
EXTERN printf
%include "Xt.inc"
;==========================================================================DATA
[section .data]
;______________Widgets__________________________
Shell:
.ptr dd 0
.name db "shell",0
MenuBar:
.ptr dd 0
.name db "menubar",0
;_______________Menus___________________________
FileMenu:
.ptr dd 0
.name db "File",0
.button dd 0
.New db "New",0
.NewID dd "101"
.Open db "Open",0
.OpenID dd "102"
.Save db "Save",0
.SaveID dd "103"
.Close db "Close",0
.CloseID dd 104
.Exit db "Exit",0
.ExitID dd 105
HelpMenu:
.ptr dd 0
.name db "Help",0
.button dd 0
.About db "About",0
.AboutID dd 201
;____Classes____________________________________
XtMenu: db "XtMenu",0
;____Misc_______________________________________
ARGC: times 128 db 0
szOutString db "%s selected",0ah,0dh,0
AppContext dd 0
;==========================================================================CODE
[section .text]
CALLBACK CBMenuSelect
mov ebx, ClientData
mov eax, [ebx]
cmp eax, dword [FileMenu.NewID]
je MenuNew
cmp eax, dword [FileMenu.OpenID]
je MenuOpen
cmp eax, dword [FileMenu.SaveID]
je MenuSave
cmp eax, dword [FileMenu.CloseID]
je MenuClose
cmp eax, dword [FileMenu.ExitID]
je MenuExit
cmp eax, dword [HelpMenu.AboutID]
je MenuAbout
jmp unhandled
MenuNew:
push dword FileMenu.New
jmp do_printf
MenuOpen:
push dword FileMenu.Open
jmp do_printf
MenuSave:
push dword FileMenu.Save
jmp do_printf
MenuClose:
push dword FileMenu.Close
jmp do_printf
MenuAbout:
push dword HelpMenu.About
do_printf:
push dword szOutString
call printf
add esp, 8
unhandled:
CALLBACK_RETURN
MenuExit:
mov esp, ebp
pop ebp
push dword 0
call exit
ret
;____________________________________________________________PROGRAM_ENTRYPOINT
GLOBAL main
main:
InitXtApp Shell.ptr, AppContext, XtMenu, ARGC, 0
;-------Make Menu
CreateMenuBar Shell.ptr, MenuBar.name, MenuBar.ptr
AddMenu MenuBar.ptr, FileMenu.name,FileMenu.button, FileMenu.ptr
AddMenu MenuBar.ptr, HelpMenu.name,HelpMenu.button, HelpMenu.ptr
;-------Build File Menu
AddMenuItem FileMenu, FileMenu.New, FileMenu.NewID, CBMenuSelect
AddMenuItem FileMenu, FileMenu.Open, FileMenu.OpenID, CBMenuSelect
AddMenuItem FileMenu, FileMenu.Save, FileMenu.SaveID, CBMenuSelect
AddMenuItem FileMenu, FileMenu.Close, FileMenu.CloseID, CBMenuSelect
AddMenuSeparator FileMenu
AddMenuItem FileMenu, FileMenu.Exit, FileMenu.ExitID, CBMenuSelect
;-------Build Help Menu
AddMenuItem HelpMenu, HelpMenu.About, HelpMenu.AboutID, CBMenuSelect
;-------Show Top-Level Widget
ShowXtWidget Shell.ptr
;-------Enter Xt Application Loop
XtAppLoop AppContext
;___Compile_Strings_________________________________________
; nasm -f elf xtmenu.asm
; gcc -o xtmenu xtmenu.o -lXaw -lXt -lX11 -L/usr/X11R6/lib
;==========================================================================-EOF
The strings needed to compile and link Xt assembly apps are provided at the end of the file. Note once again how short the main application code is; the menu data could easily be moved into an xtmenu.inc file and thereby trim the "apparent size" of the application down to the initialization code and the callback.Conclusioni
Further automation could involve creating a "resource editor" which would produce .INC files compatible with the Xt.inc macros, as well as creating high-level calls to automatically adjust the stack and high-level structures to deal with message handling in the callback.