The text is both for people who know Smalltalk and do not know GEOS
or the Nokia Communicator and for people who know GEOS and the Communicator
and do not know Smalltalk. For those who do not know any of these worlds
it may be difficult to understand and I would recommend to start with some
introductory text on Smalltalk programming (see references near the end
of this document) and to come back here after having a basic idea about
what Smalltalk is and how the Smalltalk object system works.
Although the device has excellent possibilities and the built-in applications (plus some extras available from Nokia) are not that bad, the success so far with techie type folks has been limited due to the lack of any decent way to write own applications for the device. Nokia supplies a "Software Development Kit" (SDK for short), which contains the tools and documentation required to write native GEOS applications for the Communicator. There are several problems with the SDK for an average programmer: first, there is a huge amount of information one has to digest before writing the first usable program; and second, the GEOS system has severe limitations due to the fact that it uses the Real mode of the 80386 processor (i.e.. runs like the old IBM PC XT in 16 bit segmented memory, rather than the 32 bit flat memory model used in operating systems on current PCs).
After trying to write some simple programs for the Communicator using the SDK, I have tried to find some environment which would help to develop complex applications with full UI compatible with the built-in applications, but with higher level language and constructs than the GOC language used in the SDK. The main objectives were to have a completely transparent memory management, graphical environment for program editing and other functions. The only alternatives to GOC for the Communicator were FreeBAS 9K by Conrad Davies (a simple variant of the BASIC language, the environment enables to write programs directly on the device) and PFORTH (an unfinished port of the FORTH language). None of these had the possibility to build the UI compatible with built-in applications and to have the power similar to the SDK. Nokia has also introduced a "Nokia 9000/9110 Development Environment", which is a GUI shell around the SDK with some limited possibilities to generate GOC code for common tasks (this tool has been known in the Communicator programming community as "Blank Screen Generator"), but the only really useful functions were syntax coloring and better access to the documentation files.
After I have failed to find something useful, I have decided to try to port some existing environment to the Nokia Communicator. The source of inspiration was mainly PalmOS, which is gaining popularity and has a lot of available software. The older models of PalmOS devices have similar architecture (16 bit rather than 32 bit, very limited RAM), so porting from PalmOS to GEOS should be much simpler than, say, porting from Windows CE or EPOC32, which are both 32 bit operating systems with flat memory models. Another source of inspiration were old MS-DOS development tools from times when DOS was 16 bit and used to run "well" in 640 KB of RAM. The hot candidates for porting were:
Smalltalk is the oldest (and some people say purest and others say the best) object oriented programming language developed in late seventies and released to public in 1980 as Smalltalk 80 (yes, people were calling versions by the release year even before Windows 95, Algol was probably the first in late fifties). Smalltalk, besides being an elegant and easy to use Object Oriented programming language, is usually a complete programming/runtime environment, which was the first to use bitmapped graphics, a mouse as a pointing device and overlapping windows on the screen. Everything else (Apple, X11, Windows) is based on the ideas and experience of the original Xerox team behind Smalltalk.
Pocket Smalltalk has been written by Andrew Brault (at least all source files contain Andrew's and no other copyright) and is being maintained by Eric Arsenau. It has been written from scratch for the Palm Pilot (before it was called PalmOS or Palm Computing platform), uses the Smalltalk language, but quite a different compilation/runtime model as compared with other Smalltalk implementations. Pocket Smalltalk was released with full sources and a liberal license derived from the license of the popular web server Apache (please read the file LICENSE.TXT). I have decided to port this system to the Nokia Communicator.
It took me over 6 months (about 2 evenings per week, one or two full days in a month over weekends, about three full days in bed while sick at home) to get to a system which could be used for writing real life applications. The Virtual Machine (VM, what interprets the Smalltalk bytecodes on the Nokia Communicator) has changed very little over this time, except some glue code between Smalltalk and GEOS. The IDE (Integrated Development Environment, the application running on a PC and producing a binary file to be run by the VM on the Communicator) has changed only due to some enhancements and bug fixes. What has changed the most is the glue from Smalltalk to GEOS and the set of predefined files in Smalltalk with definitions of the GEOS system interfaces. With these, you can do (nearly) everything as in the GOC/SDK, and even the existing limitations are slowly being removed.
I owe much credits to Andrew Wilson from Magna Praeda, who answers all my crazy questions about GEOS internals and has been my valuable source of information and advice. Without his help, the GEOS port would never become a reality. Other people such as Marcus Groeber have provided sources to their GEOS programs to give me some background information how a real life GEOS application for the Nokia Communicator should look.
So, to summarize the (now nearly 100% achieved) goals of the Pocket Smalltalk for Nokia Communicator (PST9110 for short):
To run the demo application, go to Extra launcher, start EC Pocket Smalltalk (the EC means Error Checking), press the RUN trigger, see what happens, CLOSE the application.
This version of the demo application does several things:
If using the SDK emulator, please use the Error Checking version (EC). This is slower, but performs a lot of checks on all GEOS system calls, so any problems encountered should be reported in SWAT (a GEOS debugger available with the SDK). To see the errors reported, you should always start SWAT after starting the emulator and keep it sitting in the background. If any problem is detected by the EC functions, it is (sometimes) reported with explanations and context in SWAT and you can use SWAT commands to explore the reasons even deeper. For the standalone emulator, only the non error checking (NC) version is available, so replace .EC to .NC in the following path names.
To install the Virtual Machine, copy the file pstec.geo to a directory called WORLD\EXTRAPPS in the SP_TOP of the emulator (this would be something like C:\PCGEOS\yourname\N9110V10.EC\WORLD\EXTRAPPS in the 9110 SDK emulator and emulator\WORLD\EXTRAPPS in the free emulator). Then copy the file pstvm.vm to DOCUMENT (i.e.. C:\PCGEOS\yourname\N9110V10.EC\DOCUMENT or emulator\DOCUMENT).
Once installed, you can run the emulator, go to Extra Launcher (CTRL-F12 on my keyboard) and to start the Pocket Smalltalk demo as in previous section.
Q initGeosSystem. QLauncher show.With this text selected, type CTRL-E (Execute) and a new window will open (Pocket Smalltalk Launcher). Now you can close the Package Browser window and in the Dolphin System Transcript Save the Image (either with a toolbar button or from the File menu). After you run Dolphin Smalltalk next time, you will get a Pocket Smalltalk Launcher window immediately. Do not save the Image while closing Dolphin Smalltalk, unless you have made some changes to the IDE classes or want to save your environment as it is and return to the same state later.
All further work will be done in the various Pocket Smalltalk windows. All these windows are launched from the Pocket Smalltalk Launcher.
For any Nokia Communicator application in Pocket Smalltalk, you will have to include the following system packages before you start to make any changes. Please note that some of the longer files parse and compile a long time (up to a minute on my notebook), the usual Windows hourglass cursor is displayed during this time. So, to prepare for the demo application, you have to include the following packages and you should mark all of them as Don't save with project:
Now we need to include the files which are specific to our Pocket Smalltalk for Nokia Communicator demo. As you will probably change these, you should not mark them as Don't save with project and you should make the last file marked as Default (which means that all new Smalltalk classes will go into this package). Please note that the Default package information is not saved with the project, so that you have to mark some package as Default or to check all classes and Constants for Uncommitted before saving the project, or you will lose your changes. The demo application specific files are these:
When the Pocket Smalltalk application is started, the interpreted first executes the method basicStart in the class side of Smalltalk class. As you can see, basicStart does some system initialization and starts Smalltalk class>>start, which does the real work. The definition of start contains simple invocations of the individual demo steps one by one, always displaying the result into the console window using Geos class>>showText:.
The hierarchy of classes under the class called Q is the Pocket Smalltalk parser/compiler/interpreter and these classes are similar (or even the same) as those under Q in the Dolphin PST IDE. Please note that if you comment out the last step in Smalltalk class>>start, the resulting compiled bytecodes will not include anything from the Q class hierarchy, as these will be left out by the optimizer (unless you turn optimization off).
All classes under Meta are GEOS classes and the Smalltalk objects are simply links to underlying GEOS objects and Smalltalk methods are either glue methods (on the class side of GEOS classes), or map GEOS instance variables, vardata and messages to Smalltalk methods (these are on the instance side of GEOS classes). There are some Smalltalk helper methods, which were defined manually (remember all the geos/*.st files were generated automatically with the classes.pl PERL script) and there is one GEOS class defined in Smalltalk in the demo - it is GenInteractionSmalltalk, which overrides the GEOS system MSG_GEN_GUP_INTERACTION_COMMAND, which is available in Smalltalk as #gupInteractionCommand:.
Now you should save the demo project under a different name (say, demo.prj). You can also save the Dolphin Image file so that you can continue any time from where you have left off. To generate the compiled bytecode file, use System, Generate Code (it will ask you for the name of the *.vm file; the current pstec.geo application can handle only the pstvm.vm file, so you have to name the file with this name or to rename it later to be used).
To install a new PST Image, install that either to the Communicator (using the VM.INS script; say yes to the question to replace an existing document file) or to the emulator.
The supplied demo pstvm.vm file is with debug info (names of classes and symbols for error messages) and with optimizations on - so it has about 67 KB. If you turn off optimizations, the pstvm.vm will increase in size several times (515 KB). Turning off debugging and keeping optimizations on reduces the size to 51 KB for the whole demo (remember it is over 10 thousand lines of Smalltalk code).
You need a CD called Nokia 9110 SDK - you need to register at http://www.forum.nokia.com/developers/communicators/geossdk/9110sdk_request.html and Nokia will send you the CD free of charge (it took over a month in my case, so be patient). The SDK works only on Windows NT or Windows 2000, so you're lost if you have Windows 95 or 98.
To compile the sources, you need the Borland C/C++ compiler version 4.5x (I have 4.52, which was given to me by someone who had this for a long time and had no use for it any more). The 4.x is apparently shipped with the newer versions of the same product (such as 5.x) and is bundled with certain C/C++ textbooks widely available. The Borland C 4.x is not available from normal software retailers any more. You need only the MS-DOS real mode parts of the environment for the Nokia SDK development, you do not need to install any Windows or database or other optional components, these are of no use for Nokia Communicator development.
Once you have installed Borland C and the Nokia SDK (you need about 500 MB of free disk space for these two CDs!), you can copy the PST directory to your GEOS development branch, go to PST and to make a new version you can execute "mkmf" (make makefile) and then executing "pmake" will build a new version of the PST Virtual Machine. If you change existing files, executing pmake would do the rebuild, if you introduce new files, you need to re-run mkmf (the Makefile is created automatically, you do not need to manually list the files anywhere).
The GEOS API is different to that of PalmOS in many respects - the SYSTRAPs use single entry point to call all the functions which are assigned mnemonic names, but the actual call uses a 16 bit number to identify the entry point. The programming model in PalmOS is for plain C language. In GEOS, the system API is spread over a number of dynamically loaded libraries and the actual interface is via a function call directly to the entry point of the library (identified with library name and ordinal number of the entry). GEOS uses both Pascal and C calling conventions, which complicates things even further, as the Smalltalk glue code needs to know whether to use the C or Pascal calling convention. The aspect which complicates the link between Smalltalk and GEOS the most is the fact, that the GEOS system itself is object oriented, has its own class system, objects and messages. The GEOS system of classes and methods is more complicated than the Smalltalk one in at least two aspects: first, the GEOS classes can have multiple inheritance, that is a class can have more than one class as its superclasses (in GEOS speak this is called a variant class); second, the object instances can have fixed instance variables, but can also have optional data attached to them, which are called vardata in GEOS speak. For a system with most of the power of GOC we have to map the variant classes mechanism and the vardata to the straightforward Smalltalk object model.
Most of the complexity to learn to use Pocket Smalltalk for the Nokia Communicator is in the fact that one has to learn at least some basics about the GEOS object model, understand what the various GEOS UI classes do and which methods should be used for which function and which methods are called by the operating system and the application has to override them in application specific classes to handle some system events. For the functionality to handle GEOS system events in Smalltalk it is necessary to be able to create new GEOS classes in Smalltalk and with user written methods in the Smalltalk language.
The PalmOS program is started at one starting point when launched and executes until finished (or in infinite loop). The GEOS program on the other hand does some initialization at start-up and then is event driven by the operating system itself, rather than some get-event/process-event loop (this loop is part of the GEOS kernel, rather than being part of every application). This means that a typical Pocket Smalltalk application would build most of the UI and other GEOS objects at start-up and then simply pass control back to GEOS kernel. As the user controls the UI, the GEOS kernel calls user written methods (in Smalltalk), which once finished pass again control back to GEOS. So in PalmOS, the Virtual Machine loop is started once and executes until the program finishes or crashes, but in GEOS the Virtual Machine is called many times (sometimes even at nested levels) and after executing some methods passes control back to GEOS. It is obviously possible to write start-to-finish programs in Pocket Smalltalk, but it is rather difficult to provide more than very basic UI for these non-event-driven programs.
For linking of Smalltalk to GEOS there are some system primitives, which provide access to various things in GEOS or to components of the Virtual Machine. For instance the Geos>>geosClass:number: primitive returns a CPointer object to the GEOS internal data structure (ClassStruct) used to describe GEOS classes, based on library entry number and library number (GEOS classes are exported from libraries in a similar way to functions through numbered library entries). All the GEOS specific primitives are defined as class methods of the Geos class.
The library entries can be functions with three different calling conventions: Assembly (the parameters and results are passed in processor registers), C (parameters are passed on the stack in reverse order from right to left and removing the parameters is the responsibility of the caller) and Pascal (parameters are passed on the stack in normal order from left to right and are removed from the stack within the called function), global variables and classes. For the C API, only the C and Pascal calling convention functions can be used from Pocket Smalltalk. Most functions are available with both Pascal and Assembly calling conventions and the Assembly calling convention functions cannot be used from C/GOC, so you do not use anything without the possibility to call Assembly calling convention functions.
The Perl script classes.pl generates Smalltalk definitions for all C and Pascal entries in all GEOS libraries as helper methods on the class side of the Geos class. The implementation of the method is to use the "GEOS" instruction of the Pocket Smalltalk Virtual Machine (the idea is based on the PalmOS Pocket Smalltalk SYSTRAP instruction), which has normal Smalltalk syntax of a message with a special receiver GEOS and the first keyword specifying the type of GEOS call (GEOS method, function). For API calls, the first message keyword has to be call:. All other keywords are with: only to separate the arguments from each other.
The last parameter to GEOS call: is a Smalltalk ByteArray constant (using #[v1 v2 ...] syntax) with library number in the first byte (and a flag whether C or Pascal calling convention should be used in the topmost bit, so you can use up to 128 shared libraries in one Pocket Smalltalk application), the entry number in the next two bytes (in the network byte order, so more significant byte first), followed by one byte for each parameter specifying the type of the parameter and followed by the last byte which specifies the type of the return value from the API function call.
The codes for types of parameters are as follows:
Another limitation is that you cannot write callback functions in Pocket Smalltalk and pass them as arguments to certain system functions. So API calls requiring a callback functions cannot be used in Pocket Smalltalk. You would have to code the callback (including the API call to use it) in C or GOC and interface this code to Smalltalk as a primitive or to put your code into a separate shared library and call it using Geos call: from Smalltalk.
Let's use the memcpy function for copying a block of memory as an example. This function is provided by the ansic.geo library and is exported as entry number 44 and uses the Pascal calling convention (you can find this with the printobj.exe tool provided with the Nokia SDK the ansic.ldf file). The definition in the SDK ansi/string.h file is as follows:
void * pascal memcpy(void *__dest, const void *__src, size_t n)That is, the function returns a pointer and takes two pointers and an unsigned 16 bit integer as its arguments. The Pocket Smalltalk definition generated by the classes.pl script is as follows:
memcpy: __dest with: __src with: __n "void *pascal memcpy (void *__dest, const void *__src, size_t __n) in ansi/string.h" ^GEOS call: __dest with: __src with: __n with: #[1 0 44 3 3 1 3]So, the Smalltalk helper method to access the memcpy function from the ansic.geo library has the selector memcpy:with:with: (the first keyword is always the name of the function as in C, all parameters are separated with with: keywords), the names of Smalltalk parameters are taken from the C definition, the C definition is repeated as a comment and the link to the actual library is via the GEOS instruction with the call: selector, all parameters are passed to the GEOS instruction plus the extra parameter defines in a ByteArray that the memcpy function is exported from library number 1, has Pascal calling convention (highest bit of the first byte is zero), is entry number 44 (second and third byte), takes two pointers (3) and one unsigned 16 bit integer (1) as parameters and returns a pointer (3). The value returned from the GEOS instruction is returned as the result of the Smalltalk helper method. To use the Smalltalk method, say we have a Smalltalk String object and a CPointer and want to copy the content of the string to the pointer. To make a usable C string, we have to supply the trailing null byte after the content of the string. We simply do:
Geos memcpy: aCPointer with: aString with: aString size. aCPointer byteAt: aString size put: 0.In our example, we throw away the return value from the function, which is the usual way to use the memcpy function in C.
The GEOS instruction is defined in the Pocket Smalltalk Virtual Machine source file vm.c and you can understand the handling by reading the C source code there. In short, the instruction is followed by a byte with the number of parameters really passed to the instruction (including the spec argument), which is checked against the length of the spec ByteArray for the correct number of parameters. The calling convention, library number and entry number are extracted from the spec argument, all parameters are checked against the spec and converted to C integers or pointers from Smalltalk integer or pointer representation (integers are objects in Smalltalk and therefore are not simply words or doublewords), pushed into a parameter array in a correct order depending on the calling conventions, the actual library entry address is obtained and the function is called via a pointer to function (there is some extra trickery in GEOS for function calls over a pointer, as function pointers do not point to actual functions, but need to be converted to real addresses first). The helper functions are in the file pstgeos.goc. Finally, the returned value is converted to the appropriate Smalltalk object and the execution is finished, returning control back to the main Virtual Machine interpret loop.
Pocket Smalltalk constants are very similar to C #defines, as they are resolved to actual values at compile time and for the Virtual Machine it is irrelevant if the source did say 123 or ##const when ##const is defined as 123. The underscores and capitalization of all enums and #defines is preserved from C, so you can use any constant available to a C program as ##constant. As opposed to C API definitions and GEOS classes, which use individual *.st packages for each library, the file enums.st contains definitions for all the available .h and .goh header files in the whole SDK and therefore for all the libraries. These constants take memory in the IDE, but do not have any overhead at run time, so do not hesitate to use them.
Please note that in C, the C compiler can evaluate constant expressions at compile time, which is something Pocket Smalltalk cannot do (Dolphin Smalltalk has a syntax ##(expression) which is evaluated at compile time, but Pocket Smalltalk does not have this functionality at the moment), so if you write in C something like (GIA_NOT_USER_INITIATABLE|GIA_MODAL), which is evaluated at compile time to a single numerical value (0xa0), but in Pocket Smalltalk you have to write (##GIA_NOT_USER_INITIATABLE bitOr: ##GIA_MODAL), which is evaluated at runtime on the device as (16r80 bitOr: 16r20).
You can browse the enum and #define constants in the Constant browser, under the Geos enums and define constants category (in alphabetical order). There is no way to include a comment with constant definitions, so the information which constant belongs to which type is not available in the Pocket Smalltalk IDE, but you can rely on the naming scheme used in GEOS to prefix the constant with the abbreviated name of the type (GIA for GenInteractionAttrs in our example above).
The Pocket Smalltalk defines a class CStructure, which contains some basic methods to access various fields of the structures, which are referenced by a pointer to the structure. The fields have to be either bytes, words (2 bytes) or dwords (4 bytes) and only numeric values can be extracted or stored in the structure fields. To access a field in the structure, you need to know its offset from the start of the structure and the length of the data type of the field. The CStructure class is not meant to be used directly, but it is meant to be used as a superclass to classes defining different structures. The structs.st files provides definitions to all GEOS system structures and unions.
The GEOS structures and unions are placed one extra level of subclassing deeper, under a class called GCS (short for GeosCStructure), so that user defined CStructures can be easily separated from system structures (remember, there are 937 definitions and 937 subclasses of the GCS class). Due to name conflicts with Smalltalk classes (Rectangle, ...), the GEOS structures and unions are prefixed with GCS followed by their original C name (so that C Rectangle becomes GCSRectangle).
The Pocket Smalltalk definition of each GEOS structure or union contains one class side method called sizeInBytes, which returns the size of the data in the structure or union and in the instance side contains Smalltalk access methods to access and change all structure fields in a way similar to access methods to access named instance variables of Smalltalk objects. So for a C structure Rectangle defined in C as:
/* standard structure for a rectangle */
typedef struct {
sword R_left;
sword R_top;
sword R_right;
sword R_bottom;
} Rectangle;
the Pocket Smalltalk definition would define the class GCSRectangle
with a class side method GCSRectangle class>>sizeInBytes returning
8 (four words of 2 bytes each) and the access methods GCSRectangle>>R_left,
GCSRectangle>>R_left:
and so on. The access methods call the CPointer>>wordAt: and CPointer>>wordAt:put:
methods to actually access the fields within the structure. The offsets
and sizes of data types are computed statically while translating from
.h
and .goh files into Pocket Smalltalk definitions. So the definitions
of the access methods for the R_right field are as follows:
rRight "sword R_right" ^pointer wordAt: 4 rRight: value "sword R_right" pointer wordAt: 4 put: valuePlease note that the C names of structure fields are converted to Smalltalk conventions, that is words are separated with capitalization rather than underscores and instance names/methods start with a lowercase letter. This rule is applied to most names with different conventions in Smalltalk and C or GOC.
The problem are fields which are either arrays or nested structures or unions, as the access methods cannot simply be expressed in terms of methods like wordAt: and wordAt:put:. The current version of the classes.pl program does not try to generate something clever like using memcpy or other functions, as the representation in Smalltalk would be a problem. So to give you the information about the fields existence, the generated access method has only the comment with the C definition of the field, but does not do anything to access or modify the data in the field. The comment includes besides the C definition two numbers, the first is the offset of the start of the field within the structure or union and the second is the size of the field in bytes. You can use this informatio to write your real accessors for the field. Please note that from the all structures and unions, only a few contain nested arrays, structures and unions.
Another problem with converted defintions which need to be manually modified is with structures having arrays as their last field, where the array can have any size for a particular instance of the structure, but the structure definition contains a fixed size array field with only one item in the array (i.e.. char text[1]). As classes.pl things this field is exactly one byte long, it generates the access methods as for a charfield (using byteAt: and byteAt:put:).
There are other subtle problems and limitations with the converted definitions, but listing them all here would take too much time, but as a general rule everything consisting of bytes, words or dwords should work fine, everything else needs manual checking or redefinition of the accessor methods. So far I have not found any bug in my algorithm to compute the offsets and field sizes (and the size of the structure), but as classes.pl does no do a complete C/GOC parsing, but is based only on simple regular expressions, there may be structures or unions with incorrectly computed sizes or field offsets. Please let me know if you find any irregularities of this kind.
The GEOS object system is strongly influenced by Smalltalk (remember: Smalltalk was the original OO language) and other newer object oriented programming languages, but certain aspects of the GEOS class system are quite different from what is used in Smalltalk. The GEOS object system was designed with Assembly programming in mind and extended to GOC, which is an extension to the C language influenced by the language Objective C (not C++, another object oriented extension to the C language). There is therefore no direct mapping of the GEOS objects to Smalltalk objects and the object-oriented features of both systems. The resulting implementation of the mapping is therefore inevitably a compromise between power, memory consumption and speed of execution.
In Smalltalk, all data are objects, even numbers, characters and strings. In GEOS (and C++ and Java, to name some recently popular OO languages), there are "primitive types" such as numbers, which are not objects, and then there are objects, which contain data in C struct-like instances and methods, which act on the data. Most UI objects in GEOS are statically created at compile time and new objects are rarely created in runtime in a typical GEOS application. In Smalltalk, most objects are created in runtime and most operations even on simple values create new temporary objects, which are later reclaimed by the Garbage Collector.
In Pocket Smalltalk, the GEOS objects are typically created in runtime, so the UI is built from scratch after the application starts and does not exist already built in the application geode which is the case with most GEOS applications. For this reason, programming the UI and working with objects in Pocket Smalltalk is different from the usual ways to deal with objects in GEOS. But Pocket Smalltalk provides most of the glue necessary to handle creation and destruction of UI objects in runtime, so you may ignore most of the warnings in the GEOS SDK documentation discouraging anybody to try to create objects dynamically in runtime. In Pocket Smalltalk, that is the only way to work with GEOS objects.
The top of the hierarchy of classes in Smalltalk is called Object and all Smalltalk classes are subclasses of the Object class (well, in Pocket Smalltalk you can have other root classes besides Object, but we can ignore this for discussing GEOS objects). The top of the GEOS object hierarchy is the class called MetaClass (the word Class is always the part of the class name in GOC) and all GEOS classes are subclasses of MetaClass.
For each GEOS class, the classes.pl program generates a helper
Smalltalk class, which enables processing of GEOS objects of that class
in Smalltalk. The top of the helper classes is the Smalltalk class GeosMetaObject
and all Smalltalk helper classes for GEOS classes are subclasses of the
Smalltalk class GeosMetaObject. The Smalltalk helper classes for
GEOS classes are called after the GEOS classes, but without the trailing
word Class. At the moment, there does not seem to be any name clash among
Smalltalk and GEOS class names. So under GeosMetaObject class there
is a single subclass called Meta, which is the Smalltalk helper
class for GEOS MetaClass. All other subclasses of the MetaClass
have their helper Smalltalk classes below Meta.
Please note that while working with Smalltalk helper objects there may be created some temporary objects (such as objects of classes CPointer, CStructure and LongInteger), which will be reclaimed by the Smalltalk Garbage Collector when not needed any more.
The clases.pl program generates enough information for all Smalltalk helper classes for system GEOS classes so that combined with predefined methods in the Smalltalk class GeosMetaObject you can create new GEOS objects including the Smalltalk helper objects by simply sending new to the Smalltalk helper class. The result can be used in a cascaded message send in Smalltalk to further set the new object to the required state.
GEOS objects are not created in one common object memory as in Smalltalk, but are created in so called Object Memory Blocks. Each object memory block in GEOS is owned by one GEOS thread and all objects in that object memory block belong to that thread. While dynamically creating (instantiating) GEOS objects, the system call has to specify in which object memory block should the new object created. In order to be able to use a simple new class message for creating new objects, the Smalltalk helper classes store the object memory block handle for all newly created objects in a class variable ObjBlock in the GeosMetaObject class. A new object block is created and a handle to this block is assigned to the ObjBlock variable in the newObjBlock method. If you need to create more than one object block or to create GEOS objects belonging to other than the UI thread, you need to assign the ObjBlock class variable a different value.
The GEOS accessor methods should be more efficient than the Smalltalk generated accessor methods for instance variables, so unless you want to achieve maximum similarity with the static GOC object definitions, using GEOS messages to set object instance variables should be faster.
The vardata tags are 16 bit integers in GEOS, which have a similar hierarchical system as GEOS message numbers (the message numbers are described in GEOS SDK documentation, the vardata numbering is not explained anywhere). If the vardata item has a value, the value is included after the tag, including the length of the value. This enables to store variable length vardata such as strings to GEOS objects. There are both C style API functions for working with vardata (taking GEOS objects as arguments) and object oriented messages which change or query the vardata when sent to any GEOS object.
In Pocket Smalltalk there are several helpers for each GOC vardata definition. First, there is a Pocket Smalltalk constant with a name derived from the name of the vardata tag (translating names such as ATTR_GEN_HELP_FILE to ##attrGenHelpFile) and the value equal to the vardata tag value (the GOC compiler translates the vardata tags to C #defines, so the vardata tag names are treated as numbers in C). You can use the constant as a parameter to C API functions like ObjVarAddData or to GEOS methods such as MSG_META_ADD_VAR_DATA.
For valueless (void) vardata items which contain only the tag and can be tested for presence or absence in any object, classes.pl creates a helper method with the same name as the vardata tag (in Smalltalk conventions), which adds the vardata item to the receiver GEOS object. Using these methods and cascaded Smalltalk messages, the actual syntax to add various vardata to a certain UI object is nearly identical to that of GOC while defining static objects. So, if one would write in GOC:
@object ComplexMonikerClass AboutBox = {
...
CMI_fontSize = FOAM_NORMAL_FONT_SIZE; /* assign instance variable */
...
HINT_DRAW_IN_BOX;
HINT_DRAW_SHADOW;
HINT_PLACE_MONIKER_TO_LEFT;
...
}
the equivalent dynamic object creation in Pocket Smalltalk would
be:
aboutBox := ComplexMoniker new ... cmiFontSize: ##FOAM_NORMAL_FONT_SIZE; "assign instance variable using PST accessor method" ... hintDrawInBox; hintDrawShadow; hintPlaceMonikerToLeft; ... .For vardata values, there are two generated helper methods. One is the same as for the valueless which simply creates the vardata, but leaves the value equal to all zeros - this method actually returns a CPointer to the vardata value, so the value can be set with other methods. The other generated method uses a keyword message with one parameter (the name is the same as in the first case but the added colon at the end), which creates the vardata and immediately assignes the passed value to the vardata item. For simple values (byte, word, dword), the usual wordAt:put: type of access is used, for structures and unions the passed value is not a value as such, but rather a Smalltalk block (a piece of Smalltalk code used as data and executed on demand), which is passed as parameter the correct GCS (GeosCStructure) to the actual vardata item. The block can contain code to set the individual items such as structure fields of the complex value. As an example, consider the following GOC code:
@object SomeClass SomeObject = {
...
HINT_FIXED_SIZE = {
SST_PIXELS | FOAM_DEFAULT_COMPONENT_WIDTH,
0,
0
};
...
}
with the equivalent Pocket Smalltalk code:
someObject := Some new ... hintFixedSize: [:csha | csha cshaWidth: (##SST_PIXELS bitOr: ##FOAM_DEFAULT_COMPONENT_WIDTH)]; ... .In the above example, the remaining fields of the structure are left as zero without explicitly assigning them a value.
To check for vardata presence, accessing or changing existing vardata
values, you have to use the normal GEOS means for dynamic vardata handling,
there are no automatically helper methods for those purposes. The goal
of the Smalltalk helper methods is to create a syntax similar to that used
while defining static GEOS objects in GOC.
As some objects store message selectors, the constants used to represent messages in GOC are defined as Pocket Smalltalk constants as well so that the message number can be referenced anywhere in the Pocket Smalltalk program. As in GOC all the messages are processed in a flat name space with other constants and names, the names of messages (the selectors to use the Smalltalk terminology) have the prefix MSG_CLASS_what, and the MSG_CLASS_ prefix is dropped while converting the name to Smalltalk conventions, so that MSG_META_ADD_VAR_DATA would be ##addVarData in Smalltalk and the helper method in class Meta would be addVarData.
The GEOS applications can be multithreaded and each GEOS object instance
is stored in one of the object memory blocks. Object memory blocks are
owned by individual threads and messages in GEOS are always processed by
the thread which owns the object memory block, not the thread which has
invoked the message send. Besides calling messages, GEOS can send
messages which means the message is stored in a queue and can be processed
later, while the sending thread continues immediately (in Smalltalk terminology,
there are no message calls and the GEOS call mechanism is called
a message send, but there is no queue and all Smalltalk sends are
processed sychronously). Each GEOS message send or call can have some extra
parameters and message sends can be "recorded" and actually sent
later (saved message sends are called events). In Pocket Smalltalk, these
functions are not possible at the moment, but implementing them is quite
easy and will be done in a short time. But looking at the sample applications
in the Nokia SDK, most applications do not use these features and most
tasks can be achieved with message calls, which is what Pocket Smalltalk
supports at the moment.
In GEOS, besides "normal" classes, there are master classes, which have different internal organisation of the instance variables, but bring no new expressive power to the object model. Master classes enable to save memory, as they break the object instance into several parts (master levels) and only some parts of the object can be installed, while the remaining parts may stay empty. GEOS is able to create the missing master parts more or less automatically. In Smalltalk, if you access the object only by GEOS messages (sent via Smalltalk helper methods), you do not need to worry about which master part is initialized and which is not. If you want to acces the object instance variables below the topmost master level, you should call the initNextLevel method on the Smalltalk helper object to make sure the next master level is initialized (such as accessing the GenXXX level instance variables in an object of class ComplexMoniker. If you need to initialize some master levels below the topmost two levels, you will have to write a helper method yourself.
But there are also variant classes (all variant classes have to be master classes as well, but this is more an implementation issue), which enable to change the strict subclass/superclass relationship and enables for a class to inherit from a whole group of superclasses. The main use of this multiple inheritance in GEOS is in separation of an abstract UI (Generic in GEOS terms) and its visual representation (Specific UI in GEOS terms). All UI in GEOS is described using so called Generic objects (all generic classes are subclasses of the class Gen) and the Generic objects do not contain any information about the visual representation of the user interface elements. The visual representation is described in Visual objects (all visual classes are subclasses of the class Vis), but Gen is a subclass of the class Vis and for each GenXXX class there is some corresponding VisXXX class. But the VisXXX classes are subclasses of Vis, as is the Gen class, so Gen and its subclasses cannot inherit from VisXXX classes under the normal class inheritance model. But Gen is a master and variant subclass of Vis, which means that each instance of Gen or its subclasses will be a subclass of one of the subclasses of Vis. So for a particular object instance the inheritance is straightforward, but for a class, it can inherit from any subclass of the superclass of the variant class. In practice, object instances of subclasses of Gen can have different instance variables, depending on which of the Vis subclasses is the superclass of the object, and respons to a different set of messages with potentially different behaviour, again depending on the acutal superclass.
The selection of the actual superclass (ie. which VisXXX class will be the superclass of a particular GenXXX instance) is either automatic (the system knows which classes belong to each other and fills in the link in runtime when needed, this is the case with most Generic objects), or manual, that is the object has to specify its superclass before being fully usable (this is the case of the Nokia Communicator UI class ComplexMoniker). If the class is filled in manually, it is performed by assigning an instance variable with the same name as the variant class a value, which is a pointer to the ClassStruct of the actual superclass of the variant class.
The GEOS object system uses a very efficient way to handle variant classes, both for data storage and for lookup of methods for messages, and uses smart tricks to magically create the automatic links just in time when the first message is sent which needs to cross the boundary of the variant class.
As this system is more powerful than the simple Smalltalk model of class inheritance, it is not trivial to model it using the helper Smalltalk classes. As all the helpers in Smalltalk are methods (accessors, vardata creation, messages), it is sufficient to be able to change the Smalltalk message lookup algorithm so that the helper methods in Smalltalk helper classes are being looked up in the same way as in GEOS. If we relax our requirements that the lookup has to be exactly the same, there is a relatively easy way to map the GEOS functionality to that of Smalltalk helper classes. If we make the Gen helper class a simple subclass of the Vis helper class, then any object of any subclass of Gen can handle all messages properly, except those defined in other subclasses of Vis. The Smalltalk message lookup for messages defined in Vis classes therefore fails. In Smalltalk if a message is not understood by any superclass of the class of the object, the object is sent doesNotUnderstand: message with the original message including arguments as its parameter. This message is normally handled in Object, which produces some error and starts a debugger or does some other action. But as doesNotUnderstand: is a normal Smalltalk method, any class can provide its own definition and override the behaviour defined in Object. So for all the GEOS variant classes, we define doesNotUnderstand: in such a way that if the message is not understood through the normal inheritance, we resend it through the actual superclass, but with the same underlying GEOS object. As we cannot change the class temporarily (we can change classes permanently in Smalltalk, but it is not a good idea to do this for multiple inheritance), we have to keep the information about the superclass within the Smalltalk helper object and to create a temporary Smalltalk helper object of the actual superclass (but with the same GEOS object referenced by optr) and send the message to the temporary object.
This mapping can be broken deliberately, but it does not seem to be broken within the existing GEOS system classes. As the Smalltalk methods do not do any real work, but merely exist to map the Smalltalk message sends to GEOS message numbers (which are hierarchical depending on where the class is in the class inheritance tree) and other information required to translate the call to a proper GEOS message call, the doesNotUnderstand: trick should handle all the existing classes in GEOS. After the information is looked up in Smalltalk, the GEOS message is sent using the GEOS method lookup rules to the original GEOS object (which means that there are two very similar message lookups, one using Smalltalk selectors in Smalltalk Virtual Machine and one using message numbers in GEOS kernel).
The only restriction on using GEOS variant classes through the Smalltalk helper classes is in the fact that the Smalltalk helper classes cannot automatically infer the correct actual superclass, so for objects which need to handle messages for superclasses outside the direct line of ancestors you have to define the superclass manually even in cases which are handled automatically by GEOS. The only example I could find so far where this is necessary is with the GenText class, as you will usually need to use not only the MSG_GEN_TEXT_xxx messages, but also the MSG_VIS_TEXT_xxx messages as well and therefore you have to link all instances of the GenText Smalltalk helper class to the VisText actual Smalltalk helper superclass. Besides the link at the GEOS object level, the Smalltalk helper classes for GEOS variant classes define link to the actual superclass. As filling in the actual superclass at the GEOS level for Gen objects breaks their functionality (except the ComplexMoniker class, where the link to the underlying Gen class has to be both at the Smalltalk helper level and the GEOS object level), there are two ways to make the link in Smalltalk helper classes: to set only the Smalltalk helper link and to transfer the Smalltalk helper link to the underlying GEOS object with a setVariant message.
To illustrate this rather abstract discussion, we can use the following example. While creating an instance of the GenText class, we set the link to the VisText superclass at the Smalltalk helper level, but leave the GEOS system to fill in this link automatically. The actual code is as follows:
Gen>>gen: gen: aClass "create another helper Smalltalk object with the same optr, but belonging to the actual superclass" gen := aClass basicNew optr: optr Gen>>setVariant setVariant "propagate the link to the actual superclass to the GEOS object level" self dwordAt: 0 put: gen class classPtr asInteger Gen>>doesNotUnderstand: doesNotUnderstand: aMessage "if the message cannot be handled in the straight inheritance, send it using a different helper class" ^gen perform: aMessage selector withArguments: aMessage arguments ... example use ... aText := GenText new gen: VisText; gtxiText: ... result := aText textGetAll. "this is a VisText message, which needs the gen helper method"Please see the Pocket Smalltalk demo application use of different ComplexMoniker superclasses as an example of propagating the actual superclass to the GEOS level.
While defining UI objects statically in GOC, the programmer can desribe the tree-like relations by specifying the full list of children at the parent level. The syntax for listing the children is as follows:
GI_comp = @child1, @child2, @child3;The GOC compiler generates all the necessary child, neighbour and parent links automatically. This can be used only for statically defined objects. For dynamically created objects, one has to use GEOS messages defined in the class which contains the instance variables for the linking. In GEOS there are two tree-like hierarchies, one is for Vis objects (linking the visual representations of the UI) and one is for Gen objects, linking the logical relations between the UI objects. As the Vis objects are rarely manipulated directly (remember, every Gen class is a superclass of some Vis class), the application programmer usually needs to worry only about the Gen objects and their relationships. The messages to manipulate the tree-like relations of Gen objects are defined in the class Gen which is inherited by all Gen subclasses.
While creating the trees to describe UI objects in Smalltalk, for simple applications you will need to add items to their parents one by one to create the tree for dialog windows and then you will need to be able to destroy the whole branch of the tree when you are finished with the dialog. You will not need any other functionality for simple applications.
Adding Gen objects to other Gen objects is done by the GEOS message MSG_GEN_ADD_CHILD (with an automatically created Smalltalk helper method addChild:with:). All Gen objects have a state information and can be enabled or disabled and usable or not usable. To attach a Gen object to a generic tree, the object has to be set to non-usable. But to use it, it has to be set to usable. For this reason, Pokcet Smalltalk defines a helper method called addUsable:, which does the two usual steps together - attaches the object to a specified generic tree and sets the usable flag (the enabled flag should be set while creating the object). The typical scenario for building the generic tree is to use the Smalltalk cascaded messages syntax to create and setup the object and to attach the children one by one in another embedded cascaded message send. As opposed to GOC where you need to name all the objects at all levels in order to be able to describe the relations, in Pocket Smalltalk you need to define local variables and to assign references to UI objects only for those objects which need to be manipulated directly later (as for retrieving the current value from UI objects), purely organizational and descriptive UI objects do not need to be named. Pocket Smalltalk supports a syntax where you can make an assignment in the middle of a hierarchy cascaded message sends, so you can build the whole tree including creation of the required references in local variables in one large hierarchical message send. You can see an example of the programming style in the gdemo.st dialogs and to compare them with the GOC definitions in the settings sample Nokia SDK application.
The top of the GEOS generic tree for an application needs to be referred to in the application object, so that the system can launch a separate UI thread and to start to process the generic objects in this thread. The top of the generic tree is an instance of the GenPrimary class and the instance is created in the Pocket Smalltalk Virtual Machine application. In order to link your dunamically created generic objects to the application's generic tree, you need the optr to the instance of the GenPrimary class. There is a Smalltalk primitive method which returns the optr to the instance of GenPrimary. This method is called in GeosMetaObject class>>newObjBlock and saved in a class variable PSTPrimary for further reference. You can therefore send the addUsable: message to PSTPrimary in order to attach your GenInteraction instances for dialog windows to the existing generic trees.
To be able to create real life applications, the Pocket Smalltalk system has to be able to create GEOS subclasses of GEOS system classes and to defined methods in Smalltalk for these new classes for system generated messages. Also, many UI components communicate with each other by sending user defined messages to each other, so there has to be the possibility to define new messages and to write methods in Smalltalk to handle them.
The classes.pl program generates Smalltalk glue code, which creates a Smalltalk class for each existing GEOS class, so that GEOS objects are represented in Pocket Smalltalk as Smalltalk objects with a single instance variable, which contains a link (in GEOS lingo these are called OPTRs - Object Pointers) to the underlying GEOS object. The GEOS classes exist in GEOS before any Smalltalk application is started. Application specific classes which are subclasses of system classes are defined in GOC and created statically at compile time (i.e.. the GOC compiler builds the ClassStruct data structure for the class and the class itself usually does not change while the application runs). In Pocket Smalltalk, you can easily define a Smalltalk subclass of the Smalltalk (helper) class for the GEOS system class, but you cannot statically create the GEOS data structures for the new GEOS class. So for Smalltalk defined GEOS classes, you have to dynamically build the class during the application initialization time and only once the GEOS class for the Smalltalk class is defined, you can start to make and use objects of this new GEOS class (in GEOS or Smalltalk).
The required ClassStruct (which has other arrays and structures immediately following the basic ClassStrcuct) is built using a helper Smalltalk method GeosMetaObject class>>initializeSubclassHandling:, which takes an Array of Associations, which map GEOS message numbers (as Smalltalk constants, i.e.. something like ##kbdChar) to Smalltalk message selectors (as Smalltalk symbols, i.e.. something like #kbdChar:with:with:). The Smalltalk instances of Smalltalk objects point to the underlying GEOS obejcts, but for system classes there is no need to have the opposite link from the GEOS object to the glue object in Smalltalk. For GEOS classes defined in Smalltalk, such a link is required, so that the Virtual Machine function knows which Smalltalk object should be sent the Smalltalk message equivalent to the GEOS system message. For this reason, the Smalltalk defined subclasses of GEOS classes always have to have at least one more instance variable, which contains a 16 bit object pointer pointing to the Smalltalk object instance for the underlying GEOS object (i.e.. the Smalltalk object points to the GEOS one and vice versa). This instance variable has to be the last word of the GEOS object instance. The GeosMetaObject class>>new method fills in this instance variable automatically for each new object created, so its existence is mostly hidden from the Smalltalk programmer. The only place where its existence is important is the fact that each Smalltalk defined subclass of GEOS system class has to define two more class side methods for the Smalltalk class: SomeClass class>>isInSmalltalk which has to return true to tell the new method to fill in the reverse pointer (this method is defined at GeosMetaObject class>>isInSmalltalk as returning false, from where it is inherited in all GEOS system classes, and the classes defined in Smalltalk have to override this) and SomeClass class>>instanceSize, which has to define the instance size (for this master level) for the new subclass. Unless you want to include some more instance variables, this will be always the size of the GEOS system superclass instance plus 2 bytes for the backpointer with the Smalltalk object pointer. So, for nearly all Smalltalk defined classes, you should use the definition: ^self superclass instanceSize + 2 (please note that this has to be written this way and not ^super ..., which would try to fetch the instance size from the yet undefined ClassStruct structure for the class just being defined). To make things easier, there is a helper method GeosMetaObject class>>superInstanceSizePlus2, so that you can define SomeClass class>>instanceSize simply as ^self superInstanceSizePlus2.
GEOS system generated messages never return any value, so there is no transfer of the return value from Smalltalk back to GEOS (if someone finds an example where this is not true, I will fix this easily). The Virtual Machine level function which translates GEOS messages to Smalltalk messages can handle at this moment only messages with 0 to 3 word (16 bit) arguments passed in the CX, DX and BP registers. This handles nearly all system generated messages and with a bit of effort handles also messages with one 32 bit argument and possibly one more 16 bit argument, by combining the values in 16 bit registers together. The reason is that this can be coded in C, anything better needs to be coded in Assembly, which will take some more time for me to write and debug (plus to understand the kernel code wich actually passes the message to the message handler). The C language handler function passes 0 to 3 arguments to the Smalltalk method as integers from the CX, DX and BP registers (well, they are actually on stack, but the important part is how they are defined in the GOC files for which registers), based on the number of arguments of the Smalltalk method (the first argument would be the CX, the second DX and the third BP). In most cases, the only thing you need to do is to have a look at the GEOS definition of the message in the Smalltalk glue method (which contains a copy of the GEOS GOC message definition as a comment), find out how many parameters there are and to write a Smalltalk method for this message.
For the moment, the main limitation is in the fact that the C handler for system messages always calls the system superclass handler after the Smalltalk language handler and with the same parameters as the original system generated message. This means you can react with your own functionality to system generated messages, but you cannot prevent the system from doing the default action or to change the behaviour with passing different parameters and you cannot change the order that the Smalltalk code is executed first and the default processing is done afterwards. In the next version of Pocket Smalltalk you will have to call the default processing yourself as you would do in GOC, but at the moment this is not possible and it is not worth of delaying the realease of this version of Pocket Smalltalk.