This document specifies the current API for
My
own OS, which is the same as the framework glue I use for cross-platform
support of MacOS and Win32. I considered Qt for a while, but this is smaller,
faster, and easier to maintain, which also makes it more portable -- for
example, it also runs on the (classic) MacOS -- and (unlike Qt) it is not
subject to the whim of whoever currently owns it. Actually it is, but I
have only myself to blame for it. My TAG
compiler is also built over this framework, so it fully compiles itself.
MOS and application programs built over it are written in Turkish Demitasse (T2, "A stronger brew than Java"), but it is similar enough to C++ and Java that the APIs here can mostly be read as if written in one of those more popular languages.
Separately compiled T2 programs are imported (linked) as "packages". This library API is segmented by package. When you
import SomePackage;all the (non-private) declared functions and data structures of SomePackage become available to your program. The package System has a great many functions, so they are grouped by function here. Other packages are generally imported only if you need those functions.
Dangerous -- For systems programming
only, avoid importing this!
System -- Most programs need to import
this
Utilities -- Some non-essential,
but sometimes useful functions
UnDragPkg -- Undo, drag, copy/paste
and scripting support
ImDlogPkg -- Widget-based
window support, such as dialogs
BigTxtPkg -- Large text
files
Sortex -- Text sorting
TextAry -- Some utilities for faster
text manipulation
Processes -- Multiple processes
System Events -- This is a listing of the predefined events
These packages are all contained in the single SysLibs.t2 source code file included with the system building tools. The functions are mostly described here below, but you can learn more about the system-programming capabilities of T2 by reading the code. One hack "C0DE" (spelled with a zero, not the letter Oh) is used frequently for low-level machine-code access, so that the compiler can produce well-optimized code without resorting to separate assembly language subroutines or files. There is a brief introduction to how to read (and maybe write your own code using) this function in "How to Read SysLibs.t2".
X1NT, X1NT X0NT(int x)
Sometimes it is necessary to export an unsafe function from a package,
so it can be used in other packages where the programmer knows its dangers.
Normally this requires importing Dangerous. To enforce
that requirement, you can substitute opaque type X1NT for
an integer parameter. Nobody can do anything with this type except convert
it to or from integer, and then only with function X0NT()
imported from Dangerous and built-in function ORD().
Unary &
The prefix & operator gets the machine address
of a variable or function. You have no business doing that unless you want
to poke around dangerously in memory.
int PEEK(int addr), void POKE(int addr, int valu)
Given a memory address addr, you can PEEK
at the data there or POKE a different valu
into it. This obviously can corrupt all sorts of things. Don't do it unless
you know what you are doing. Requiring these functions to be imported from
Dangerous
prevents their wanton use without giving notice that the code is (ahem)
Dangerous.
void C0DE(int val, ...); someType C0DE(:someType, int val, ...)
It's too hard to make a compiler generate all possible useful machine
code sequences. This is a way to get arbitrary code in sysmem environments
without involving a separate assembly language. It also permits type-casting
to arbitrary types, using the second form. Any parameter values given are
pushed onto the virtual (compiler) stack, and then zero or more special
integer codes preceded by a prefix # specify (68K now,
eventually x86) machine instructions and/or compiler pseudo-ops. The details
of these operations are presently only embedded in the Turk68 compiler.
Sorry about that. When T2 is compiled to C++, a final quoted string parameter
to this function gives the C++ text value to be generated for it; the string
parameter is ignored for 68K.
void RAW_CODE(String txt)
When T2 is compiled to C++, this function transfers the literal txt
to the output code file, as a way to accomplish essentially the same thing
that C0DE does when generating machine code; the Turk68
compiler ignores it.
Note also T2 predefined constant _Is_In_C_ is true when compiling to C++ and false otherwise; if/else statements that depend on compile-time constants like _Is_In_C_, when the constant is false, the C code is not generated at all. Turk68 similarly removes code controlled by false constants. In all cases the code must still be valid T2, so this is not as flexible as C macros.
RAW__C0DE "some C++ text"
This is not a statement or function, but more like a preprocessor macro
for placing C++ code in the output text file created by T2C. If the text
is the empty string, then the immediately following RAW__C0DE
specifies the name of a C++ namespace directive (or if
again empty, the end of the previous one). I use this mostly for inserting
the beginning or end of a block comment, which removes the enclosed generated
C++ code, so that the same source code files can be used for both 68K and
C++ outputs. There are other hacks for controlling the C++ output, which
I hope to document soon, so that you can get decent C++ code files with
nothing more than hacked-up T2 code.
int
boolean: true, false
char
Char4
String
float, double
Object, Throwable, this, super
null, void
int ORD(scalar x)
char CHR(int x)
Char4 CHR4(int x)
int ABS(int x), int MAX(int x), int MIN(int x)
int PackText(char a, char b, char c, char d), char TextChar(int x, int i)
_XCH_(someType x, someType y)
int LENGTH(someType x)
subClass DownCast(superType x,subClass)
Type Changing Functions
Data & Types
Widgets
Array Functions
String Functions
File I/O Functions
Debugging Functions
Widget & Window Functions (including text
and fonts)
Menu Management
Event Functions & User Interaction
Miscellaneous Functions
int
This is an ordinary 32-bit integer, like C++ or Java. We do not (yet)
contemplate 64-bit integers, because they tend to be slower than 32-bit
integers except for very large programs that need access to more than 2
billion bytes of data. Such programs are still very rare.
boolean: true, false
This is its own data type, not compatible with numbers. If you want
to convert a boolean to integer, use the ORD() function;
if you want to convert integer to boolean, use the ODD()
function, or else compare it to 0. All scalar data is 32 bits. Memory is
big enough to not justify wasting time and effort packing small data into
small spaces -- unless the programmer wants to do that.
char
For now, T2 only supports 7-bit ASCII characters,
but the other 128 8-bit characters can be used within comments and as data.
This is its own data type, not compatible with numbers. If you want to
convert a character to integer, use the ORD() function;
if you want to convert an 8-bit integer to character, use the CHR()
function.
Char4
This is a new data type in T2, inspired by the 4-byte character codes
of the MacOS. This is a scalar value that fits in a 32-bit word (and uses
integer hardware operators), but is not compatible with numbers. If you
want to convert a Char4 to integer, use the ORD()
function; if you want to convert an integer to Char4, use
the CHR4() function. There is automatic coercion between
Char4
and String (both ways). Compares are bitwise and case sensitive.
float, double
These types are not yet implemented.
String
This is a native data type designed to be easy and safe to use. The
T2 compiler automatically coerces all of the above data types to string
when needed in expressions, and there are functions for parsing out each
of the above types from string data. Unlike C, string compares are not
case sensitive. There are also numerous handy functions for working with
string chunks.
String references are stored in a single 32-bit word. Although there is no obvious distinction in the source code and they are completely compatible with each other, there are four underlying representations. Null strings are stored as zero. Most strings are dynamically allocated with a reference count, and the pointer to the opaque allocated block is stored. Literal (quoted) strings are encoded by the compiler as Mac-ish "P-strings" preceded by a 1-byte length code, and the (odd) pointer to the string is stored. However, small numbers are cheaper to work with in the original MOS and IBSM, so the compiler encodes the string literals as small positive integer indexes into a string table. All system routines that operate on string values know to convert the small integer index into a P-string, and know about the differences between P-strings and dynamic strings. In Win32, everything except empty strings is converted to dynamic strings. That's probably why C is so much slower than (emulated) 68K code on a computer with a clock 5x slower.
Object, Throwable, this, super
T2 classes may be declared as subclasses of Object
or Throwable or some other class, or they may be declared
without reference to a superclass, in which case they are not subclasses
of any other class (except in T2C, where everything is a subclass of Object).
Exceptions can only throw and catch objects of type
Throwable.
Within all class methods, this always points to the current
object, and within all methods of a subclass,
super points
to its superclass for purposes of accessing the overridden methods. I don't
think you can reach more than one level back. Objects can be allocated
dynamically by new, or statically declared using final.
References to final objects are fully compatible with dynamic
objects, except of course you cannot re-assign them.
You can make arrays of any data type above, and (unlike C++ or Java) the array type is an honest data type, not compatible with any other data type. Arrays can be dynamically allocated (like objects) by new someType[size] or statically where they are declared. References to arrays are pointers to the first element, as in C++ and Java, but (like Java and unlike C++) you cannot do arithmetic on array pointers. Each dynamic object and array is stored with its own reference count and size, and the language both automatically deletes the storage when it goes out of scope, and prevents you from exceeding its bounds.
null
All dynamic object and array variables are initially set to null where
they are declared (unless initialized otherwise), and can be re-assigned
to null or other (compatible) references from time to time. References
can be compared to other (compatible) references or to null. Arrays are
compatible if their element type is the same. Objects are compatible if
they are the same class or one is a subclass of the other, except that
you cannot directly assign to a variable declared as the subclass of the
type of reference being assigned to it; you must use DownCast()
to coerce it (see below).
void
Although you cannot have variables of type void, it
is a true data type and not compatible with any other data type. In particular,
statements are type void, and you cannot return a non-void
function value to a void statement, nor (obviously) a void
function result to an expression that wants some other type.
int ORD(scalar x)
This function casts boolean, char,
Char4
or X1NT data to integer. No code is generated, it just
lets the compiler know that you wanted a different type.
char CHR(int x)
This casts the low byte of x into a character. No error
is possible, the excess bits are simply discarded.
Char4 CHR4(int x)
This casts an integer into Char4. No code is generated,
it just lets the compiler know that you wanted the other type.
int ABS(int x), int MAX(int x, int y), int MIN(int x, int y)
This returns the obvious result.
int PackText(char a, char b, char c, char d), char TextChar(int x, int i)
This is a hardware-neutral way to pack and unpack text bytes. PackText builds an integer from the four characters in such a way that a is first and d is last in the normal byte ordering for the underlying hardware. TextChar conversely extracts the ith byte from the integer x, where i is an index from 0 to 3 in the normal byte ordering of the underlying hardware. Only the low two bits from i are used, any other bits are disregarded.
_XCH_(someType x, someType y)
All dynamic T2 objects and arrays are reference-counted, which takes a certain amount of time. If x and y are two (32-bit) variables of the same type, it is always safe to exchange them without concern for their current reference counts; this function does that very efficiently.
int LENGTH(someType x)
Objects, arrays, and strings have a length; this function returns it.
subClass DownCast(superType x,subClass)
It is not always safe to down-cast a value declared to be a supertype
into a variable declared to be its subtype. If the source value is not
the same or a subclass of the destination type, DownCast
returns null, which you can test. The approved way to do
this in Java seems to require introspection, or an exception if it can't
be done. I think this is faster and simpler.
The first of these is true if compiling to the MacOS, the second is true if compiling to Windows, and the third is true if the compiler is generating C++.
type IntArray = int[];
type ArrayArray = IntArray[];
type StringArray = String[];
These are convenience names for common array types.
type Process; type File; type Window;
type MenuBar;
type Point; final Point Nowhere = &0x80008000;
These are opaque types, that is, application programs are not allowed to look inside. Point is actually two signed 16-bit integers packed into 32 bits and representing a position on a visible screen or window, with casts to convert Point to and from integer, and a predefined constant representing no valid position.
final int HiBit = 0x80000000;
This is a convenient name for an integer constant I use a lot to represent something like an invalid value.
final Char4 Null4 = CHR4(0), NoName = " ";
These are convenient names for common Char4 constants.
final char TabKey = CHR(9), BSkey = CHR(8), DelKey = CHR(127);
final char EnterKey = CHR(10), EscKey = CHR(27), LfArKy = CHR(28);
final char RtArKy = CHR(29), UpArKy = CHR(30), DnArKy = CHR(31);
These are convenient names for some control characters.
final int CapsLockKey = 0x10000, ShiftKey = 0x20000, ControlKey = 0x40000;
These bits are defined for the "KeyC"
event, and represent the state of the specified modifier key when the
key was pressed. You can get the current state of these keys from the function
GetButtons,
but you should not depend on GetButtons to accurately represent
the modifier state at the event time, because other things may be happening
and delaying the delivery of events. Unlike Windows, there are no events
to notify the client program of key release or when the modifier keys are
actually pressed. This is not a gaming
API.
class RectAngle {int rTop, rLeft, rBotm, rRite;}~RectAngle
class Widgit extends RectAngle {
Char4 wKind, wName; int wFlags; // see: 'wFlags
bit masks'
Widgit wLink, wParent, wChild; // wLink is siblings,
in Z-order
Window wWindo; int FoBgColo; String theText;
int MoInfo; X1NT HostInfo;}~Widgit
The RectAngle is the (methodless) base class for a Widgit. AWidgit has not only a RectAngle (frame), but also what wKind of Widgit it is, a unique (for the window) wName, and some binary wFlags detailing various aspects of this Widgit. These are the bit values for the flags:
final int wiVisble = 1; // =0 if hiddenThe same bits apply to windows (with function access). Widgits can be linked into a tree structure, with children, siblings, and a parent. Every Widgit also points to its parent window (wParent in the root Widgit is null). Some Widgits have meaningful text, and most of them have meaningful foreground and background colors. Widgit color is defined using six levels of brightness in each of three primary colors (sometimes called HTML colors), for a total of 216 distinct colors, which fits in one byte. Some of these colors have been given predefined names, and you can do arithmetic to form others:
final int wiHilit = 2; // visibly active
final int wiHasFocus = 4; // takes keystrokes
final int wiActive = 8; // takes/reports clicks
final int wiClikIn = 64; // chkbx checked; cursor blink in text
final int wiRollOver = 128; // reports rollovers
final int wiClicker = 256; // reports clicks to owner
final int wi2clicks = 512; // reports 2-clicks
final int wiDragble = 1024; // dragable
final int wiDropble = 2048; // takes drops
final int wiOwnDraw = 0x1000; // owner draws it
final int wiDefalt = 0x2000; // default [btn] auto-close [win]
final int wiTxRight = 0x4000; // right-just text
final int wiTxLeft = 0x8000; // left-just text
final int wiTxCenter = 0xC000; // centered text; =0 if not Texty
final int wiAutoSizeM = 0xF0000; // auto-size bits: TBLR
final int wiAutoFill = 0x50000; // all 4 edges track their edge
// 00 no mods (stay with TL), 01 fill win, 11 stay with BR
final int wiUserBits = 0x3F00000; // specific to the wigit
final int wiUsrBitShft = 20; // for positioning wiUserBits
final int wiEscape = 0x8000000; // default [btn] response to esc key
final int minRed = 1, minGreen = 6, minBlue = 36, minGray = 43;An example of a specific Widgit is the scrollbar:
final int MaxColor = 5, black = 0, white = 215, winGray = minGray*4;
final int purple = minBlue*3+minRed*3, green = minGreen*3;
final int dkRed = minRed*4+minBlue, red = minRed*5;
final int ltBlue = minRed*4+minGreen*4+minBlue*5, blue = minBlue*5;
final int brown = minGreen+minRed*3, amber = red+minGreen*4+minBlue;
class Dial extends Widgit { // pcnt = # scroll units in visible pageThe wKind of a scrollbar can be either "ScrV" or "ScrH". Additional fields tell the display (and application program) what the current value is, its minimum and maximum values, and what percent of the range is "visible" -- if that makes sense; for example, if the scrollbar represents lines of text on a window, the base would be 0 (top line showing), the limit would be the maximum number of lines that can be scrolled off the top, and pcnt would be how many lines are visible at any one time. The two opaque fields are used in drawing. Another example is the text label:
int value, base, limit, pcnt; X1NT prior, thumb;}~Dial
class Texty extends Widgit {int TxFace;}~Textywhere wKind is "Labl". Other common Widgits include "Butn" button and "ChBx" checkbox, with their obvious functions. However, these Widgits are being deprecated for the somewhat more robust dialog window.
void IgnoreInt(int xx);
void IgnoreBool(boolean xx);
void IgnoreStr(String str);
void IgnoreWin(Window theWin);
void IgnorePtr(IntArray thePtr);
These functions convert their respective parameter types to void by discarding the value (typically a function result). Note that C and Java do this implicitly.
boolean Is4chars(int valu);
int ValidAsC4(int num);
The Char4 type does not forbid any combination of control characters and what-not, but the type is more useful if restricted to printable ASCII characters. The function Is4chars() tests each of the four characters and returns true if they all conform. The function ValidAsC4 corrects (to space) any non-ASCII, non-printing characters. Both functions are compiled inline (for the Mac) and do all four characters in parallel for speed.
int Point2int(Point pt);
Point Point4int(int pt);
These are codeless typecasts.
Point MakePoint(int V, int H);
int PointV(Point pt);
int PointH(Point pt);
These functions construct a Point from two short integers, or else extract one or the other (signed) values from a Point. See also Pack2ints()/HiWord()/LoWord()/LoHalf() in Miscellaneous Functions below for the same operations on ints.
String Num2Str(int theValu);
T2 does not let you directly compare a number to a string (do you want
the number converted to string? Or the string converted to a number?) so
you can use this function to explicitly convert an integer to a string
for such compares. Use ParseInt (below) to convert the
other way.
String IffyStr(boolean whom, String tru, String fls);
String FormDateTime(int timDt, int pts);
Given the number of seconds since midnight January 1, 2000, FormDateTime can format it as a short (3-letter month) date, the time, or both, depending on whether pts is -1, +1, or zero. There are also several functions (see Debugging Functions) for preparing debugging text where the converted data is often surrounded by text separators and/or labels, which can also be used in isolation. They generate smaller code because the type conversion is in the function instead of inline everywhere.
char CharAt(int offs, String str);
boolean ParseBool(String str);
int ParseInt(String str);
Char4 Chr4Str(String str);
float ParseFloat(String str);
These functions convert strings to the specified type. The objective is to allow as much format lattitude as possible, so most of these conversions keep going as long as the data makes sense, then return what it found. If the source string is shorter than 4 characters, Chr4Str fills out with spaces. If there are no digits at the front of the source string, ParseInt returns zero. ParseBool accepts '1' and 'T' and 't' as true, and everything else (including the empty string) as false. CharAt checks for offs within the string length, and returns the null character if not.
Dial Dial4wgt(Widgit wgt);
Texty Texty4wgt(Widgit wgt);
Because DownCast() does not work on methodless classes, these functions give a type-safe way to access the extra fields in these subclasses.
boolean ODD(int valu);
This efficiently tests the low bit.
IntArray ResizeAry(IntArray theAry, int sz);
Unlike the MacOS, where "handle" data can be moved and resized without
altering the "master pointer," C and Java and T2 all allocate a fixed position
in memory. Maybe this can be grown, but probably not, so this function
recopies the data shared by the old and new sizes, and zeroes any added
elements. If the array shrinks (but not too much) the array bounds may
be reset on the same allocation. See also PartOfArray
in Utilities.
int Offset(String seek, String instr);
String Substring(int offs, int len, String str);
String Replace(String nu, int offs, int len, String str);
String ReplacAll(String nu, String olds, String str);
These are essentially the same as their Java counterparts, where the offset offs is zero-based. Replace replaces len characters of the source string with nu, whereas ReplacAll is a search-and-replace that replaces every occurrance of olds with nu. If new instances of olds are formed by or included in the substitution, they are not replaced unless you make a second call on the function.
int nLines(String str);
int nWords(String str);
int nItems(char sep, String str);
String LineOf(int ordn, String str);
String WordOf(int ordn, String str);
String ItemOf(char sep, int ordn, String str);
String PutLineOf(String nu, int orn, String str);
String PutWordOf(String nu, int ordn, String str);
String PutItemOf(String nu, char sep, int orn, String str);
String InsertB4Line(String nu, int orn, String str);
String InsertB4Word(String nu, int orn, String str);
String InsertB4Item(String nu, char sep, int orn, String str);
String DeleteLineOf(int orn, String str);
String DeleteWordOf(int orn, String str);
String DeleteItemOf(char sep, int orn, String str);
int n0Lines(String str);
int n0Items(char sep, String str);
The T2 library has a rich set of string operations. I borrowed from HyperCard the notion of chunks, which can be lines or words or items separated by some kind of delimiter (comma in HC). The chunks are numbered from one, not zero. In the case of words, excess whitespace (spaces, tabs, newlines) have no effect on the word count; any non-white characters count as the first word. Otherwise, when counting chunks, an empty string has zero chunks, but a single delimiter at the end is not counted. If you care about it, there's an alternate form (with '0' in its name) that counts the trailing delimiter, if any.
int Len2LastLine(String str, int len);
int Offs2ItemOf(char sep, int itm, String str);
String AllItemsFrom(char sep, int itm, String str);
String LastItem(String str, char sep);
String ShortFiName(String str);
These are some additional chunk-like functions I found useful. When
doing operations on files greater than what can be conveniently held in
memory, Len2LastLine measures all but the last line (which
might be partial, if reading a fixed block size). Similarly, Offs2ItemOf
measures the amount of text before a particular item, which lets you pick
out multiple items in a single chunk. AllItemsFrom is a
multiple chunk extractor, but extracts everything after (including) the
specified item. Substring(0,Offs2ItemOf(sep,itm,str),str) #AllItemsFrom(sep,itm,str)
would
return (most likely a copy of) the original string. You can pick out only
the last item of a string using LastItem, which is equivalent
to ItemOf(sep,nItems(sep,str),str); but slightly more convenient.
You can use either of these two functions with lines by choosing '\n'
as
the separator. ShortFiName is the same as LastItem,
where the separator is the host file system file path delimiter, ':'
for
MacOS, '\' for Windows, or '/' for unix.
Strings can be packed into a binary data structure with integers using these functions (see also package TextAry). This is useful for storing data into resources (see FileI/O Functions) or other binary blocks of data. Accessing the data as integers avoids the overhead of string allocation, and if you use a static array, also the overhead of reference counting. Given an existing array, Str2TxtBlok inserts (part of) a string into it, replacing whatever is already there, beginning at character offset bg for ln bytes. If ln is zero, the string length is used, up to (but not past) the end of the array. TxtBlok2Str reverses the process, extracting a string of the specified length (or less, if it reaches a null); a zero ln gets whatever is there up to a null or the end of the array. Array2Str is equivalent to TxtBlok2Str(0,0,ary); it gets the whole (first) string there. FindStTxBlk searches the array for the first occurrance of the specified string after offset bg, but not beyond bg+ln and returns the offset or -1 if not found. Unlike Offset, you can search for additional hits after the first one found.
void InitPstr(IntArray ary);
void AppChar(IntArray ary, char theCh);
void AppCh4(IntArray ary, Char4 theCh);
void AppHex(IntArray ary, int nm, int nd);
void AppNum(IntArray ary, int nm);
void AppPstr(IntArray ary, String st);
boolean IsntPstr(String st);
int GetPstrLn(IntArray ary);
void TrimPstr(IntArray ary, int tab2);
String GetPstr(IntArray ary, boolean same);
Much more useful when dealing with small strings in inner loops, are
these utilities for manipulating Mac-like "P-strings" stored in a user-supplied
array. If the array is statically allocated, these are much faster than
dynamically allocated strings. However, the programmer is responsible for
making sure the strings fit into the given array (maximum P-string length
is 255 characters, but it is further limited by the array size, if less
than 64). If you use
GetPstr with same=true the
"string" returned is actually a pointer into the array (in the Mac only),
so you cannot let it go out of scope or get reused until you are done with
the string. If
same=false the returned string is a dynamically
allocated copy with no such restrictions; if it is constructed from multiple
small parts, it's still much faster than vanilla strings.
InitPstr
sets the length byte to zero; usually I just do
ary[0]=0; which
does the same thing faster. Each of the five append functions appends one
item of the specified type (if it fits); you get to specify how many hexadecimal
digits to append.
AppPstr was originally specified to accept
only other P-strings (use IsntPstr to determine that) and
string literals (which are also encoded as P-strings), but it has been
extended to accept any string now, although P-strings are still much faster.
Different hardware stores integers differently with respect to strings,
so the length byte might be at different ends of ary[0], but GetPstrLn
always returns the correct length byte. TrimPstr cuts the
length as specified, or else inserts spaces to extend it.
File NewFileDlog(String msg, File dir, String fnm);
File FileDialog(String msg, File dir, Char4 fty);
File OpenFileRef(File ref, int opt);
File FileOpen(String name, int opt);
File CloneFileRef(File fi, Process frm);
File Close2Ref(File fi);
void FileClose(File fi);
void FileDelete(File fi);
To open a file, you must specify opt as a combination of these bits:
final int ReadWrite = 0, ReadOnly = 1, WriteOnly = 2,Zero is the default value, if none is specified in that bit position. If you open a Directory, then reading from it (you cannot write to it) reads all the file names in that directory. Resource files (ResFileTy) are a random-access binary file type similar to MacOS resource forks, a sort of mini file system in one host file, which lets you save and retrieve "resource" data, typically strings or arrays of integers, using functions optimized for that purpose.
CreateFi = 0, ExistingFi = 4,
TextFile = 0, BinaryFile = 8,
Directory = 16, ResFileTy = BinaryFile+Directory,
DirecOnly = ResFileTy, Executable = 32,
MacResFi = Executable+ResFileTy,
MakeFiRef = 64, SharedFile = 128,
SharedResFile = ResFileTy+SharedFile;
If any of the File-returning functions fails, it returns null. If you have several programs running and interacting, they can send File references to each other, but (in MOS) a program can only open its own files unless they are SharedFile (usually SharedResFile) Therefore you need to use CloneFileRef to create a file reference owned by the current process as a copy of the given reference owned by the specified process frm. File references can be extracted from events and their replies by EventFile and FileFrom. Note also that the file must be closed to a reference for FileDelete to delete it from the physical file system.
int GetFileErr();
int GetFileFlags(File fi);
int GetFilePosn(File fi);
int GetSizeFile(File fi);
boolean FileAtEOF(File fi);
String FullPath(File fi);
String GetFileName(File fi);
String ProgFileName();
int GetFiRefNum(File fi);
File FileFromID(int fi);
void GetFileInfo(File fi, IntArray ary);
void SetFileInfo(File fi, IntArray ary);
These functions return information about the file, or in the case of SetFileInfo, change it. GetFileErr returns 0 if the most recent file operation completed without error. Otherwise it will be one of these codes (all negative):
final int FileErr = -256, FileEOF = FileErr+1,GetFileFlags returns the bits used to open the file, or if it's not a valid file, zero. A TextFile open as ReadWrite has another (higher) bit added to make the result non-zero. Do not depend on unspecified bits, always mask off what you wish to test. FileAtEOF(fi) returns true exactly when GetFilePosn(fi)=GetSizeFile(fi). GetFileName returns just the short file name; it's equivalent to ShortFiName(FullPath(fi)). ProgFileName returns the full path name of the running program. I use it to extract the partial path of the directory in which the program file is, as a default location for support files, but that probably wouldn't work in a unixy file system with all kinds of restrictions.
NoSuchFile = FileErr+2, FileNotOpen = FileErr+3,
CantAccess = FileErr+4, OpenOther = FileErr+5,
CantWrite = FileErr+6, TooManyFiles = FileErr+7,
NoSuchRes = FileErr+8;
int ReadByte(File fi);
int ReadData(File fi, IntArray ary);
String ReadText(File fi, int size);
String ReadLine(File fi);
int WriteData(File fi, IntArray ary);
void WriteBytes(File fi, IntArray ary, int bgn, int ln);
void WriteText(File fi, String str);
void WriteChar(File fi, char ch);
void WriteChr4(File fi, Char4 ch);
void WriteLn(File fi);
These functions read and write data of various types. ReadData reads up to the size of the given array, and ReadText reads up to the specified number of (file) characters; either will be less if the file ends early, or if the file is opened as TextFile, if there are return-newline character pairs which are replaced internally by newline alone. Regardless of the host or file encoding of text line ends, all such delimiters are converted to newlines for internal processing, then converted back to the host encoding when written to a TextFile.
void Seek(File fi, int offs);
void SetEOF(File fi);
Files opened as ReadWrite or ReadOnly can (if the host permits it) be restarted at an arbitrary byte offset. Use GetFilePosn to determine where you wish to come back to, or SetEOF to shorten the file after it has been written beyond that point. Files normally written sequentially automatically set the end of file after the last byte written.
void InitResFile(File fi);
void NewResource(File fi, Char4 ty, int id, String nm, IntArray
ary);
void ReplaceRes(File fi, Char4 ty, int id, String nm, IntArray
ary);
void NewStrRes(File fi, Char4 ty, int id, String nm, String
st);
void DeleteResource(File fi, Char4 ty, int id, String nm);
Once a file has been opened as ResFileTy, you can use InitResFile to clear out any old resources. Consistent with the MacOS, resources have a Char4 type and a short ingeter reference number and an optional (but not yet implemented) text name. A resource of the same type and reference number as an existing resource replaces it in the file, so ReplaceRes is just another name for NewResource. Resources are limited at this time to a maximum size of 1024 integers or 4096 characters.
int CountResTypes(File fi);
Char4 GetIxResType(File fi, int ix);
int NumResources(File fi, Char4 ty);
int GetIxResource(File fi, Char4 ty, int ix, IntArray ary);
int GetIxResID(File fi, Char4 ty, int ix);
String GetStrRes(File fi, Char4 ty, int id);
IntArray GetResource(File theFi, Char4 rty, int rno);
IntArray SeeResource(File fi, Char4 ty, int id);
int RawGetRes(File fi, Char4 ty, int id, String nm, IntArray
ary);
To inspect a resource file, you can CountResTypes to learn how many types there are, then GetIxResType to look at each type in sequence. For each type, you can ask for NumResources of that type, then GetIxResource to get the data, or GetIxResID to learn only its reference number. Normally you already know what's there, so you would use GetStrRes or GetResource to get the data directly. These functions cache the data in memory to minimize file access time for repeated access to resources near each other. If you know you only need to access it once, you can use SeeResource, which bypasses the cache. For lower-level access, RawGetRes and GetIxResource tell you how big the resource is (pass it ary=null), so you can allocate an array of an appropriate size for a second access call.
void SaveRegistryItem(String da, String nm, String itm);
String GetRegistryItem(String nm, String itm);
Like Windows, the Registry is the approved place to store program-specific
configuration data. In the MacOS it is implemented as MacOS resources in
a file in the "Preferences" folder. In Windows it is stored in the system
Registry. Only text strings may be stored, but you can use EncodeIntArray
from package TextAry to convert arrays
of integers to text.
void SystemDebugLog(String str);
boolean SysDebugLogging();
The basic cross-platform debugging tool supported by the MOS framework is a system log file, to which you can write various diagnostics. The framework itself also logs what is going on at various debugging levels, so your information can be seen in context. In a production program no such file is open, and calls to SystemDebugLog harmlessly do nothing; however, formatting the text for display takes time, and you can test SysDebugLogging() which returns true if the file is active, so to omit the formatting if not.
String Int2Log(String before, int whom, String after);
String Dec2Log(String before, int whom, String after);
String Str2Log(String before, String theStr, int theLen, String
after);
String Hex2Log(String before, int whom, int theLen, String after);
String Chr2Log(String before, char whom, String after);
String Boo2Log(String before, boolean whom, String after);
String FormWinID(String before, Window theWn, String after);
String Tim2Log(String before, int whom, String after);
Each of the common data types has a formatting function here. The converted string is sandwiched between the before and after parameters, which can be nested with multiple calls to make a continuous string like this:
SystemDebugLog(Int2Log("Info: ",aNum,Boo2Log(", ",aCond,Str2Log(" '",aStr,35,"'"))));This is approximately the same as
SystemDebugLog(("Info: "#aNum#", "#aCond#" '"#aStr#"'");except that Int2Log automatically formats large numbers as hexadecimal (unless they make sense as a pair of small integers or a Char4 or certain colors), and Str2Log converts newlines and tab characters to something printable ('\' and '`'), and cuts string values down to the specified maximum (0 does not cut it). Chr2Log reports most characters outside the normal ASCII range as numbers. Tim2Log formats the current time according to the parameter used by FormDateTime; alternatively, it will accept a time in whom and formate it as date and time, or if you add 0x80000000 to it, only the time.
String Safe2Log(String theStr);
The Mac version uses Array Text Functions for the intermediate and result strings; if you are not using the result immediately by concatenating it into a larger string by strictly left- or right-nesting as in the example above, you probably should isolate it with a wrapper call to Safe2Log (uneeded in T2C at this time, unless I get around to implementing P-strings in C).
You can use the xx2Log functions to build up text strings for other purposes than logging, but if there's a chance that another of these functions will be used before a previous result ceases to be useful, you should always wrap that previous value in a Safe2Log call. The main reason for the xx2Log functions is to eliminate the unsightly long runs of nested "_cat_(" calls in the C code, and (in the Mac) to reduce the system calls through (A4) which cannot be single-stepped over in the debugger. If you don't need these benefits, long strings of concatenation operators # is slightly faster and much safer.
String PosTime(String prefx);
String DateLog();
String ArrayDumpLine(IntArray ary, int nw, int pos);
String LogCameFrom(String prefix, int deep);
In addition to the type-changing functions mentioned above, you can also use these functions to build debugging strings of useful data. Most of these let you specify a text prefix, so that concatenating the items is simple and takes minimal code in the application program. LogStr converts newlines and tab characters to something printable ('\' and '`'), and limits the length to theLen, do that the line in the log file is not excessively long. ArrayDumpLine returns as text part or all of an integer array, where nw is the number of words you want (but limited to the array size), and pos is the length of the line in front of where this will display, so to fold the line before it gets too long. If pos is negative, it stops when a couple of zeros are encountered.
LogCameFrom returns a string representation of the line that called this function, where the low four bits of deep specifiy how far back to look, zero meaning the function that called LogCameFrom, bit 4 (=16) specifies that you want a source line number (if it can be found in the Mac object code; always given in the PC), and if negative, it adds a timestamp. Because C does not specify a way to get traceback information, the T2C compiler surrounds each function call with coming_from("name")...retnd_from() calls to record this information, but only if you called the BoundsNullError() function which otherwise does nothing in T2C.
void GoodDump();
This dumps out everything that is known by the framework concerning the running program. It responds to a magical menu command "GSD!" and puts this directly into the debug log without troubling the application program, if selected.
int CompilerVersion();
int StakDepth();
In case you care, CompilerVersion() returns the compiler version as a hexadecimal number yyyymmdd, the date the compiler was compiled. While I'm making changes to the compiler, this can help identify down-rev bugs. Not available in T2C, StakDepth is the maximum depth of the function stack so far, which can help in determining sufficient stack space to allocate when starting up a sublaunch (see NewProgram in package Processes). You probably need at least 1000 words more than the highest this ever gets. On the PC, StakDepth only counts functions that called BoundsNullError.
void DEBUGGER();
void Stop4Debug(Char4 msg);
void HeapCheck(int why);
These invoke low-level debug facilities in the host (MacOS) system.
String Stx(String theStr);
This is a work-around for a compiler bug. Some MacOS framework String
functions mess up the reference count in a variable declaration, but not
if enclosed in a function call like Stx.
0 Mono -- Derived from MacOS Monaco 9, but altered to distinguish all charactersThe user can extend this list by subclassing IconFontFetch.
1 Text -- Derived from MacOS Times 12, but with whitespace between characters
2 Ital -- Derived from somebody's Times 12 Italic, not the usual Mac algorithmic slant
3 Tiny -- My own hand-crafted 6-pt font
4 Bold -- Derived from MacOS Helvetica 10 Bold
5 Head -- Derived from MacOS Helvetica 18 Bold
6 Grek -- Greek, derived from MacOS Symbol 10
7 Nano -- My own hand-crafted 4-pt font, almost readable
Back when the Mac first came out there was some debate about copyright on pixel fonts, and as I recall, the pixels themselves on the screen are not subject to copyright, but the algorthims for shaping them (first Adobe, then for obvious reasons, Apple+Microsoft TrueType) could be protected. Like the original Mac, these fonts are just pixels.
int FontStrWidth(String str, Char4 fnt);
int StringWidth(String str);
int IxStrWidth(String str, int fo);
int FontChrWidth(char aCh, Char4 fnt);
void GetCharWidths(Char4 fnt, IntArray tos);
Given the name or index number of a font, this calculates the width in pixels of a string or character. These framework calls use some significant overhead, so it is also possible to download the character width table for a given font. A string width is just the sum of the character widths, unless there are control characters included. We don't do kerning yet.
RectAngle NewRect(int tp, int lf, int bt, int rt);
void StuffRect(RectAngle rc, int tp, int lf, int bt, int rt);
void OffsetRect(RectAngle rc, int ver, int hor);
boolean InsetRect(RectAngle rc, int ver, int hor);
boolean EmptyRect(RectAngle rc);
boolean SectRect(RectAngle a, RectAngle b, RectAngle c);
boolean PtInRect(Point pt, RectAngle rcx);
These operations on RectAngle are essentially the same
as the MacOS operations similarly named.
The public ways to create a new window are exported from their respective packages (below). Given that windows exist, these calls access them. Each window has a (hopefully unique) 4-character "name" which identifies it in various logging operations, and which can be used to find it. You can also find a window from its title, if it has one. If you pass null to NextWindow, it will return the first in the window list (which I think is the most recent created); it returns null if the given window is the last of the list.
void CloseWindow(Window theWn);
void HideWindow(Window theWn);
void ShowWindow(Window theWn);
void Win2Behind(Window theWn, Window whom);
Widgit GetMyWidgits(Window theWn);
CloseWindow removes the window from existence, while
HideWindow
merely makes it invisible. ShowWindow always brings it
to the front, while Win2Behind moves it to behind the second
parameter window; neither of these affect its position in the window list,
only the order on screen. GetMyWidgits returns the root
Widgit
of this window, from which you can use the link and child pointers to access
additional Widgits.
Char4 GetWindID(Window theWn);
void SetWindID(Window theWn, Char4 val);
void GetWindRect(Window theWn, RectAngle rc);
void SetWinTitle(Window theWn, String stz);
String GetWinTitle(Window theWn);
void MoveWind(Window theWn, Point loc) ;
Point GetWinLoc(Window theWn);
void SizeWind(Window theWn, Point loc);
Point GetWinSize(Window theWn);
void SetDocScroll(Window theWn, int loc);
int GetDocScroll(Window theWn);
void SetWinColor(Window theWn, int val);
int GetWinColor(Window theWn);
void SetWinDatum(Window theWn, int val);
int GetWinDatum(Window theWn);
void SetWindFlags(Window theWn, int val);
int GetWindFlags(Window theWn);
void SetWinFlag(Window theWn, int val, int flg);
int GetWinFlag(Window theWn, int flg);
You can get or set each of these window properties. If you pass null to GetWinSize, it returns the size of the main screen; otherwise null is ignored (or returns zero or null). If the window itself (possibly in its top Widgit) has scrollbars, you can get or set the scroll. The window color refers to the background and text colors, if that makes sense. The "datum" can be used for anything you like. You can get or set all the flags at once, or else a single numbered bit.
void InvalRect(Window theWn, RectAngle rc);
void ValdRect(Window theWn, RectAngle rc);
void DrawWindNow(int why, Window theWn);
void SetCursor(int whom, Window theWn, RectAngle rc);
InvalRect forces all or part of a window to be redrawn, and ValdRect cancels that. On the Mac update events don't happen while dragging or the mouse is otherwise held down, so if you want the window redrawn in those circumstances, you need to call DrawWindNow to do it. Bad Things Happen in the managed windows if this is interrupted and re-enters in a different process, so MOS detects this situation and sends a "DraW" event to the main process, which should call DrawWindNow when it arrives. If you are using AutoEvent, it will do this for you.
Each window has a default cursor to show when the cursor is within the specified rectangle (or the whole window, if null), which SetCursor sets; if theWn is null, then the cursor applies generally, except over an active window with its own specified cursor. These cursors are defined:
-1 Spinning beachballThe beachball cursor must be continually refreshed to spin; otherwise it reverts to the default for the window or screen.
0 Default pointer
1 I-beam text cursor
4 Wait cursor (hour-glass)
Originally I imagined operations on a MenuBar, but it turns out we really want the program to set up its menubar and just leave it there forever. So all the other calls operate on the current (unnamed) menubar. The specification string is a list of menu items with a blank line between each major menu. The first line in each major menu is the menu title (shown in the menubar). Each item has a Char4 command that is sent to the program (see events) when that menu is selected, and an optional shortcut key code (distinguished by its prefix '^' or '`' if disabled). The first four characters of the menu name is its command, unless you give an explicit command separated by a tab from the rest of the line. A single hyphen '-' on a line is an inactive separator line. The first four characters of the menu title should be unique, and identifies it for the following operations:
void KillMenu(Char4 mID);
void AddMenu(Char4 mID, Char4 aft, String stz);
void SetMenuItem(Char4 mID, Char4 msg, String stz, char cky,
int pos);
void DeleMenuItemsFrom(Char4 mID, int pos);
A whole major menu can be removed from the menubar, or a new one inserted after (to the right of) the specified one. You can also alter (or insert at the end) individual menu items, given the major menu title code, and the line number (zero is the title itself). A single item can be deleted by giving it an empty item name, or all the menu items below a certain point can be deleted at once.
void MenuDisable(Char4 mID, int pos, boolean dsb);
void CheckMenu(Char4 mID, int pos, boolean ckd);
Without otherwise altering a menu item, it can be disabled or enabled, and a checkmark added or removed.
int PopupMenu(Point pt, String stz, int pre);
Generally in response to a mouse click (usually a context menu event),
the program can choose to pop up a menu under the mouse and get back a
0-base number representing which line was selected, or -1 if none.
The point is in screen coordinates where the menu is to pop up, which should
be near the mouse location at the time of the click, and you can specify
which line comes up there under the mouse, usually the default selection
(if that makes sense) or the previous value selected (if this pops up from
a selection item).
final class Event {void GetEvent(Event theEv);
Char4 EventNo;
int EvInfo; // file reference or click time or other info
int EvLocn; // mouse loc or other value
int EvDatum; // relevant window ID or other data
Process EvSend;}~Event
The main program is expected to loop more or less continuously through a call to GetEvent, which returns only when something happens. When there are multiple processes, you can use SendEvent to send an event to another process; the sending process waits for the event to be accepted and possibly changes made to the Event object sent. Similarly, you can SendMsg to that program with specified values for the individual fields of the Event, and it returns the result from EvInfo. Obviously you cannot SendEvent nor SendMsg to yourself (so SendMsg must be imported from the Processes package). AsyncMsg posts the event to the other process then returns immediately without waiting for it to be accepted. Use MySelf() to send a message to yourself, which is queued and then delivered in sequence to GetEvent. If you use null for the toProc parameter, MOS will try to determine an appropriate destination from whichever process is willing to accept it (see ProcEvent) or whichever process owns the Window whose ID is in EvDat (if so, see GetWindID), or else the main process if none other.
void DelayedEvt(Char4 EvNo, int eInfo, int EvLoc, int EvDat, int msec);
It is also useful to sent yourself messages that are delivered at a later time, the delay specified in milliseconds. This allows programmed delays and allows for something to happen at a later time without tying up the computer on unnecessary idle events.
void AsyncFiMsg(Char4 EvNo, File fi, int EvLoc, int EvDat, Process
toProc);
void ReplyFile(Event theEv, File fi);
File EventFile(Event theEv);
File references can be sent between processes using these calls. AsyncFiMsg is the same as AsyncMsg, but sends a file reference number in EvInfo (see GetFiRefNum above). The recipient can use EventFile to extract the file reference from the received event, and CloneFileRef to localize the reference. EventFile uses FileFromID to convert a file ID to a valid file reference, if any.
Window EventWindow(Event ev);
Widgit ClickedWidgit(Event theEv);
Similarly, many events are relevant to a particular window, and its Char4 name is sent in EvDatum. EventWindow extracts the window reference from that ID. Some events include a Widgit name in EvLocn, from which ClickedWidgit can recover the Widgit pointer. If there is not an actual Widgit with that ID in the event window, it returns null.
int MakeEventStr(String str);
String GetEventStr(int str);
Although only integers can be sent in event objects, there is a way
to send an arbitrary text string to another process, by encoding it using
MakeEventStr.
This returns a small integer, which any process can send to GetEventStr
to recover a copy of the original string. Use EncodeIntArray
(in package TextAry) to convert an array
to a string. This is a temporary storage, and the strings so encoded are
not preserved past the first call to GetEventStr. You are
also limited to a maximum of 30 outstanding strings. Maybe in the future
we can build a more comprehensive solution.
boolean AutoEvent(Event theEv);
Some specialized windows manage their own events. You should call this function early in your event loop, and immediately continue to GetEvent if it returns true (the event was handled). It's an explicit call so you can override any events in your own program before letting the defaults act, if you so choose. You can also set up your own event handler(s) to be called by AutoEvent by subclassing EventBroadcast:
class EventBroadcast {AutoEvent maintains a linked list of EventBroadcast objects, and the HandleEvent method of the each object in this list is called for each event, until one of them returns true.
EventBroadcast link;
boolean HandleEvent(Event theEv) {return false;} // true if event is taken
void DeInstall() {/* remove this from event path */}~DeInstall
void ReInstall() {/* install this in event path */}~ReInstall
EventBroadcast() {ReInstall();}~EventBroadcast}~EventBroadcast
When you have multiple processes with their own windows and their own
event handlers, sometimes these events get sent to whatever process owns
the top window, which may be a different process than the event should
be handled by. The ProcEvent class in the Processes
package is a subclass of EventBroadcast, and it maintains lists
of which events should go to which processes.
void SetClip(String theTxt);
String GetClip(Event theEv);
Usually in response to a menu event, the program can access text in the system clipboard. There are other functions in the UnDragPkg package for doing fancier stuff with clipboard data.
Separate processes must be managed using the Processes package, but these functions are useful also in a stand-alone program:
void Quit();
Quit unconditionally terminates the running process. You are responsible for making sure user data is saved before asking to Quit. When a sublaunched process Quits, a "Term" event is sent to the parent process. When the root process Quits, everything is unconditionally terminated. These are some other useful functions for accessing process information:
Process MySelf();
Process Proc4ch(Char4 pnam);
Char4 ProcID(Process pnam);
Char4 WhoAmI();
Point GetMouse(Window theWn);
int GetButtons();
// mods: lok:0, shft:1, ctrl:2, cmd:3, opt:4; mouse:31,30,29,
frnt:28
boolean ForeGround();
boolean AbortMe();
These are some functions for accessing the computing environment. The mouse location is relative to the specified window, if any, or else to the main screen if null. The low five bits returned by GetButtons represent the various (Mac) modifier keys; its upper bits represent the mouse button state(s). ForeGround is true if the program has an active window (its menubar is showing on the Mac); it could also be true if a sublaunched program is active. AbortMe is only true if the running program is in the foreground, and the user has pressed the system-defined key combination for aborting whatever is running; on the Mac that is command-period. You should call it often in computationally intense loops, and terminate the operation if true.
int GetTime();
int GetMilliSecs();
The system time is returned in seconds since midnight 2000 January 1. On the Mac the milliseconds is not particularly accurate relative to the seconds, it's just a way to get a finer gradation of time.
void SpinBeachBall();
void TurnOffBlak();
void Beep();
These are some non-event ways of interacting with the user. SpinBeachBall spins a beachball cursor to show that the computer is busy doing something long computation; you need to call it often for the ball to spin. If the computer runs continuously with no user activity for several minutes, a screen-blanker automatically kicks in; use to TurnOffBlak to disable this for the current process. Beeping is bad manners, but it can be used to call attention to something that might not otherwise be noticed. I use it to signal the end of a very long run, because it can be heard across the room.
String AskText(String msg, String txt, int opt);
This asks for short text input, giving a prompt and a default initial value. If it is cancelled, it returns the empty string. The option number was originally intended for specialized processing, like hiding password typing, or filtering the data, but I never got around to it; just use zero.
int MsgBox(String msg, Char4 icn, int opt);
This presents a short text message, one of three icons ("Qstn", "Bang", "Stop"), and up to three buttons for the user to respond with. HyperCard let the program specify the exact text of the buttons, but Windows is less generous with their system message box, so I have numbered the allowed choices thus:
final int MsgOK = 1, MsgCancel = 2, MsgNo = 3, MsgYes = 4, MsgSave = 5,Each four bits of the opt parameter designates one of these values, with the default being the low four bits. These constants are defined to assemble the various values, with a common one as an example:
MsgDiscard = 6, MsgYesAll = 7, MsgResume = 8;
final int MsgBtn1 = 1, MsgBtn2 = 16, MsgBtn3 = 256;If you pass it 0 for opt, the default MsgOK is assumed. The value returned by MsgBox is the numerical value for whichever button was selected, such as 1 for MsgOK, 2 for MsgCancel, etc.
final int MsgOKCancel = MsgCancel*MsgBtn2+MsgOK;
void StatusMsg(String txt);
void HideStatus();
boolean StatShownQ();
If you just need to report the status of a computation without requiring
user response, StatusMsg can show a small text message.
When it finishes, you can HideStatus to hide the window.
StatShownQ
returns true if the status window is showing.
Window NewProgress(int vx, String txt);
Window ProgressBar(Window wx, int vx);
Window ProgressMsg(Window wx, int vx, String txt);
void EndProgress(Window wx);
For a fancier progress bar with a built-in cancel button, use NewProgress
to initialize the window with the total number of units of estimated elapsed
time (or items to process) and a brief text message explaining what is
being done. Each subsequent call to ProgressBar or ProgressMsg
should give a number less than this final value, to show a thermometer-like
progress bar; ProgressMsg also replaces the text message.
It returns
null if the user hit the Cancel button, or you
can use
EndProgress to dismiss it at the end. The full
implementation of these functions is in package ImDlogPkg;
if you do not import it, you will get a reduced functionality which uses
StatusMsg.
These are some operations on the Point data type. It's an opaque type, so the compiler does not allow you to compare it to anything, but EqPt compares two Points for equality. AddPt adds two Points as a vector, that is, separate sums of the vertical and horizontal components. Similarly, NegPt takes the negative of the two components. You can subtract two Points by taking the NegPt of one then AddPting them. Extract the signed components using PointV and PointH, or assemble two integers into a Point using MakePoint.
int LoHalf(int pt);
int HiWord(int pt);
int LoWord(int pt);
int Pack2ints(int V, int H);
int MaskInts(int a, int b, int m);
These are the same operations, but applied to plain integers. LoHalf returns a positive (unsigned) number between 0 and 65535; it's equivalent to (pt&0xFFFF). HiWord and LoWord return signed values. The mask in MaskInts picks out the bits of the second parameter that replace the corresponding bits of the first parameter in the returned value; thus Pack2ints(a,b) is exactly equivalent to MaskInts(a<<16,b,0xFFFF).
int Next2pow(int valu);
int Log2(int valu);
Next2pow returns the smallest power of two not less than its parameter. If it's not an exact power of two, the next bit up is returned. Log2 returns the bit number of the highest non-zero bit. To get the smallest non-zero bit in an integer, simply AND it with its negative: (valu&-valu), then (if desired) apply Log2 to get its bit number, which is the number of (insiginficant) zero bits below it.
char Capitize(char ach);
int YearMonthDay(int theTime);
Capitize capitalizes a letter, and returns other characters unchanged. For a time given in seconds since the turn of the millennium (as returned by GetTime), YearMonthDay returns in a single integer the year in the high 16 bits, the day of the month in the low six bits, and the month in the four bits above that.
boolean IsOnlyLetter(char xCh); // true only if xCh is one
of the 26 letters
boolean IsLetter(char xCh); // also true of digits and underscore
'_'
int RomanDeco(String theStr); // returns the integer value
of a roman numeral
String LastWordOf(String theStr); // like LastItem
but ignores trailing whitespace
int TheItemIn(String seek, String inHere, char sep);
If seek is a whole item in the list inHere, this returns its item number, otherwise zero.
String SingleSpace(String theStr);
Each run of whitespace is replaced by a single space, except that leading and trailing whitespace is removed. Line breaks are preserved, but blank lines are removed (including at the front and end, as are also spaces at the front or end of each line.
String BlankLns(int here, String theData);
Unlike HyperCard, PutLineOf(something,here,theData) does not insert extra lines at the end of theData if here is greater than the number of lines in theData; BlankLns inserts enough blank lines so that PutLineOf works as desired.
String Trim2Lines(String theText, int nlns);
This returns only the first nlns of theText. If there are fewer, nothing is added (use BlankLns for that).
String FormMyChar(String prefx, char whom);
If whom is a valid (printable, 7-bit) ASCII character, this returns prefx followed by whom in apostrophes; otherwise it returns FormMyInt(prefx,ORD(whom)).
Char4 Char4int(int theNum);
This converts a decimal number less than 10000 (and greater than -1000) into a 4-digit, zero-fill Char4 representation. For example, -12 is returned as "-012".
String ReadWholeFile(File theFi);
void WriteWholeFile(File theFi, String fiData);
Given a file reference theFi to a text file (such as returned by the file dialogs FileDialog and NewFileDlog), this reads or writes the whole file. theFi is left in the reference state. These should be limited to text files of a couple megabytes or less.
String FileListInfo = "", FileListUsed = "";
File GetFileListRef(String refName, boolean newly);
String ReadListFile(String refName);
void WriteListFile(String refName, String fiData);
FileListInfo is a list of reference names and file name paths which the application program is responsible for maintaining. The first line is not part of the list, but thereafter, each line consists of some reference name followed by a '/' character, followed by the full-path file name. GetFileListRef looks in this list for the reference name, and tries to open that file (creating it if newly is true), then closes it and returns the reference, or null if it fails. FileListUsed (if not empty) is a list of the successful references sought: if it starts with '/' it records just the reference names, one per line, otherwise the whole line from FileListInfo; if it starts with '+' it adds only lines not already there. The other two functions combine the services of GetFileListRef and ReadWholeFile or WriteWholeFile.
IntArray MakeTxtAry(String theStr, String whom);
String GetTxAryLn(IntArray theData);
A common software requirement involves the processing of sequential lines from a block of text. Fetching text lines from a larger string is convenient in MOS, but not very fast, because each access must count the lines up to the next line desired. MakeTxtAry stores the text into an (integer) array with the line breaks already marked out (see also the Array Text Functions). GetTxAryLn then sequentially fetches those lines without searching the entire body of text each time. The array has integer indices at the front pointing to the line breaks; position [0] always is the line number of the next line to be accessed, or zero when finished.
char GetArrayByte(IntArray ary, int offs);
void PutArrayByte(IntArray ary, int offs, char whom);
This is a fail-safe way to fetch characters out of a block of text in an integer array (see also the Array Text Functions), or put them back. GetArrayByte(ary,offs) is equivalent to TextChar(ary[offs>>2],offs&3) but defaults to '\0' instead of crashing (MOS dies politely, but terminally) if ary is null or offs is out of bounds. Similarly, PutArrayByte simply does nothing in such a case. Array reference-counting in the function call is also slightly faster than string reference-counting and bounds-checking.
void Install_Font(Char4 whom, IntArray fnt, int inx, IntArray krn);
Besides the eight built-in fonts, you can install
two more of your own devising (index inx=9 and inx=10)
with their own Char4 names. The font table format is described
elsewhere, and the kerning table krn is not used at this
time (just pass it null).
IntArray PartOfArray(IntArray theData, int bgn, int nwds);
Returns a smaller array containing only the specified part.
String EncodeIntArray(IntArray ary);
IntArray DecodeIntArray(String theStr);
These two functions convert an integer array to text and back, as a convenient way to send it to another process or save it in an Undo package.
Unrelated to them are the functions for embedding text into integer arrays for faster access, which are actually in System and Utilities:
void Str2TxtBlok(String st, int bg, int ln, IntArray ary);
String TxtBlok2Str(int bg, int ln, IntArray ary);
String Array2Str(IntArray ary);
int FindStTxBlk(String st, int bg, int ln, IntArray ary);
void InitPstr(IntArray ary);
void AppChar(IntArray ary, char theCh);
void AppCh4(IntArray ary, Char4 theCh);
void AppHex(IntArray ary, int nm, int nd);
void AppNum(IntArray ary, int nm);
void AppPstr(IntArray ary, String st);
boolean IsntPstr(String st);
int GetPstrLn(IntArray ary);
void TrimPstr(IntArray ary, int tab2);
String GetPstr(IntArray ary, boolean same);
IntArray MakeTxtAry(String theStr, String whom);
String GetTxAryLn(IntArray theData);
String SortLns(String str);
String SortWords(String str);
String SortItems(String str, char sep);
String SortGen(String str, char sep, boolean desc, boolean irev,
boolean nemt);
The first three are more or less obvious. They call SortGen with specified default settings for the additional parameters. desc sorts in reverse (descending) order; irev informs the sort that the incoming data is mostly already sorted in reverse order, which might save some time if that is the case; nemt removes blank lines or multiple delimiters from the front (or end) of the final sorted text. All three default to false, except that nemt is true for SortWords (excess spaces removed).
Process NewProgram(String fnm, File dir, Char4 pnam, IntArray stkF);
New processes are created by starting up a separately compiled program. The containing directory (not yet used) and file name are the same as opening a file, the name of an existing program (file) compiled by the Turk/2 compiler. T2C ignores the file, and each sublaunched program must be linked into the main program at compile time, which should be automatically set up in the T2C compile (of course you still need to do the right thing in your make file). You can give NewProgram a suggested process ID in pnam, or Null4 to have it use a default based on the file name fnm. In any case, the process ID will be unique; multiple instances of the same program will be given different IDs. The compiled package code must either have different package numbers, or else have been compiled at the same time (and is re-used in memory). You can give it an array stkF to be used for the program stack, or null and it will allocate its own (T2C ignores your stkF). Unless you keep a pointer to the array, the memory will be deallocated when the subprogram quits. Use StakDepth to help calculate a reasonable size for stkF. If you run this program stand-alone, and call StakDepth just before quitting, the returned number, plus the size of its code, is the minimal size required for stkF. If you run it as a NewProgram, StakDepth returns the actual size used (but it's a good idea to allow another +1000 or so for margin).
You can use the returned Process reference to send messages to the new program. It can ask for its ParentProcess, so to send messages back.
Processes are managed asynchronously at (at least) two priority levels (on the Mac). A process that quickly blocks on GetEvent or SendEvent or some other blocking operation generally runs at the higher priority. Heavily compute-bound processes run round-robin at the lower priority. A new process is started in the lower priority (and its parent advanced the higher) so that the parent process has opportunity to make whatever adjustments it may need to do to shared data before the new process actually starts up.
A single line of code will generally execute on the MacOS without a context switch (to another process) unless it contains a loop or a function call. Note that some framework calls are invisible, such as array bounds checking and integer division (in the Mac) and all string operations. Win32 threads can interrupt at any time.
void Quit(); // (in System)
void KillProc(Process whom);
Quit unconditionally terminates the running process. You are responsible for making sure user data is saved before asking to Quit. When a sublaunched process Quits, a "Term" event is sent to the parent process. When the root process Quits, everything is unconditionally terminated. You can also KillProc another process, which is the same as if it asked to Quit; use it with care.
Process MySelf(); // (in System)
Process ParentProcess();
Process Proc4ch(Char4 pnam);
Process ProcByName(String st);
Char4 ProcID(Process pnam);
Char4 WhoAmI();
String GetProgName(Process prc);
These are some useful functions for accessing process information.
int MaxProcIx();
Char4 GetIxProc(int ix);
Sometimes it is useful to access every other running process without knowing in advance their names or IDs. You can index through all running processes using GetIxProc to get their IDs. The root process is always index 0, but others may shift around if additional processes start up or quit between looks. At most 15 processes may be running at one time (in the Mac).
int SendMsg(Char4 EvNo, int eInfo, int EvLoc, int EvDat, Process toProc);
This is like AsyncMsg, but it blocks
waiting for the receiving process to reply. Obviously you must use this
with care, because two processes could send each other messages at the
same time and thus "deadlock", because neither can reply until the other
does.
When you have multiple processes with their own event handlers, sometimes
these events get sent to the top window, which may be owned by a different
process than the event should be handled by. The ProcEvent subclass
of EventBroadcast maintains lists of
which events should go to which processes.
class ProcEvent extends EventBroadcast { // subclass in each processEach process in your program would subclass ProcEvent and instantiate an object of it. Your DispatchEvt method is given the index in myEvents corresponding to this event (from IsMyEvent), which you can use in a switch statement to quickly get to the handler for that event. Like HandleEvent, it should return true if the event was handled. If theList installed by KnowTheseEvts is accurate, you should never need to return false, but if you do, the next handler in the list will get a chance at it.
Process myProc; // set by constructor
IntArray myEvents; // set by KnowTheseEvts
boolean DispatchEvt(int whom, Event theEv) {return false;} // override to do it
int IsMyEvent(Char4 theMsg); // rtns index in myEvents
void KnowTheseEvts(String theList); // installs in myEvents
ProcEvent() {myProc = MySelf();}~ProcEvent}~ProcEvent
Immediately after instantiating your subclass object, you should call its KnowTheseEvts method to set up the myEvents array. Give it a string list of the event names your DispatchEvt will handle, four characters each. You may separate them by spaces or commas for readability if you include the same delimiter at the front of theList (but theList must begin with an event name which starts with a letter or digit); in any case, each event name must still be exactly four characters long after the delimiters are removed. You can call the IsMyEvent method at any time to ask if a particular event name is in the list; it will return its index if it is, or -1 if not.
boolean SeeProcEvtLists();
This function logs the event names of all installed ProcEvent objects, and returns true if there are any duplicates. This is a diagnostic tool, because the first installed process in the list that accepts it will be sent the event, and it cannot be forwarded to another process that also accepts it. Generally, the first object/process instantiated will be the first in the list, and others are added behind it in reverse order, unless you ReInstall one or more of them.
void CantUndo();
boolean BeganUndoPkg(Char4 sgn, int inf, String lbl, boolean
fize);
Each time a user initiates some operation, your program should call BeganUndoPkg to start up a (possibly new) Undo package. Some operations should be incrementally accumulated, such as moving an object on the screen, where each separate "mDrg" event might call BeganUndoPkg again, but you want them accumulated into a single Undo that moves the object back to its starting point. Other operations are inherently atomic, and should be individually Undone. The parameter fize should be false for atomic operations, and true if like operations should be accumulated into one package. BeganUndoPkg returns true if it began a new package (always if fize is false), and false if it is continuing to build on an existing package with the same signature sgn. When fize=true, the signature should be unique for different operations, and the same when you want to collect multiple operations into a single Undo (when fize=false it always begins a new package, even if the signature is the same). Your program will be sent an event named by the signature sgn with EvInfo=inf if the user chooses Undo from the menu while an accumulated Undo is being built, that is, the "finalize" parameter fize was true when BeganUndoPkg was called; you use this event to do whatever needs to be finished up before the Undo package can be processed. Use CantUndo for the (hopefully rare) operation that cannot be undone.
void DoUndo(Window theWn);
Normally an Undo package is processed by the menu manager when the user chooses Undo from the menu. You can, however, start the process up yourself by calling DoUndo. However you are responsible for invoking your own finalize in this case. The Undo events will be queued immediately. Note that if there are other unprocessed events pending, they will come before the Undo package events, but the Undo events will all be posted before any unrelated events are added to the event queue.
void NewUndo(String lbl);
void RedoMenu(String txt);
If you find reason to change the name of the Undo menu from what was given in BeganUndoPkg, use NewUndo; a user might begin typing deletes, which your program would report as "Delete" in the Undo menu, then switch over to typing new data, necessitating the change to "Typing" while continuing the same package. RedoMenu stores in the package a different label for the automatically generated Redo menu, if you so wish. Note that MOS automatically adds the words "Undo" and "Redo" to the label you give it. The same label will be used for both Undo and Redo unless you specify differently with RedoMenu.
void UndoAdd(Char4 ms, int inf, int loc, int dat);
void RedoAdd(Char4 ms, int inf, int loc, int dat);
void UndoText(String txt);
void RedoText(String txt);
void UndoTxAdd(Char4 ms, String txt, int loc, int dat);
void RedoTxAdd(Char4 ms, String txt, int loc, int dat);
void UndoFile(File fi);
void RedoFile(File fi);
You can set up most any arbitrary event messages as records of the operation
to be undone, but often you might just want to specify some text to be
replaced. The UndoText and RedoText messages
come as "TEXT" events, or you can specify your own message codes
using UndoTxAdd and RedoTxAdd. Use UndoFile/RedoFile
to package up file references. Other types of data you should cache yourself,
and include in the Undo pack only some kind of reference to it.
Clipboard and DragonDrop operations exchange data not only with
your own program, but also with other programs running on the host system.
They are implemented in MOS using the same data type, DraCliPkg,
which packages up a variety of data types and contents. Normally these
activities are initiated by the user through some kind of event; your program
then puts together the data package and sends it off to the recipient,
whoever that may be. When your program is the recipient, you can extract
the data and apply it to the current context. Each item of data has a Char4
type to distinguish it from other data types; a few of these (right now
only "File" and "TEXT") are predefined, but you can use
any type code you wish for data received only by your own program.
DraCliPkg DroPastEvent(Event theEv);
This is the call you use when data is dropped on your window (you received the "Drop" or "OpFi" event), or else the user calls up "Paste" from the menu. For simple text copy/paste you can use the System functions SetClip and GetClip.
DraCliPkg NewClip(Event theEv, int sz);
DraCliPkg NewDragSelec(Event theEv, int sz);
DraCliPkg NewDragSelStr(Event theEv, int sz, String im);
Use one of these calls when the user has selected "Cut" or "Copy" from the menu, or has started a drag and you got a "Drag" event. You may offer an estimated size sz, or pass it zero and MOS will make an estimate; if the estimate proves too small, the package is enlarged. If the window management code supports it, you can have it define a drag outline matching the selection rectangle, possibly containing some short text. Otherwise it will use a default outline.
void DragFrom(DraCliPkg id, Point loc);
void PkgText(DraCliPkg id, String txt);
void PkgFile(DraCliPkg id, File fi);
void PkgItem(DraCliPkg id, Char4 ty, int val);
void PkgAdd(DraCliPkg id, Char4 ty, IntArray ary);
void PkgDone(DraCliPkg id);
Once you have defined a new drag package, you can add items to it. DragFrom gives a screen location where the drag will appear to begin. Most often shared data will be text or file references. Individual (integer) items can be added with arbitrary specified type, or you can add a whole array of integers. When you are finished adding data to the package, call PkgDone to close it and send it on its way. If it's a drag and it successfully lands somewhere, you will get an event ("DrgX" or "DrTr"if it is to be deleted from its source document, or "Drug" if not) signifying that.
int FromPkg(DraCliPkg id, Char4 ty, IntArray ary);
int GetPkgInt(DraCliPkg id, Char4 ty);
String GetPkgTxt(DraCliPkg id);
File GetPkgFile(DraCliPkg id);
Point PkgLoc(DraCliPkg id, boolean drp);
int PkgInfo(DraCliPkg id);
When your program is the recipient of a drop or paste, you can extract the data by the obvious reverse functions. PkgLoc gets either the drop or origin screen location; PkgInfo returns the drag modifier bits (which match the shift and control bits from GetButtons).
btns -- the value returned by GetButtons() at the time the event is posted.
time -- the time the event is posted, for event ordering, but not particularly related to real time.
cloc -- the location of the click at the time the event is posted, relative to the window.
winID -- the window in which this event occurred. Use EventWindow to convert this to a Window.
"wAct",activ,,winID
This is sent to the owner of the window whenever the window is activated
(receives focus, activ=1) or deactivated (gives up focus, activ=0).
You might want to redraw the window to reflect its new state.
"SizW",btns,size,winID
This is sent to the owner of the window whenever the window is resized
by the user; size is the new window size.
"SzWn",btns,size,winID
This is sent to the owner of the window whenever the window is resized
for any reason, but contained widgits are not automatically resized and/or
repositioned to match.
"CloW",btns,,winID
This is sent to the owner of the window whenever the user clicks the
window close box.
"KeyC",keyc,btns,winID
This is sent to the owner of the window whenever the window is active
and the user types a (non-modifier) key. The low 8 bits of keyc
is the (ASCII) key code; the upper half of keyc
contains the modifiers that were pressed at the time the event happened.
"0win"
This is sent to the main program when the last window has closed. In
Windows it is customary to quit.
"Term",procID
This is sent to the parent program when a sublaunched process (procID)
quits.
"Clik",wgt,cloc,winID
"ModK",btns,cloc,winID
"2clk",time,cloc,winID
"msUp",time,cloc,winID
This is sent to the owner of the window whenever the mouse does something
in that window and it does not trigger some other (programmed) event. If
the user clicks on a defined hotspot, that hotspot's message is sent instead.
If there is no hotspot defined where the mouse is clicked, "Clik"
is sent the first time, and "2clk" is sent if another click happens
very soon after in the same place. If there are any modifier keys down
at the time, or an alternate mouse button is used (but not as defined for
context menu), "ModK" is sent first. "msUp" is sent when
the mouse is released.
"mDrg",time,cloc,winID
"Drag",time,cloc,winID
"Drug",mods,dloc,winID
"DrTr",mods,dloc,winID
"DraX",mods,dloc,winID
"|Clk",time,cloc,winID
These are sent to the owner of the window whenever the mouse is dragged
in that window. If the mouse was clicked in a hotspot defined to be draggable,
then the "Drag" event is sent after the mouse begins to move;
if you build and send off a drag package, then you will receive "Drug"
after the data is successfully dropped somewhere, unless it is into the
trash ("DrTr") or the user has indicated that it should be moved
not copied ("DraX"). If the user does not move the mouse after
clicking in a draggable hotspot, "|Clk" is sent instead. If the
user begins dragging somewhere other than a defined draggable hotspot,
"mDrg"
is sent each time its position changes after the initial
"Clik"
event, until the mouse is released.
"CxMx",time,cloc,winID
"CxMn",msg,cloc,winID
"CxMn" is sent to the owner of the window whenever the user
activates a context menu gesture (right-click, or control-click on a Mac)
in that window. If the user holds the mouse down but does not move it for
a few seconds, "CxMx" is sent instead of the usual "msUp".
"Drop",btns,hots,winID
"OpFi",file,dloc,winID
One of these is sent to the owner of the window whenever the user drops
something on it, and the specified window management code accepts it. If
it does not accept a text file drop, then it is converted to "OpFi",
and sent anyway; use EventFile to get a file
reference.
"Imag",bitz,wgt,winID
"WnUp",bitz,wgt,winID
"ReLo",bitz,wgt,winID
These are sent to the owner of a managed window, in addition to programmed
events, as defined in their respective packages. "Imag" is sent
when the image window needs to be drawn. "WnUp" is sent with a
ImDlogPkg
window is opened or closed. "ReLo" is sent when a BigTxtPkg
window needs a segment loaded.