Thornton 2 dot Com
1K61U

GEOS Environment

Ariel's GEOS Programmer's Reference Guide
Back: Preface Up: Contents Next: GEOS Kernal Routines


GEOS Development
Back: GEOS: Graphics Up: GEOS Development Next: GEOS: Operating (WIP)

This page is still a work in progress.

Table of Contents

The GEOS Environment

GEOS is more than just a graphics abstraction library. The GEOS environment allows your programs to have text input and output using proportional fonts, clickable icons and buttons, and a clickable pull-down menu structure, all managed by GEOS for you.

Text Output in GEOS

On the Commodore 8-bit and Apple II-series computers, text is normally displayed in a monospace font: every character has the same width as every other character. GEOS, however, always displays text in a proportional font: the width of each character is proportional to the space its glyph actually uses. For example, the letters I and M are the same width in monospace fonts, but the letter M is wider than the letter I in proportional fonts.

GEOS displays text on the screen by stamping rectangular graphics objects. Each stamped object contains foreground and background pixels in the shape of a text character, and there's no transparency mask. If you display text on a clear background, the text will display without visible artifacts, but if you display text on a patterned background, the rectangle contanining each character will overwrite and clear parts of the background pattern.

Each stamped object corresponds to an ASCII character, and the collection is a font and size. In most operating systems, the size of a font is measured in points, which are 1/72 of an inch (about 352.777778 micrometers), but in GEOS the size is measured in pixels, which are 1/80 of an inch on paper (about 317.5 micrometers), but are also imprecisely called points.

GEOS fonts are specified by their font IDs (which map to names) and their font sizes. A font's size is always the height of the font in points (pixels).

GEOS has one always-resident font, BSW 9, which it uses for menus and dialog boxes. This is also the default font your program will use for text display.

The vertical components of a font are usually separated into three fields by four lines. The middle field is the x-height, which is the height of the lowercase letter X and most other lowercase letters; above it is the ascent field, where a lowercase letter's ascender stroke extends into, and below it is the descent field, where a lowercase letter's descender stroke extends into. The lines separating these fields are, in order top to bottom, the top line, the mean line, the baseline, and the bottom line. Lowercase letters usually stay between the baseline and the mean line, and uppercase letters usually stay between the baseline and the top line.

In font size and character coordinates, GEOS deals only with the top line, the baseline, and the complete height top to bottom for the vertical component. For the horizontal component, GEOS deals only with the left edge at the baseline, the font's card set width, and the individual character's width.

Font sizes larger than 28 are rare, but a font could theoretically (but impractically) have a size up to 255. The width of any character within a font depends on the character's pixel definitions, but no character definition can extend past 54 pixels wide.

In addition to a font and size, a character can be displayed in an additive variety of styles: bold, italic, underline, outline, and reverse video. The GEOS character set includes control codes for style changes that can be embedded in text strings, saving you the need to break strings and fiddle with bit flags to change styles mid-string.

Relevant Font and Style Variables
Name Purpose
baselineOffset Offset from the top line to the baseline.
curHeight Height (or font size) in pixels.
currentMode Bit flags for currently active styles.

GEOS text routines need one of two coordinates: the top left corner pixel of the first character, or the baseline left corner pixel of the first character. Add baselineOffset to the Y coordinate to convert top left to baseline left, and subtract baselineOffset from the Y coordinate to convert baseline left to top left.

Text Styles

When the style given by currentMode is not plain text ($00), GEOS applies a series of transformations to each character when displaying it. They are, in order:

Underline
The line 1 pixel below the baseline is inverted, causing characters to display with an underline.
Boldface
The character is stamped twice, with the second offset 1 pixel right of the first. The character's width increases by 1 pixel.
Outline
The character is transformed into an outline of itself, causing the character's width and height both to increase by 2 pixels (one at each edge).
Italic
Each pair of lines above the baseline is shifted 1 pixel right of the pair below it, and each pair of lines below the baseline is shifted 1 pixel left of the pair above it. The effect transforms the rectangular area of a character into a parallelogram, causing the character to lean forward about 22.5 degrees. The character width is not changed, and the character's baseline is not offset.
Reverse
The character is transformed into a reverse-video image of itself.

