This is a complex API library written by Apple circa 1985. The Apple Programmers and Developers Association (APDA) documentation can be found at:
Changes from this documentation are called out below.
The Graphics Primitives are likely originally authored by Bill Budge, based on his The Graphics Page series in Softalk magazine:
- Debut: The Graphics Page (Oct 1983)
- De-Wozzing the Apple II (Nov 1983)
- Facts of the Apple's hi-res screen (Dec 1983)
- Budge's Graphics Zone, HLine (Feb 1984)
- Pretzel Logic: HLine, swatches, SetPattern (Mar 1984)
- Hi-res clipping and windowing (Apr 1984)
- Cartesian square dancing (Jun 1984)
- Graphics Primitives - screen management, lines, rects, polys, text, patterns, pens
- Mouse Graphics - windows, menus, events, cursors
- Creating Applications and DeskTop Desk Accessories
For the purposes of DeskTop, the entry point is fixed at $4000 AUX, called MLI-style:
JSR $4000
.byte call
.addr params
Result will be in A, with Z bit set, 0 indicating success (so BNE error
works).
ca65 syntax is used for primitives: .byte
, .word
(interpreted as 16-bit signed integer), .addr
(16-bit address), .res N
(N byte buffer)
.word xcoord
.word ycoord
.word x1
.word y1
.word x2
.word y2
or equivalently:
Point topleft
Point bottomright
A simple repeating 8x8 pattern is defined by 8 bytes. All bits of each byte are used.
.byte row0
.byte row1
.byte row2
.byte row3
.byte row4
.byte row5
.byte row6
.byte row7
Used with GrafPorts to define offsets/clipping, and bitmaps for source data.
Point viewloc
.addr mapbits $2000 for the screen, or bitmap bits
.byte mapwidth $80, or stride for bitmap
.byte reserved
Rect maprect a.k.a. cliprect
There is always a current GrafPort (or "port" for short) that defines the destination and pen state of drawing operations.
MapInfo portmap
Pattern penpattern
.byte colormask_and
.byte colormask_or
Point penloc
.byte penwidth horizontal pen thickness
.byte penheight vertical pen thickness
.byte penmode
.byte textback text background
.addr textfont
.byte count number of vertices in this polygon
.byte last high bit set if there are more polygons
Point vertex0
... repeats for each vertex
... repeats for each polygon
This is a MousePaint-compatible font. Single- and double-width fonts are supported; max height is 16 pixels.
.byte fonttype 0=regular, $80=double-width
.byte lastchar char code of last character (usually $7F)
.byte height pixels (1-16)
.res N charwidth pixels, for each char
.res N row0 bits
.res N row0right bits (double-width only)
... repeats for each row
No-op
No parameters.
No parameters.
Configure display switches
Parameters:
.byte flags bit 0=hires, 1=page2, 2=mixed, 3=text
Initialize GrafPort to standard values
Parameters:
(input is address of GrafPort record)
Set current port as specified
Parameters:
(input is address of GrafPort record)
Get pointer to current port
Parameters:
.addr port (out)
This also copies the current GrafPort data from the zero page to the current port. When SetZP1
has been called to disable preserving the zero page, it is necessary to call this manually to retain the GrafPort state.
Set just the mapinfo (viewloc, mapbits) of the current grafport.
Parameters:
(input is address of MapInfo record)
Set the pen mode of the current grafport.
Parameters:
.byte mode pen*/notpen*
Pen modes are:
- 0 = pencopy
- 1 = penOR
- 2 = penXOR
- 3 = penBIC
- 4 = notpencopy
- 5 = notpenOR
- 6 = notpenXOR
- 7 = notpenBIC
Set the pattern of the current grafport.
Parameters:
.res 8 pattern 8x8 pixel pattern for PaintRect calls
Set the color masks of the current grafport.
Parameters:
.byte and_mask
.byte or_mask
These flags are primarily for controlling the high bit of bytes on the double-hires graphics screen, which is useful for certain RGB cards.
Set the pen size of the current grafport.
Parameters:
.byte penwidth horizontal pen thickness
.byte penheight vertical pen thickness
Set the font of the current grafport.
Parameters:
.addr textfont font definition
Set the text background of the current grafport. Text is drawn in the inverse of this color.
Parameters:
.byte backcolor 0=black, $7F=white
Set pen location (relative) of the current grafport.
Parameters:
.word xdelta
.word ydelta
Set pen location (absolute) of the current grafport.
Parameters:
Point pos
Draw line from pen location (relative) of the current grafport.
Parameters:
.word xdelta
.word ydelta
Draw line from pen location (absolute) of the current grafport.
Parameters:
Point pos
Fill rectangle with pattern of the current grafport.
Parameters:
Rect rect
Draw rectangle with pen mode/size of the current grafport.
Parameters:
Rect rect
Is current position in bounds? A=$80 true, 0 false
Parameters:
Rect rect
Draw bitmap.
Parameters:
(input is address of MapInfo record)
Note that the cursor is not hidden automatically for this operation.
See PaintBitsHC
.
If PaintBits
is used the cursor is not hidden, then the caller must
assure that the cursor save/restore area and the bitmap do not
intersect, or when the cursor moves the display will be distorted.
In general, PaintBitsHC
should be preferred except for truly
performance-sensitive operations (e.g. animations) where the cursor
remains visible but is hidden conditionally.
Fill multiple closed polygons with the pattern of the current grafport.
Parameters:
(input is address of PolyList record)
Draw multiple closed polygons with the pen mode/size of the current grafport.
Parameters:
(input is address of PolyList record)
Is pen location of the current grafport within the polygon? A=$80 true, 0 false
Parameters:
(input is address of PolyList record)
Measure the width of a string in pixels
Parameters:
.addr data
.byte length
.word width (out) result in pixels
Draw string at the pen location of the current graphport (as left, baseline)
Parameters:
.addr data
.byte length
Configure usage of upper ($80-$FF) zero page by the API (speed vs. convenience)
If high bit is set (the default), part of the zero page is preserved across all MGTK calls. This is convenient for callers, at the expense of performance.
If the high bit is clear, MGTK assumes that the caller will not modify this part of the zero page.
Parameters:
.byte preserve $00=stash/no auto restore; $80=restore now and onward
When zero page preservation is turned on (the default), the active GrafPort is automatically updated after each call, and so changes made by calls (e.g. SetPenMode
) will be retained when the port is re-selected in the future.
If zero page preservation is turned off, the active GrafPort will not be updated by calls. In this mode, the application may force the active GrafPort to be updated by calling GetPort
.
Configure usage of lower ($00-$43) zero page by the API (speed vs. convenience)
If high bit is set (the default), part of the zero page is preserved across DrawText
calls. This is convenient for callers, at the expense of performance.
If the high bit is clear, MGTK assumes that the caller will not modify this part of the zero page.
Parameters:
.byte preserve $00=stash/no auto restore; $80=restore now and onward
Get toolkit version
Parameters:
.byte (out) major
.byte (out) minor
.byte (out) patch
.byte (out) status
.word (out) number
- To enter Mouse Keys mode, hold down both the Open-Apple key and the Solid-Apple (Option) key and then press the Space key. A confirmation sound will play.
- Move the mouse cursor using the arrow keys. Use the Solid-Apple (or Option) key as the mouse button.
- To exit Mouse Keys mode, press Escape. A confirmation sound will play.
.res 24 bitmap 2x12 byte bitmap (XOR'd after mask)
.res 24 mask 2x12 byte mask (OR'd with screen)
.byte hotx hotspot coords (pixels)
.byte hoty
.byte kind MGTK::EventKind::*
.res 4
if kind
is MGTK::EventKind::key_down
:
.byte kind MGTK::EventKind::*
.byte key (ASCII code; high bit clear)
.byte modifiers (0=none, 1=open-apple, 2=solid-apple, 3=both)
.res 2 reserved
if kind
is MGTK::EventKind::update:
.byte kind MGTK::EventKind::*
.byte window_id (0=desktop)
.res 3 reserved
otherwise:
.byte kind MGTK::EventKind::*
.word mousex
.word mousey
MGTK::EventKind::no_event = 0 ; No mouse or keypress
MGTK::EventKind::button_down = 1 ; Mouse button was depressed
MGTK::EventKind::button_up = 2 ; Mouse button was released
MGTK::EventKind::key_down = 3 ; Key was pressed
MGTK::EventKind::drag = 4 ; Mouse button still down
MGTK::EventKind::apple_key = 5 ; Mouse button was depressed, modifier key down
MGTK::EventKind::update = 6 ; Desktop/window update needed
event_modifier_open_apple = 1 << 0
event_modifier_solid_apple = 1 << 1
Menu Bar record:
.word count Number of menu bar items
.byte menu_id Menu identifier
.byte disabled Flag
.addr title Address of length-prefixed string
.addr menu Address of Menu record
.res 6 reserved Reserved
... repeats for each menu
Menu record:
.word count Number of items in menu
.res 5 reserved Reserved
.byte options bit 0=OA, 1=SA, 2=mark, 5=check, 6=filler, 7=disabled
.byte mark_char Custom mark character if mark option set
.byte char1 ASCII code of shortcut #1 (e.g. uppercase B); or 0
.byte char2 ASCII code of shortcut #2 (e.g. lowercase b, or same); or 0
.addr name Address of length-prefixed string
... repeats for each menu item
.byte id
.byte options MGTK::Option::*
.addr title
.byte hscroll MGTK::Scroll::option_*
.byte vscroll MGTK::Scroll::option_*
.byte hthumbmax
.byte hthumbpos
.byte vthumbmax
.byte vthumbpos
.byte status
.byte reserved
.word mincontwidth minimum content size (horizontal)
.word mincontlength minimum content size (vertical)
.word maxcontwidth maximum content size (horizontal)
.word maxcontlength maximum content size (vertical)
GrafPort windowport GrafPort record
.addr nextwinfo address of next lower window in stack
Windows have a content area which has the requested dimensions. Above this is an optional title bar which in turn has an optional close box. Within the content area are an optional resize box and optional scroll bars.
MGTK::Option::dialog_box = 1 << 0
MGTK::Option::go_away_box = 1 << 1
MGTK::Option::grow_box = 1 << 2
MGTK::Scroll::option_none = 0
MGTK::Scroll::option_present = 1 << 7
MGTK::Scroll::option_thumb = 1 << 6
MGTK::Scroll::option_active = 1 << 0
MGTK::Scroll::option_normal = option_present | option_thumb | option_active
Inits state, registers interrupt handler, draws desktop
Parameters:
.byte machine ROM FBB3 ($06 = IIe or later)
.byte subid ROM FBC0 ($EA = IIe, $E0 = IIe enh/IIgs, $00 = IIc/IIc+)
.byte op_sys 0=ProDOS, 1=Pascal
.byte slot_num: Mouse slot, 0 = search (will be filled in)
.byte use_interrupts 0=passive, 1=interrupt
.addr sysfontptr
.addr savearea buffer for saving screen data (e.g. behind menus)
.word savesize bytes
Deallocates interrupt, hides cursor
No parameters.
Parameters:
.byte hook_id 0=before, 1=after event checking
.addr routine_ptr 0=remove hook_id
Install pointer driver; A=0 on success, $95 if mouse disabled
Parameters:
.addr hook Mouse hook routine to install
.addr mouse_state (out) Address of mouse state (.word x, y; .byte status)
Set mouse/screen scaling
Parameters:
.byte x_exponent x-scale factor for mouse, 0...3
.byte y_exponent y-scale factor for mouse, 0...3
Next operation (DragWindow
or GrowWindow
) will be performed by keyboard
No parameters.
Get address of interrupt handler
Parameters:
.addr handler (out) Address of interrupt handler (after `CLD`)
This call is not present in the 1985 APDA documentation. All subsequent call numbers are offset by one.
Set cursor definition
Parameters:
(input is address of Cursor record)
Instead of an address, one of the MGTK::SystemCursor::*
enum values can be passed instead: pointer
, ibeam
, or watch
.
The SystemCursor enum support is a modern addition, so is not present in the 1985 APDA documentation.
Return cursor to visibility.
No parameters.
Cursor hidden until ShowCursor
call
No parameters.
Cursor hidden until moved.
No parameters.
Get cursor definition
Parameters:
.addr definition (out) Address of cursor record
Process mouse/kbd if GetEvent
will be delayed.
No parameters.
Return the next event from the queue.
Parameters:
(parameter is address of Event record)
DA specific:
- Call
JUMP_TABLE_YIELD_LOOP
to allow DeskTop to do periodic tasks.
Drop any pending events from the queue.
No parameters.
Observe the next event in the queue, without removing it.
EventKind::no_event
signals that there was no event in the queue.
Parameters:
(parameter is address of Event record)
Post event to queue.
Parameters:
(parameter is address of Event record)
If set, keypresses are ignored by Tool Kit
Parameters:
.byte handle_keys high bit set = ignore keyboard, otherwise check
Configure characters used for menu glyphs. Optional. The defaults are open=$1F, solid=$1E, check=$1D, control=$01.
Parameters:
.byte open_char char code to use for open apple glyph
.byte solid_char char code to use for solid apple glyph
.byte check_char char code to use for checkmark glyph
.byte control_char char code to use for control key glyph
The 1985 APDA documentation defines a
inactive_char
parameter, but this was removed.
Configure (and draw) menu
Parameters:
(input is address of Menu Bar record)
Enter modal loop for handling mouse-down on menu bar
Parameters:
.byte menu_id (out) Top level menu identifier, or 0 if none
.byte menu_item (out) Index (1-based) of item in menu, or 0 if none
Find menu item corresponding to keypress
Parameters:
.byte menu_id (out)
.byte menu_item (out)
.byte which_key
.byte key_mods bit 0=OA, bit 1=SA
Toggle highlight state of menu
Parameters:
.byte menu_id
Disable/enable a menu. Effectively disables all items, but individual disable/enable states are restored when re-enabled.
Parameters:
.byte menu_id
.byte disable 0=enable, 1=disable
Disable/enable a specific item in a menu.
Parameters:
.byte menu_id
.byte menu_item
.byte disable 0=enable, 1=disable
Sets a specific menu item as checked.
Parameters:
.byte menu_id
.byte menu_item
.byte check 0=unchecked, 1=checked
Sets a specific menu item as using a distinct mark character when checked.
Parameters:
.byte menu_id
.byte menu_item
.byte set_char 0=use checkmark, 1=use mark_char
.byte mark_char char code to use for mark
Parameters:
(input is address of WInfo record)
Parameters:
.byte window_id
No parameters.
Get pointer to window params by id; A=0 on success
Parameters:
.byte window_id
.addr window_ptr (out) winfo address
Populate GrafPort with current drawing state of window, clipped if the window is partially offscreen.
Parameters:
.byte window_id
.addr port address of GrafPort to populate
Returns Error::window_obscured
if the content area of the window is completely offscreen and drawing must be skipped. (The port rect will be invalid.)
Update port of window
Parameters:
.byte window_id
.addr port GrafPort to copy from
Respond to update event for desktop/window
Parameters:
.byte window_id 0 if desktop
Returns Error::window_obscured
if the content area of the window is completely offscreen and drawing should be skipped. (The port rect will be invalid.)
Update events with
window_id
of 0 are a modern addition, and not present in the 1985 APDA documentation. They are added to support redrawing onto the desktop itself, e.g. volume icons.
No parameters.
Parameters:
.word mousex screen coordinates
.word mousey
.byte which_area (out) MGTK::Area::*
.byte window_id (out) of window
Get id of top window
Parameters:
.byte window_id (out) window, or 0 if none
Make window topmost
Parameters:
.byte window_id
Parameters:
.byte clicked (out) 0 = canceled, 1 = close
Parameters:
.byte window_id
.word dragx mouse coords
.word dragy
.byte moved (out) high bit set if moved, clear if not
The 1986 APDA documentation specified that the
moved
parameter has values of 1 for yes, 0 for no. The implementation uses the high bit instead.
Parameters:
.byte window_id
.word mousex
.word mousey
.byte itgrew (out) high bit set if resized, clear if not
The 1986 APDA documentation specified that the
itgrew
parameter has values of 1 for yes, 0 for now. The implementation uses the high bit instead.
Map screen coords to content coords
Parameters:
.byte window_id
.word screenx
.word screeny
.word windowx (out)
.word windowy (out)
Map content coords to screen coords
Parameters:
.byte window_id
.word windowx
.word windowy
.word screenx (out)
.word screeny (out)
Parameters:
.word mousex
.word mousey
.byte which_ctl (out) MGTK::Ctl::*
.byte which_part (out) MGTK::Part::*
Parameters:
.byte which_ctl MGTK::Ctl::*_scroll_bar
.byte ctlmax maximum value
Parameters:
.byte which_ctl MGTK::Ctl::*_scroll_bar
.word mousex
.word mousey
.byte thumbpos (out) 0...255
.byte thumbmobed (out) 0 = no change, 1 = moved
Parameters:
.byte which_ctl MGTK::Ctl::*_scroll_bar
.byte thumbpos new position 0...250
Activate/deactivate scroll bar
Parameters:
.byte which_ctl MGTK::Ctl::*_scroll_bar
.byte activate 0=deactivate, 1=activate
Lower level (and screen-only?) than PaintBits.
Further documentation is needed.
Parameters:
(input is address of MapInfo record)
This call was not listed in the 1985 APDA documentation, so the behavior is inferred from the source.
Get address of desktop pattern.
Parameters:
.addr pattern (out) 8x8 pixel pattern
This call is a modern addition, so is not present in the 1985 APDA documentation.
Set new desktop pattern. Note that this does NOT redraw anything.
Applications can use RedrawDeskTop
to force a redraw.
Parameters:
.res 8 pattern 8x8 pixel pattern
This call is a modern addition, so is not present in the 1985 APDA documentation.
Redraws the current menu bar. Useful after full-screen operations.
Note that hilite state of menu bar items is not restored; this must
be done by manual calls to HiliteMenu
No parameters.
This call is a modern addition, so is not present in the 1985 APDA documentation.
Get the rectangle framing a window. This is in screen coordinates, and is the same rectangle that would be drawn for grow or move operations.
Parameters:
.byte window_id
Rect rect (out)
This call is a modern addition, so is not present in the 1985 APDA documentation.
Redraws the desktop background, and posts update events for the desktop and all windows.
No parameters.
This call is a modern addition, so is not present in the 1985 APDA documentation.
Like FindControl
, but works on all windows, not just the topmost window.
Parameters:
.word mousex
.word mousey
.byte which_ctl (out) MGTK::Ctl::*
.byte which_part (out) MGTK::Part::*
.byte window_id
This call is a modern addition, so is not present in the 1985 APDA documentation.
Like PaintBits
, but hides/restores the cursor for the operation.
Parameters:
(input is address of MapInfo record)
If the cursor is already hidden (via a call to ShowCursor
or
ObscureCursor
) then this is the same as PaintBits
, with no
performance penalty. If the cursor is not already hidden, then
this is equivalent in behavior but faster than surrounding
a PaintBits
call with ShowCursor
/HideCursor
because the
extra dispatch is avoided.
This call is a modern addition, so is not present in the 1985 APDA documentation.
Negates (XOR) the pixels of the menu bar. Useful for silent alerts. This should be called an even number of times before another MGTK call is made so the menu bar is left in a normal state.
No parameters.
This call is a modern addition, so is not present in the 1985 APDA documentation.
Save the passed screen rectangle to the save area.
Parameters:
Rect rect
This exposes the logic used to save the bits behind a menu, allowing for quickly restoring. This can be used to implement pop-ups, alerts, or other modal effects without requiring underlying windows to update.
Note that no error checking is done that the passed rect is valid or
that the save area passed to StartDeskTop
is large enough.
Restore the passed screen rectangle from the save area.
Parameters:
Rect rect
This must only be used following a SaveScreenRect
call and must be
passed the same rectangle dimensions. No error checking is done.
Expand the referenced rectangle by xdelta
and ydelta
, which can be negative.
Parameters:
.addr rect Address of MGTK::Rect
.word xdelta pixels
.word ydelta pixels
Expand the second rectangle to encompass the first rectangle.
Parameters:
.addr rect1 Address of MGTK::Rect. No modified
.addr rect2 Address of MGTK::Rect. Expanded if needed.
Multiplies two 16-bit values and then divides the 32-bit result by a third 16-bit value, yielding a 16-bit result and a 16-bit remainder.
Parameters:
.word number (in)
.word numerator (in)
.word denominator (in)
.word result (out)
.word remainder (out)
This operation is commonly used for scaling, for example setting a scroll bar thumb position based on the viewport offset or vice versa, or calculating a progress bar's position. By using a denominator or numerator of 1 this can be used for simple multiplication or division, respectively.
Note that if the result does not fit into 16 bits, the output is undefined.
Notes specific to DeskTop Desk Accessories (DA) are included where usage differs.
SetZP1
(optional)StartDeskTop
InitMenu
(if necessary; the defaults are sensible)SetMenu
- Run main loop until quit
StopDeskTop
- Call a
SystemTask
-equivalent function provided by the app to do periodic tasks. GetEvent
- If
MGTK::EventKind::button_down
orMGTK::EventKind::apple_key
:FindWindow
to figure out what was clicked- If
MGTK::Area::desktop
- ignore - If
MGTK::Area::menubar
- handle menu - If
MGTK::Area::dragbar
- handle window drag - If
MGTK::Area::grow_box
- handle window resize - If
MGTK::Area::close_box
- handle window close - If
MGTK::Area::content
:FindControl
- If
MGTK::Ctl::*_scroll_bar
- handle scrolling - If
MGTK::Ctl::dead_zone
- ignore - If
MGTK::Ctl::not_a_control
:- If not topmost:
SelectWindow
- Otherwise, handle content click per app
- If not topmost:
- If
MGTK::EventKind::key_down
- handle key - If
MGTK::EventKind::drag
:- TODO
- If
MGTK::EventKind::update
:- If
window_id
is 0, draw any desktop details into clipped port - Otherwise, draw contents of
window_id
into clipped port
- If
DA specific:
- Call
JUMP_TABLE_SYSTEM_TASK
to allow DeskTop to handle periodic tasks such as updating the clock.
GetWinPort
- populate a local GrafPort with an appropriately clipped port- if
Error::window_obscured
is returned, abort these steps (port will be invalid) SetPort
- make it current- optional:
HideCursor
- if multiple drawing calls will be made - ... draw ...
- optional:
ShowCursor
- if it was hidden above - optional:
SetWinPort
- save changed attributes (penpos, etc) if desired
MenuKey
- If
menu_id
is not 0:- Dispatch for
menu_id
andmenu_item
HiliteMenu
to toggle state back off when done
- Dispatch for
- Otherwise:
- handle key press per app
DA specific: Menus are not supported in DAs, so the first steps here can be skipped.
MenuSelect
to initiate menu modal loop- If
menu_id
is 0, done - Dispatch for
menu_id
andmenu_item
HiliteMenu
to toggle state back off when done
DA specific: Menus are not supported in DAs.
SelectWindow
to make topmost if necessaryDragWindow
to initiate drag modal loop- If
moved
is true:- Handle update events
- Redraw window content if not moved and was made topmost.
DA specific:
- Call
JUMP_TABLE_CLEAR_UPDATES
to allow DeskTop to handle update events. This will not redraw the DA window, however. - Redraw DA window content
TrackGoAway
to initiate modal close loop- If canceled - done
CloseWindow
DA specific:
- Call
JUMP_TABLE_CLEAR_UPDATES
to allow DeskTop to handle update events.
- If
MGTK::Part::thumb
:TrackThumb
to initiate modal scroll loop- If thumb did not move - done
- Redraw window content
UpdateThumb
- If
MGTK::Part::page_*
:- Scroll by a "page"
- Redraw window content
UpdateThumb
- If
MGTK::Part::*_arrow
:- Scroll by a "line"
- Redraw window content
UpdateThumb
GrowWindow
to initiate modal resize loop- If
itgrew
is true:UpdateThumb
if needed to adjust scroll bars- Handle update events
- Redraw window content
DA specific:
- Call
JUMP_TABLE_CLEAR_UPDATES
to allow DeskTop to handle update events. This will not redraw the DA window, however. - Redraw DA window content
- Repeat:
PeekEvent
- If not
MGTK::EventKind::update
- exit these steps - Otherwise:
GetEvent
BeginUpdate
- If error, continue
- Otherwise:
- If
window_id
is 0, redraw any desktop content (e.g. icons) - Otherwise, redraw
window_id
's content EndUpdate
- If
DA specific:
- Following a window move, resize or close, call
JUMP_TABLE_CLEAR_UPDATES
to allow DeskTop to handle update events. This will not redraw the DA window, however.