Statistics

Members: 1927
News: 293
Web Links: 1
Visitors: 3933337

Who's Online

Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Using Menus in Xt
User Rating: / 0
PoorBest 
Written by Mammon   


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].

I have opted to use the Box widget for the menubar as it requires less initialization than the Form widget; normally it defaults to a vertical stacking of the component buttons, but this can be fixed with further configuration and will be addresses in a future article.

To create a menubar widget I have create a macro which will simply create a Box widget; it takes as its parameters a pointer to the parent widget [usually the top-level widget], the name of the menubar class, and a dword variable which will become a pointer to the Box instance: ;=======================================================================-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.

In Xt Athena, a menu is a button which, when pressed, creates a "Popup Shell"; that is, a container which contains the various menu items. These items are known as smeObjectClasses, or "simple menu entry" objects; selecting a simple menu entry from the popup shell [in plain English, "selecting a menu item"] will cause the callback for that menu item to be invoked and any data associated with the menu to be sent to the callback.

The AddMenu macro will create a menu button and popup shell for a given menu; it takes as its parameters the pointer to the parent Menubar widget, the ASCII string to be displayed in the menu [e.g. "File", "Edit", etc], a dword variable to be filled with a pointer to the menu button, and a dword variable to be filled with a pointer to the menu [or "popup shell"] itself: ;=======================================================================-xt.inc %macro AddMenu 4
;------Usage: AddMenu MenubarWidget, MenuText, MenuButtonWidget, MenuWidget %ifndef menuclassname
EXTERN menuButtonWidgetClass
EXTERN XtCreateManagedWidget
EXTERN simpleMenuWidgetClass
EXTERN XtCreatePopupShell
[section .data]
menuclass db "menu",0
[section .text]
%define menuclassname
%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 menuclass
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: Bitmap-String-Bitmap"; that is, each line can consist of a bitmap followed by a string followed by a bitmap. This can be useful for providing "visual"/eyecandy menus, or for checking and unchecking [via an "x" bitmap] menu items. For this example, I am using only string menu entries.

Menu items are added with the standard CreateManagedWidget function, then associated with a callback routine just like standard Xt widgets. The AddMenuItem macro performs both of these functions and takes as its parameters the pointer to the parent Menu, the text to be displayed in the menu item, a pointer to the data associated with the menu item, and the address of the callback routine:
;=======================================================================-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
callbacktype  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 callbacktype
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.

I have also included a separator menu item to break up the menus a bit; this is simply a menu item of class smeLineObject which does nothing; as such, the AddMenuSeparator macro requires only the pointer to the parent Menu as a parameter:
;=======================================================================-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.

I have chosen to use a single callback for all of the menu items; this is the most simple and perhaps the most efficient method. Each menu entry has a unique integer ID which is passed to the callback as data when the menu item is selected; the callback compares its incoming ClientData variable with each of the menu entry IDs until it finds a match, whereupon it jumps to a specific subroutine for each different ID. In short, a typical message handler -- though I refrained from my notorious call-table and SWITCH/CASE macros in this case for the sake of clarity.

The structure of the Widget declarations in this file could also bear some discussion. I have chosen to treat all widgets declared in the application [not references to the external widget classes reference by the app, but rather the pointers to those classes] as primitive structures of the following form:
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].

The menu widgets have a similar, but more advanced syntax: 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.

Now for the actual implementation:
;===================================================================-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.

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.