The italic style causes an artifact to manifest in mixed-style strings. At the start of italic mode, the part of the first italic character below the baseline overwrites the right edge of the last character before italic mode. At the end of italic mode, the rectangle of the first non-italic character overwrites the right edge of the last italic character above the baseline. Within a string of italicized characters, each character rectangle is transformed into a parallelogram before stamping onto the screen, ensuring no part of any italicized character is overwritten by the following italicized character. To keep this artifact from becoming visible when italicizing whole words, start italic mode just before the first italicized character, and end italic mode after the space, just before the first non-italicized character.

Because some style transformations alter the actual size of a character and some don't, GEOS provides GetRealSize to determine the actual dimensions of a given character in the current font, size, and style.

Special Text Considerations

Most text routines use dispBufferOn to determine which of the screens to draw text on.

In GEOS 128, the X coordinate given to text display routines is normalized the same as the X coordinate of graphics display routines. See the GEOS 128 X-axis doubling section of GEOS Graphics.

GEOS fonts are limited to defining characters for codes 32 through 159 ($20--$9f), and the text routines don't check whether or not the current font defines a character for a given code. In theory, a font can define up to 32 extended characters, but in practice, only one font has any characters above code 127: BSW 9, the built-in system font. In BSW 9, character code 128 is the shortcut character: the Commodore "chicken-lips" logo in GEOS and GEOS 128, and Apple's filled-apple logo in Apple GEOS.

Printing Characters

GEOS has two routines for printing single characters, PutChar and SmallPutChar. PutChar prints a single character on the screen, understands control codes, and processes faults in which text margins are exceeded. SmallPutChar just prints a single character with a minimum of processing: it doesn't understand control codes, and it simply clips a character at the margins if any text margin is exceeded.

Apple GEOS also has a routine for erasing a character: EraseCharacter.

PutChar checks that the character will fit entirely between leftMargin and rightMargin. By default, these are set to the extreme edges of the screen: 0 and SCPIXWIDTH-1 respectively.

Icons in GEOS

Icons are graphics objects on the screen that perform an action when clicked. The action is defined by your servicing routine for the icon.

The icon picture is a compacted bitmap graphics object ready for display by BitmapUp. Although they must be a graphics object, they are sometimes used for text buttons. In order for your program to use text in an icon, you have to paint in or pixel-art in the text using, for example, geoPaint. This is how GEOS renders buttons like the "Yes" and "No" or "OK" and "Cancel" buttons in dialog boxes, or the "Undo" button in geoPaint.

Your program puts icons on the screen by passing an icon table to DoIcons. GEOS will use the table to draw the icons on the screen, then it will keep track of the rectangles defined by the icons and dispatch your matching service routine when one is clicked.

The structure of a GEOS icon table required by DoIcons
Section Size Offset Name Byte Size Purpose
Constraints
Value with Special Meaning
Header 4 bytes OFF_NM_ICNS 0 byte Number of icons in this table.
1 to 31
OFF_IC_XMOUSE 1 word New mouse X coordinate
1 to SC_PIX_WIDTH-1
0 to leave mouse alone
OFF_IC_YMOUSE 3 byte New mouse Y coordinate.
0 to SC_PIX_HEIGHT-1
Icon 0 8 bytes OFF_PIC_ICON 0 word Pointer to icon picture.
Any address
$0000 to disable this icon
OFF_X_ICON_POS 2 byte Icon's X coordinate in cards.
0 to (SC_PIX_WIDTH/8)-1
C128: "|DOUBLE_B" for 80-col X doubling
Apple: "|INAUX_B" means OFF_PIC_ICON in aux mem
OFF_Y_ICON_POS 3 byte Icon's Y coordinate in pixels.
0 to SC_PIX_HEIGHT-1
OFF_WIDTH_ICON 4 byte Icon's width in cards.
1 to remaining screen width in cards
C128/Apple: "|DOUBLE_B" for 80-col/DHR X doubling
OFF_HEIGHT_ICON 5 byte Icon's height in pixels.
1 to remaining screen height
OFF_SRV_RT_ICON 6 word Vector to icon's service routine.
Any address
Icon 1 8 bytes OFF_NXT_ICON 8 8 bytes Offset from one icon to the next.
Icon N 8 bytes Icons 1 to N, where N is (value at offset OFF_NM_ICNS) - 1, have the same structure as icon 0.

