To simplify usage and optimize passing parameters to functions I have declared several structures
which describe the most common objects. Some of these structures are bound to static addresses in
the GEOS data space ($8000-$8fff
), so you can use their fields directly in an optimized way.
Please see gsym.h
to find them. All structures are defined in gstruct.h
and you may
find also some comments there.
A simple structure describing a point on the screen.
This structure describes a font in one pointsize. There is the current font - struct fontdesc
bound to curFontDesc
. You can also force GEOS to use your own fonts by calling
LoadCharSet
. You just need to open a VLIR font file and load one record - one pointsize -
somewhere. At the start of this area you already have all data for fontdesc
so you can
pass a pointer to the load address of that pointsize to LoadCharSet
. (Note that although
it has 'Load' in the name, that function loads only GEOS internal data structures, not data
from disk).
This widely used structure holds the description of a region of the screen. It describes the top-left and bottom-right corners of a window.
Maybe the name isn't the best - it has nothing with DoIcons
but with bitmap functions -
BitmapUp
for example. This structure holds the parameters needed to properly decode and show
a bitmap on the screen. The bitmap has to be encoded - if you have some non-GEOS bitmaps simply
convert them to Photo Scraps - this is the format used by all GEOS bitmap functions - DoIcons
too.
These structures describe click boxes (icons) that can be placed on screen or in a dialog box.
This is the definition of a single click box. Please see gstruct.h
for a description of its fields.
This is the toplevel description of icons to be placed and enabled on the screen. This structure has the following fields:
char number
- total number of icons declared herestruct pixel mousepos
- after finishing DoIcons
the mouse pointer will be placed in
this point allowing you to have a hint for the user what the default action isstruct icondef tab[]
- this table of size equal to icontab.number
contains
descriptions for all iconsThis simple structure holds the track and sector number of something. Do not expect the track to be in range 1-35, as GEOS can support many various and weird devices. For example my C128 256K expansion is utilized as RAMDisk with a layout of 4 tracks of 128 sectors each. However assuming that a track number equal to 0 is illegal might be wise.
This is a placeholder for a file datestamp. This structure is also present in struct filehandle
.
GEOS is not Y2K compliant, so if the current file has in filehandle.date.year
a value less than 86
you can safely assume that it is e.g. 2004 and not 1904.
This is the main file descriptor. It is either an entry in the directory (returned from file functions)
or its copy in dirEntryBuf
. This is optimized so you can safely get to the file's year e.g.
by testing dirEntryBuf.date.year
- it will be compiled to simple LDA, STA
.
This structure holds the fileheader description. You can load a file's header into the fileHeader
fixed area using GetFHdrInfo
. (note that fileHeader
is a place in memory while
fileheader
is a structure).
You will also need your own fileheader for SaveFile
.
This structure is defined only for system_date
. It is slightly different from f_date
so I prepared this one. You can e.g. get or set the current time using system_date.s_hour
and
system_date.s_minute
. Accesses to these will be optimized to simple LDA
and STA
pair.
You should declare a table of that type to prepare data for InitProcesses
. The maximum number
of processes is 20, and the last entry has to be equal to {NULL,NULL}
, so this table may hold
only 21 entries. The first member of this structure (pointer
) holds the pointer to the called
function (void returning void), you will probably have to cast that pointer into unsigned int
.
The second field jiffies
holds the amount of time between calls to that function.
On PAL systems there are 50 jiffies per second, while NTSC have 60 of them.
GEOSLib uses cc65 non-ANSI extensions to easily initialize data in memory. This is done with a kind of array of unspecified length and unspecified type. Here is how it works:
void example = {
(char)3, (unsigned)3, (char)0 };
Which will be compiled to following string of bytes:
_example:
.byte 3
.word 3
.byte 0
As you see this way it is possible to define data of any type in any order. You must remember to
cast each member to proper type.
DoMenu
is responsible for everything concerned with menu processing. Many, many GEOS programs
are just initializing the screen and menu and returning to MainLoop
. In GEOSLib it is the same as
returning from main
function without using exit(0)
.
A menu is described by two types of data - menu descriptors and menu items. A descriptor contains information about the following menu items, and items contain names of entries and either pointers to functions to execute or, in case of nested menus, pointers to submenu descriptors. Note that submenu descriptor can be top-level descriptor, there's no difference in structure, just in the content.
Here is how a single descriptor looks like:
void myMenu = {
(char)top, (char)bottom, // this is the size of the menubox
(unsigned)left, (unsigned)right, // counting all items in the current descriptor
(char)number_of_items | type_of_menu, // number of following items ORed with
// type of this menu, it can be either
// HORIZONTAL or VERTICAL if you will have also bit 6 set then menu won't be closed
// after moving mouse pointer outside the menubox. You can have at most 31 items.
This is followed by number_of_items
of following item description.
...
"menuitemname", (char)item_type, (unsigned)pointer,
"nextitemname", (char)item_type, (unsigned)pointer,
...
"lastitemname", (char)item_type, (unsigned)pointer };
// Note that there isn't ending <tt/NULL/ or something like that.
pointer
is a pointer to something, what it points for depends from item_type
. This one
can have following values:
MENU_ACTION
- a function pointed by pointer
will be called after clicking on the menu item
SUB_MENU
- pointer
points to next menu descriptor - a submenu
Both of them can be ORed with DYN_SUB_MENU
and then the pointer
points to a function
which will return in r0
the needed pointer (to function to execute or a submenu).
For creating nested menus (you can have at most 8 levels of submenus) you need to declare such a structure for each submenu and top level menu.
DoDlgBox
is together with DoMenu
one of the most powerful routines in GEOS. It is
responsible for creating dialog boxes, that is windows which task is to interact with the user.
The format of the command string is following:
(window size and position)
(commands and parameters)
NULL
There is a custom type defined for the command string: dlgBoxStr
.
The first element can be specified in two ways - by using the default size and position or specifying your own. The first case results in
const dlgBoxStr example = {
DB_DEFPOS (pattern_of_shadow),
... // commands
DB_END };
And the own size and position would be:
const dlgBoxStr example = {
DB_SETPOS (pattern, top, bottom, left, right)
... // commands
DB_END };
The next element of the DoDlgBox
command string are the commands themselves. The first six commands are
default icons and the number of the selected icon will be returned from window processor. The icons are
OK, CANCEL, YES, NO, OPEN
, and DISK
. You can use predefined macros for using them, e.g.:
...
DB_ICON(OK, DBI_X_0, DBI_Y_0),
...
Note that the position is counted from top left corner of window, not entire screen and that the 'x'
position is counted in cards (8-pixel) and not in pixels. This is also true for all following commands.
DBI_X_0
and DBI_Y_0
are predefined (see gdlgbox.h
for more), the default positions
which will cause icons to appear on a default window exactly where you would expect them.
DB_TXTSTR (x, y, text)
will cause to show the given text in the window.
DB_VARSTR (x, y, ptr)
works as above, but here you are passing a pointer to a zero page location
where the address of the text is stored. This is useful for information windows where only the text content
is variable. Consider following:
char text = "foo";
...
r15=(unsigned)text; // in code just before call to DoDlgBox
...
DB_VARSTR (TXT_LN_X, TXT_LN_1_Y, &r15),
...
will cause the word ``foo'' to appear in the window, but you may store the pointer to any text in
r15
(in this case) before the call to DoDlgBox.
DB_GETSTR(x, y, ptr, length)
- will add a input-from-keyboard feature. ptr
works as in the
previous example and points to the location where the text is to be stored. Note that the contents of this
location will be shown upon creating the window. length
is the maximum number of characters to input.
DB_SYSOPV(ptr)
- this sets otherPressVec
to the given pointer. It is called on every keypress.
DB_GRPHSTR(ptr)
- the data for this command is a pointer for GraphicsString
commands.
DB_GETFILES(x, y)
- for a standard window you should pass 4 for both x and y. This function
draws a file selection box and searches the current drive for files. Before the call to DoDlgBox
you
must load r7L
with the GEOS filetype of searched files and r10
with the class text. In r5
you have to load a pointer to a char[17]
where the selected filename will be copied. It works
like FindFTypes
but is limited to first 16 files.
DB_OPVEC(ptr)
- this sets a new pointer for the button press function, if you pass
RstrFrmDialogue
here you will cause the window to close after pressing mouse button.
DB_USRICON(x, y, ptr)
- places a single user icon (click box) on the window, ptr
points at a
struct icondef
but fields x
and y
are not used here. You can have at most 8 click
boxes in a window, this is an internal limit of the GEOS Kernal.
DB_USRROUT(ptr)
- this command causes to immediately call the user routine pointed by ptr
.
GraphicsString
is a very powerful routine to initialize the whole screen at once. There are
predefined macros for all commands, names are self-explanatory, see them in ggraph.h
. The last
command has to be GSTR_END
. There is a custom type defined for the command string: graphicStr
.
Here is an example for clearing the screen:
const graphicStr example = {
MOVEPENTO(0,0),
NEWPATTERN(0),
RECTANGLETO(319,199)
GSTR_END };
This type of data is used to initialize one or more bytes in different locations at once. The format is the following:
void example = {
(unsigned)address_to_store_values_at,
(char)number_of_bytes_that_follow,
(char)data,(char)data (...)
// more such definitions
(unsigned)NULL // address of 0 ends the table
};
It is possible to intercept events and hook into the GEOS Kernal using vectors. Here is a little example:
void_func oldVector;
void NewVectorHandler(void) {
// do something and at the end call the old vector routine
oldVector();
}
void hook_into_system(void) {
oldVector = mouseVector;
mouseVector = NewVectorHandler;
}
void remove_hook(void) {
mouseVector = oldVector;
}
In your main
function you should call hook_into_system()
but after all calls to the GEOS
Kernal (like DoMenu
, DoIcons
, etc.) - right before passing control to the MainLoop()
.
Be warned that vectors are most likely to be changed by the GEOS Kernal also via other functions (like
GotoFirstMenu
, DoDlgBox
and its derivatives etc.). It depends on what Kernal functions
you use and which vectors you altered. Unfortunately there is no exact list for GEOS 2.0, a complete
list for GEOS 1.x can be found in A. Boyce's Programmers' Reference Guide mentioned before. Most of the
information contained there should be still valid for GEOS 2.0. When calling a function that restores
the vector you should add a hook_into_system()
call right after it.
It is critical to restore old vector values before exiting the program. If you have more than one
place where you call exit()
then it might be worth to register remove_hook
function to
be called upon exiting with atexit(&remove_hook);
call. This way you will ensure that
such destructor will be always called.
That little example above intercepts mouseVector
. The NewVectorHandler
function will be
called every time the mouse button changes status. Other important vectors you should know about
are:
appMain
- this is called from within the MainLoop
system loopkeyVector
- called whenever a keypress occursintTopVector
- called at the start of the IRQ routineintBotVector
- called at the end of the IRQ routine