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
- GEOS: Graphics
- GEOS: Environment
- GEOS: Operating
- GEOS: System
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.
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.
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.
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 | 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.
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 the mouse goes above: mouseYPos is less than mouseTop,
- When the mouse goes below: mouseYPos is greater than mouseBottom,
- When the mouse goes left: mouseXPos is less than mouseLeft, or
- When the mouse goes right: mouseXPos is greater than mouseRight,
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:
Before altering mouseTop, mouseBottom, mouseLeft, or mouseRight, disable menus by clearing the MENUON_BIT of mouseOn first.
After not needing the mouse constrained anymore, reset mouseTop, mouseBottom, mouseLeft, and mouseRight to the screen's edges (0, SC_PIX_HEIGHT - 1, 0, and SC_PIX_WIDTH - 1 respectively), then call StartMouseMode to restore and reenable the GEOS mouse fault service routine.
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.