In each icon in GEOS 128, bit 7 of OFF_X_ICON_POS is a flag indicating whether the icon's X coordinate should be doubled in 80-column mode automatically in order to preserve the 40-column aspect ratio. Bit 7 of OFF_WIDTH_ICON is a flag indicating whether the icon's bitmap image should likewise be automatically doubled in 80-column mode. These flags are ignored in 40-column mode. See GEOS X-Axis Doubling and GEOS 128 X-Axis Doubling for details.

In each icon in Apple GEOS, bit 7 of OFF_X_ICON_POS is a flag indicating whether the icon bitmap address in OFF_PIC_ICON is in main RAM or auxiliary RAM. Bit 7 of OFF_WIDTH_ICON is a flag indicating whether the icon's bitmap image should be doubled on the X axis or used at native resolution. See GEOS X-Axis Doubling and Apple GEOS X-Axis Doubling for details.

Here's an example code snippet using an icon table. The table places three icons in the middle of the screen and moves the mouse pointer to the first icon:

;***********************************************************************
;SAMPLE ICON TABLE
;***********************************************************************

;Icon positions and bitmap data

I_SPACE       = 1           ;space between our icons (in cards)

PaintIcon:

;bitmap image pasted here

PAINTW = PicW
PAINTH = PicH
PAINTX = 16/8
PAINTY = 80

WriteIcon:

;bitmap image pasted here

WRITEW = PicW
WRITEH = PicH
WRITEX = PAINTX + PAINTW + I_SPACE
WRITEY = PAINTY

PublishIcon:

;bitmap image pasted here

PUBLISHW      = PicW
PUBLISHH      = PicH
PUBLISHX      = WRITEX + WRITEW + I_SPACE
PUBLISHY      = WRITEY

;The actual icon data structure to pass to DoIcons follows
IconTable:

I_header:
       .byte  NUMOFICONS                 ;number of icon entries
       .word  (PAINTX*8) + (PAINTW*8/2)  ;position mouse over paint icon
       .byte  PAINTY + PAINTH/2          ;

I_entries:
PaintIStruct:
       .word  PaintIcon                  ;pointer to bitmap
       .byte  PAINTX, PAINTY             ;icon position
       .byte  PAINTW, PAINTH             ;icon width, height
       .word  PaintEvent                 ;event handler

WriteIStruct:
       .word  WriteIcon                  ;pointer to bitmap
       .byte  WRITEX, WRITEY             ;icon position
       .byte  WRITEW, WRITEH             ;icon width, height
       .word  WriteEvent                 ;event handler

PublishIStruct:
       .word  PublishIcon                ;pointer to bitmap
       .byte  PUBLISHX, PUBLISHY         ;icon position
       .byte  PUBLISHW, PUBLISHH         ;icon width, height
       .word  PublishEvent               ;event handler

NUMOFICONS    = (*-I_entries)/IESIZE     ;number of icons in table

;Dummy icon event routines which do nothing but return
PaintEvent:
WriteEvent:
PublishEvent:
       rts

(IESIZE is not in geosSym, but it appears to be in geoProgrammer 128. Its value is 8.)

When your program starts, no icons are installed. You can install any number of icon tables any number of times, but only the most recently installed table is active. When you install an icon table, the previously effective table is not uninstalled, but simply no longer used. This means that, although GEOS will take care of drawing new icons on the screen for you, it will not erase old icons from the screen. If your program uses more than one icon table during its run, then when you install a second icon table, you have to take care of either erasing old icons or giving the user some other form of feedback that the picture is no longer a clickable icon.

You can install icons on the foreground screen, the background screen, or both, depending on the bit flags of dispBufferOn when DoIcons is called. In most instances, you want them on the foreground screen or on both screens. Although GEOS takes care of displaying icons when the table is installed, it does not take care of restoring icons if any parts of them get drawn over or erased, such as when a submenu is drawn. If that happens, the icon's graphics won't be restored by GEOS unless it was drawn on the background screen when installed.

A specific icon in the icon table can be deactivated by changing its bitmap pointer at OFF_PIC_ICON to a null pointer ($0000), and the icon can be reactivated by changing its bitmap pointer to a non-null value. When the icon table is passed to DoIcons, the icon is not drawn if its bitmap pointer is null. If your program waits until after the icon table is installed to deactivate an icon, GEOS will not erase the icon picture from the screen; it will just skip over the deactivated icon when polling the icon table. If you deactivate an icon in a table that will be installed more than once, then you must restore its original bitmap pointer when reactivating the icon, or garbled data will be drawn instead when DoIcons reinstalls the icon table.

GEOS Bugs Regarding Icons

Due to a bug in GEOS, applications must always install an icon table even when not using icons, and every table must always have at least one icon defined. If your program won't use icons, then it should install a dummy icon table. For example:

; Installation of a dummy icon table during the
; program's initialization routine.

       ;initialization routine code

       LoadW  r0,#DummyIcons   ; point to icon table
       jsr    DoIcons          ; install it

       ;more initialization routine code

; The dummy icon table installed during initialization above.
;
DummyIcons:
       .byte  1       ; one icon in table
       .word  $0000   ; mouse X coordinate (0 = don't reposition)
       .byte  $00     ; mouse Y coordinate (0 = don't reposition)
       .word  $0000   ; first icon's bitmap pointer (null pointer)
       .byte  $00     ; first icon's card X coordinate
       .byte  $00     ; first icon's pixel Y coordinate
       .byte  1       ; first icon's card width
       .byte  1       ; first icon's pixel height
       .word  $0000   ; first icon's service routine (null vector)

A shorter example, sacrificing readability for compact boilerplate code:

                             ;initialization routine code
       LoadW  r0,#DummyIcons
       jsr    DoIcons
                             ;more initialization code
       rts

DummyIcons:
       ;      12 bytes: a 1, seven 0s, two 1s, and two 0s.
       .byte  1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0

Icon Clicks and Icon Service Routines

When the user clicks on an icon, GEOS first looks at iconSelFlag, flashes or inverts the icon accordingly, and calls the icon's service routine. When your icon service routine is called, it's called as a subroutine from MainLoop. The variable r0L holds the icon number in the current icon table (0 for first), and r0H holds a flag indicating whether the icon was single- or double-clicked: FALSE for single-click or TRUE for double-click.

Just before your icon service routine is called, while GEOS is checking to see whether the click is a double-click, it loads dblClickCount with 30 ($1e). At every Interrupt Level, GEOS decrements dblClickCount until it reaches 0. GEOS knows whether the click is a single-click or the second of a double-click by whether dblClickCount reached 0 or not.

If you want your icon to do the same thing whether the user single-clicks or double-clicks on it, then your service routine can simply ignore r0H. If you want your icon to do nothing when single-clicked and only activate when double-clicked, then your service routine can just rts back to MainLoop when r0H is FALSE. If you want the icon to activate only when single-clicked, throw out the second click by checking r0H for TRUE and rts if so.

Making your icons respond differently to single-clicks and double-clicks is a little bit trickier. In a double-click, GEOS may call your icon service routine twice, once for each click. There are at least two ways your routine can handle a double-click, which the Hitchhiker's Guide to GEOS calls the additive function method and the polled mouse method.

The additive function method means your routine first performs the single-click action on the first click then performs the double-click action on the second click. This is useful when your icons and menus use a "noun-verb" UI idiom, in which your icons are nouns the user selects and your menus are verbs that act on the selected noun(s), and you want a double-click to execute a default verb on the icon, such as (in a file manager) opening the file the icon represents. The HHGG's example for the additive function method:

;***********************************************************************
;      Icon double-click handler
;      additive function method
;***********************************************************************
IconEvent1:
       lda    r0H          ;check double-click flag
       bne    10$          ;branch if second click of a double-click

                           ;else, this is a single-click or the
                           ;first push of a double-click,
       jsr    InvertIcon   ;so just invert the selection
       bra    90$          ;and exit.
10$:
       jsr    OpenIcon     ;double-click detected, go process it
                           ;fall through to exit
90$:
       rts                 ;return to MainLoop

The polled mouse method polls the mouse for a double-click until dblClickCount expires before choosing an action. This is useful when you need an icon's single-click action and double-click action to be mutually exclusive actions. Instead of returning to MainLoop and letting GEOS process the second click of a double-click, in this method your routine watches mouseData for the second click of a double-click itself, delaying any action until the second click happens or dblClickCount expires. The HHGG's example for the polled mouse method, with corrections:

;***********************************************************************
;      Icon double-click handler
;      polled mouse method
;***********************************************************************
IconEvent2:

;User pressed mouse once, start double-click counter going

       LoadB  dblClickCount,#30   ;start delay

;Loop until double-click counter times out or button is released

10$:
       lda    dblClickCount       ;check double-click timer
       beq    30$                 ;If timed out, no double-click
       lda    mouseData           ;Else, check for release
       bpl    10$                 ;loop until released

;Mouse was released.  Loop until double-click counter times out or
;button is pressed a second time.

20$:
       lda    dblClickCount       ;check double-click timer
       beq    30$                 ;If timed out, no double-click
       lda    mouseData           ;Else, check for second press
       bmi    20$                 ;loop until pressed

;Double-click detected (no single-click)

       jsr    DoDoubleClick       ;do double-click action
       bra    90$                 ;exit

;Single-click detected (no double-click)

30$:
       jsr    DoSingleClick       ;do single-click action
                                  ;and fall through to exit

;Exit

90$:
       rts                        ;return to MainLoop

GEOS will call your icon's service routine when the user presses the mouse button on your icon, but it will not when the user releases the mouse button on your icon. GEOS will instead vector through otherPressVec when the user releases the mouse button. If your program hooks into otherPressVec as well, you can ignore button release events, or you will have to find a way to detect when a release was due to an icon click.

Icon Precedence

When you give DoIcons an icon table, it will draw icons on the screen in the order they're given in the table. If any icons overlap, then later icons will appear on top of earlier icons. GEOS also polls for icon clicks in the same order, the order they're given in the table. If the user clicks on a pixel shared by overlapping icons, then GEOS will dispatch the service routine for the earlier icon, not the later icon, even though the later icon appears on top to the user.

Menus always take precedence over icons. If a menu is open, the open menu obscures an icon, and the user clicks on a pixel that is both in the open menu and squarely over an icon, GEOS will process the menu item click and ignore your icon.

Menus in GEOS

A well organized on-screen command menu is one of the defining characteristics of a graphical user interface. GEOS provides a structured menu system organized as a tree of tables and several routines for managing the menus in just about any way your program needs. After installing a menu tree, GEOS manages all aspects of menu operation for you: showing whatever submenus the user clicks on, dispatching the right service routine when the user clicks on a menu item, and removing open submenus for you when the user moves off without clicking an item.

By convention, the root of the menu tree, the main menu, is a horizontal menu aligned to the upper-left corner of the screen. First-level submenus are by convention vertical and aligned to appear directly beneath the corresponding main menu item as if attached to it. Second-level submenus and lower are by convention vertical and aligned to appear directly to the right of the higher-level submenu, also as if attached to it. These conventions are intended to give the user the feeling that your program is an integral part of GEOS as a system, as well as allowing the user to transfer knowledge of how to use other GEOS programs to using yours. Your program is free to break from this menu structure convention, but you shouldn't without very good and immediately obvious reasons.

Each menu is a list of text items visually separated from each other. Each item can be one of three types: a submenu, an action, or a dynamic submenu which mixes an action with on-the-fly menu construction. For submenus, GEOS follows the item to a new table and shows another list of items. For actions, GEOS leaves the menu open and calls your service routine. For dynamic submenus, GEOS leaves the menu open, calls your service routine, and expects to get a new table made just in time by your service routine. When GEOS calls your menu item service routine, it is called as a subroutine of MainLoop.

The Menu Structure Tree

Your program gives a menu structure to GEOS through a set of tables organized into a hierarchical tree of linked lists. Each table defines a single menu, its location on the screen, and its dimensions. Note that you have to specify the coordinates and size of the rectangle containing the menu; GEOS does not calculate it for you.

The menu structure is installed by calling DoMenu with the main menu table. GEOS will monitor for menu events and draw and erase menus for you until you either install a new menu with DoMenu or you clear the relevant bits of of mouseOn: Clearing MENUON_BIT disables menu polling, and clearing MOUSEON_BIT disables mouse polling. (Note: DoMenu has a bug that turns ICONSON_BIT on when installing a menu, causing GEOS to poll the current icon table even when none is installed. See GEOS Bugs Regarding Icons for installing a dummy icon table to work around this bug.)

The total number of menu levels allowed is four: a main menu and three levels of submenus. The current menu level is given in menuNumber: 0 for the main menu, and 1, 2, or 3 for each level of depth for a submenu.

The structure of a GEOS menu table required by DoMenu.
Section Size Offset Name Byte Size Purpose
Constraints
Header 7 bytes OFF_MY_TOP 0 byte Y coordinate of menu top edge
0 to SC_PIX_HEIGHT-1
OFF_MY_BOT 1 byte Y coordinate of menu bottom edge
0 to SC_PIX_HEIGHT-1
OFF_MX_LEFT 2 word X coordinate of menu left edge
0 to 255
OFF_MX_RIGHT 4 word X coordinate of menu right edge
0 to 255
OFF_NUM_ITEMS 6 byte Bit flags and number of menu items
1 to 15 plus bits 7 and 6
Item 0 5 bytes OFF_1ST_M_ITEM, OFF_TEXT_ITEM* 7, +0 word Pointer to item text string
any address
OFF_TYPE_ITEM* +2 byte Menu item type
$80, $40, or $20
OFF_POINTER_ITEM* +3 word Pointer to submenu table or vector to item service routine
any address
Item 1 5 bytes +5 5 bytes Offset from one menu item to the next.
Item N 5 bytes Items 1 to N, where N is ( (value at offset OFF_NUM_ITEMS) | $0f) - 1, have the same structure as item 0.

Note: An asterisk (*) indicates a constant that is not in geosSym.

Note: GEOS and GEOS 128 versions before v2.0 have a bug where they don't correctly handle menus extending right of X coordinate 255. Apple GEOS and all editions of GEOS v2.0 correct this bug. The constraints given in the table above are for GEOS versions that have this bug. In GEOS versions without this bug, the constraints are 0 to SC_PIX_WIDTH-1.

Bit flags at OFF_NUM_ITEMS
Bit Constant Value Meaning
7 HORIZONTAL %00000000 A row of items, like most main menus.
VERTICAL %10000000 A column of items, like most submenus.
6 CONSTRAINED %01000000 The mouse is constrained: the user must click an item or go back.
UN_CONSTRAINED %00000000 The mouse is free to leave the menus without choosing an item.
5 Reserved.
4-0 Number of items in this menu (1-15).

Bit 6, the CONSTRAINED/UN_CONSTRAINED flag, has no meaning for the main menu. If you set it in the main menu table, GEOS will ignore it. GEOS will only constrain the mouse to the menu's rectangle if the bit is set in a submenu's menu table. Note that when CONSTRAINED is set, the user will not be allowed to leave the submenu except off the edge leading back to the parent menu.

Values at OFF_TYPE_ITEM*
Constant Value Meaning of word at OFF_POINTER_ITEM*
SUB_MENU $80 Pointer to a submenu table.
DYN_SUB_MENU $40 Vector to a dynamic submenu service routine.
MENU_ACTION $20 Vector to a menu item service routine.

When the user clicks on a menu item pointing to a submenu, GEOS displays the submenu as if calling ReDoMenu for you automatically. When the mouse moves off a submenu to its parent menu, GEOS calls DoPreviousMenu automatically for you. When the mouse moves off a submenu and doesn't remain within any open menu, GEOS calls GotoFirstMenu for you automatically.

When the user clicks on a menu item pointing to one of your service routines, whether it's a menu item or a dynamic submenu, GEOS leaves the menu open and calls your service routine.

An example set of menu tables from the geoProgrammer application SampleSeq, with corrections from the geoProgrammer Errata applied:

       ; initialization routine code

       LoadW  r0,#MenuTable  ;point to menu definition table
       lda    #0             ;place cursor on first menu item when done
       jsr    DoMenu         ;have GEOS draw the menus on the screen

       ; more initialization routine code
       rts

MenuTable:                   ;menu definition table for main horizontal menu
       .byte  0,14           ;top and bottom y coordinates
       .word  0,49           ;left and right x coordinates
       .byte  2 | HORIZONTAL ;number of menu items, type of menu

       .word  GeosText       ;pointer to text for menu item
       .byte  SUB_MENU       ;type of menu
       .word  GeosSubMenu    ;pointer to menu structure

       .word  FileText       ;pointer to text for menu item
       .byte  SUB_MENU       ;type of menu
       .word  FileSubMenu    ;pointer to menu structure

GeosSubMenu:                 ;menu definition table for GEOS vertical menu
       .byte  15,30          ;top and bottom y coordinates
       .word  0,79           ;left and right x coordinates
       .byte  1 | VERTICAL   ;number of menu items, type of menu

       .word  AboutText      ;pointer to text for menu item
       .byte  MENU_ACTION    ;type of action
       .word  DoAbout        ;pointer to handler routine

FileSubMenu:                 ;menu definition table for FILE vertical menu
       .byte  15,44          ;top and bottom y coordinates
       .word  29,64          ;left and right x coordinates
       .byte  2 | VERTICAL   ;number of menu items, type of menu

       .word  CloseText      ;pointer to text for menu item
       .byte  MENU_ACTION    ;type of action
       .word  DoClose        ;pointer to handler routine

       .word  QuitText       ;pointer to text for menu item
       .byte  MENU_ACTION    ;type of action
       .word  DoQuit         ;pointer to handler routine

;text strings for above menus

GeosText:
       .byte  "geos",0
FileText:
       .byte  "file",0
AboutText:
       .byte  "SampleSeq info",0
CloseText:
       .byte  "close",0
QuitText:
       .byte  "quit",0

;handler routines

DoAbout:
       jsr    GotoFirstMenu  ;roll menu back up
       ;code to handle this event goes here
       rts                   ;all done

DoClose:
       jsr    GotoFirstMenu  ;roll menu back up
       ;code to handle this event goes here
       rts                   ;all done

DoQuit:
       jsr    GotoFirstMenu  ;roll menu back up
       ;code to handle this event goes here
       jmp    EnterDeskTop   ;return to deskTop!

Menu Events and Service Routines

When the user clicks on a menu item, GEOS inverts the menu item clicked and checks the menu table for the next action.

Item Type SUB_MENU

If the item type is SUB_MENU, GEOS increments menuNumber, displays the submenu according to the submenu's menu table, and repositions the mouse over its first item. If the mouse then moves off the submenu to its parent menu, GEOS decrements menuNumber and erases the submenu. If the mouse instead moves off the submenu and all its parent menus, GEOS works backwards through the menu tree, decrementing menuNumber and erasing the corresponding menu, until menuNumber is 0 and only the main menu is visible.

Item Type DYN_SUB_MENU

If the item type is DYN_SUB_MENU, GEOS calls your service routine for the menu item. When your service routine is called, it is called as a subroutine of MainLoop.

On entry to your service routine, A will contain the menu item number clicked (0 to 14). Your service routine must construct a menu table, load r0 with a pointer to that table, then rts back to MainLoop. On return, GEOS will process your menu table as if the item type the user clicked on was SUB_MENU instead.

If for some reason your service routine needs to tell GEOS that this item can't have a submenu at this particular time, it can do that by loading r0 with a null pointer ($0000) instead. On return, GEOS will leave the current menu open and active, as if the user didn't click on a menu item.

Item Type MENU_ACTION

If the item type is MENU_ACTION, GEOS flashes the menu item selectionFlash interrupt cycles before calling your service routine for the menu item. When your service routine is called, it is called as a subroutine of MainLoop. On entry to your service routine, A will contain the menu item number clicked (0 to 14).

GEOS will not do anything further with the open menus until instructed by your service routine. Your service routine should call GotoFirstMenu to roll up all the open submenus, DoPreviousMenu to move the user back to the parent submenu, or ReDoMenu to reactivate the current submenu. For the most common menu item actions, your routine would call GotoFirstMenu, then perform the desired command, and end with rts back to MainLoop.

Menu Parlor Tricks

If your menu needs to show a currently active selection, like geoWrite does in the font and size menus, you can construct your menu item text strings with two leading spaces; then, on the fly in a dynamic or menu item service routine, replace the first space with an asterisk in the string for the currently active selection (or asterisk with a space to show it's no longer active). In the default system font, which GEOS always uses for menus, the asterisk is just one pixel wider than the space, a difference most users won't notice.

Menu item text strings are made of GEOS ASCII characters and can contain, for example, the GOTOX control code and coordinate bytes to align things like selection characters and keyboard shortcut characters.

Menus and Mouse Fault Events

A mouse fault event occurs when the user moves the mouse pointer outside of the rectangle it's constrained to:

When this happens, GEOS resets the mouse coordinates to the nearest edge and vectors through mouseFaultVec.

When a submenu is open, there is another kind of mouse fault event: the mouse leaving the currently open menu. When this happens while mouseFaultVec points to the system mouse fault routine, GEOS calls DoPreviousMenu to roll up the menu the mouse left, and GEOS continues to call DoPreviousMenu until either it finds the mouse inside the new currently open menu or the main menu is the only menu left open.

When the menu type is unconstrained, the rectangle the mouse is constrained to is the entire screen dimensions. When the menu type is constrained, however, GEOS resets mouseTop, mouseBottom, mouseLeft, and mouseRight to the menu's rectangle.

The Hitchhiker's Guide to GEOS states that the only edge the mouse can leave a constrained menu through are the top edge in a vertical menu or the left edge in a horizontal menu. In both cases, this generates a mouse fault event, and GEOS responds to it by calling DoPreviousMenu, assuming that the edge leads to the parent menu.

If your program wants to constrain the mouse to a portion of the screen and handle mouse fault events, BSW recommended a few before and after tasks:

Other Mouse Click Events in GEOS

The GEOS Main Loop polls for clicks on icons and menus according to the currently active icon and menu table and dispatches the appropriate event service routine for you. It also polls for clicks on the screen outside of any active icon or menu, as well as any mouse button release event; when one happens, it vectors through otherPressVec.

Normally, otherPressVec is a null vector ($0000), and GEOS doesn't vector through it. When you want to handle mouse click events, you can load otherPressVec with a pointer to your event service routine. On entry, mouseXPos and mouseYPos hold the current mouse coordinates, and mouseData bit 7 holds the current button status.

Note that GEOS will only vector through otherPressVec when the user clicks outside of icons and menus, but it will always vector through otherPressVec when the user releases the mouse button, even when the click was on an icon or menu. If your service routine needs to handle button release events, such as at the end of a mouse drag, then you'll have to come up with a way to determine whether or not the release event has a matching click event. Otherwise, your service routine can throw button release events away. For example:

MyOPV:
       bit    mouseData   ; Check mouse button status.
       bpl    10$         ; Pressed: Handle event.
       rts                ; Else, do nothing.
10$:
       ;Service the mouse click event.
       rts

Unlike with icons, GEOS does not poll for double-click events on arbitrary screen coordinates. If your routine needs to respond differently to single and double clicks, then you'll have to poll for double clicks yourself similar to the polled mouse method described in Icon Clicks and Icon Service Routines.