The main idea of the SWT library is such that its internal implementation on the certain platform is made in the terms of that platform, directly using its native API through the JNI interface, so the look'n'feel comes close to the regular platform applications (i.e. usability; other important benefits of such approach are: easiness, stability, performance). This means that there is a different implementation of the basics of SWT for each supported platform; only the public interface of SWT classes is, of course, the same. So the term "porting" in this case means rewriting the most part of the internal logic from scratch (sometimes including the whole classes) rather than just correcting it for the specific OS. Therefore we have chosen to start from the very beginning and go possibly like original Eclipse developers did, from the basics (the top of the class hierarchy) to details, creating the java and the native code in parallel. The best way to do it is to divide our work into little steps (tasks): every of them is the next level of the implementation and can be tested regardless of other parts that are to be implemented on the next levels.
Technically, SWT sources consist of two parts: one is written in Java, another is written in C and contains native methods that communicate with Java code through JNI.
The table below represents basic subcomponents of SWT as they were
defined by the original developers. There's a separate directory for each
subcomponent in the structure of the Eclipse sources. Packages with names
in bold are platform specific (i.e. have their own implementation
on each platform), other are common for all platforms (but may require
some adaptation for a certain OS). o.e.swt
here and further
is a short form of org.eclipse.swt
, for convenience.
Name | Description | Packages | Prognosis |
---|---|---|---|
SWT Base | SWT Base. Contains the major part of all public classes and interfaces. Implementations of many basic classes (Display, Widget etc.) are individual for each supported platform. | o.e.swt.events o.e.swt.graphics o.e.swt.internal o.e.swt.internal.image o.e.swt.layout o.e.swt.widgetsefwef o.e.swt.graphics o.e.swt.internal o.e.swt.widgets |
Platform dependent parts need to be almost fully rewritten for OS/2 |
SWT PI | SWT Platform Implementation. Contains platform dependent internal implementations of SWT interface, including Java wrappers for platform native API data structures. | o.e.swt.internal o.e.swt.internal.<xxx> |
Needs to be almost fully rewritten for OS/2 |
SWT AWT | It's an experimental class (works currently only on Win32 and has some limitations) to make it possible to embed AWT widgets to SWT layout. | o.e.s.internal.awt.<xxx> | Can be implemented in the future? |
SWT Accessibility | Accessibility support. | o.s.swt.accessibility o.s.swt.accessibility |
Is it supported natively by OS/2 or should be emulated? |
SWT Custom Widgets | Standard custom widgets library | o.e.swt.custom | Does not contain any platform dependent code? |
SWT Drag and Drop | Public interface for D'n'D support | o.e.swt.dnd o.e.swt.dnd |
Platform dependent implementations need to be rewritten (partially ported from Win32?) for OS/2 |
SWT Printing | SWT printing capabilities | o.e.swt.printing o.e.swt.printing |
Needs to be almost fully rewritten for OS/2 (not a lot of work to do) |
SWT Program | OS specific program launching aspects | o.e.swt.program | Just one single class <) |
SWT OLE | OLE support | o.e.swt.ole.win32 o.e.swt.internal.ole.win32 |
Can the similar one be implemented for OS/2 in the future? |
C sources of native methods are organized in the following way:
org.eclipse.swt.internal.Callback
class, that allows using regular Java methods as callback functions by
the platform native API.Native methods serve as a bridge between SWT and underlying platform
API. One of Eclipse principles is that such mapping should be transparent
as much as it possible. It means that Java methods have exactly the same
name and behavior as their native counterparts, including the type and the
list of arguments and the return value. For that purposes there are Java
wrappers exist for native structures used by platform API functions to
exchange information with the application. These wrapper classes are
defined in the SWT PI part as well as the o.e.swt.internal.OS
class containing (almost?) all of native API-to-Java mappers. But due to
the fact that Java semantics differ from C there cannot be the full
equivalence between C and Java types, so some considerations about mapping
should be taken into account.
[unsigned] int
,
[unsigned] long
and derived) are mapped to Java
int
type.boolean
type.[unsigned] short
(and derived) is
mapped to Java short
type.[unsigned] char
(and derived) is
mapped to Java byte
type....
int*
is mapped to int[]
). There's one exception with ASCIIZ
strings (see below).int
type.int
array (see below).There's a special case with pointers to arrays and ASCIIZ strings, which are returned by some platform API calls to represent some data of variable length. There are few situations when such data is not copied to the storage area provided by the caller, but the pointer to the internal storage area is returned. In these situations we cannot allocate the storage for that data in Java before a call to a native method, because we don't know its size. The solution can be to create the corresponding Java array object inside of the C implementation of the native method after an API call (when we already know the size of the data returned) fill it and return the reference to this object to the Java caller.
Also there are sutiations when an API call returns a pointer to a
structure from its internal storage and the user should be able to access
this data directly to change some value (the good example is
DosGetInfoBlocks()
). Since a Java structure wrapper class
contains a copy of the original data it doesn't make sence to use it as a
parameter to call such API -- it's better to return a pointer to the
caller "as is" using a reference to one-element array of int
type to use it for the direct access. But Java cannot read/write directly
to the specified memory address, so we need some helper functions to copy
data from a wrapper class directly to a given memory location and back.
These functions are overloaded versions of
o.e.swt.internal.pm.OS.objcpy()
. They take two arguments: the
destination (the first one) and the source (the second one) of data to be
copied. One of them is a reference to a wrapper class and another is an
int
value containing the address of (the pointer to) the
corresponding C structure. So, to the sequence of operations to get the
required result can be:
int
arrayOS.objcpy()
to copy data to the object of the
wrapper classOS.objcpy()
to copy data back from the object to
the memoryNote. These helper functions are potentionally dangerous and
should be used very carefully since there's no way to check whether a
given pointer really points to the structure of the appropriate type or
not. However, if zero or null
is passed as one of arguments,
an SWTError
exception will be thrown (this behavior depends
on the DEBUG_CHECK_NULL_EXCEPTIONS
macro definition during
compilation of the SWT DLL).
ASCIIZ strings are mapped using references to the special Java wrapper
class: o.e.swt.internal.pm.PSZ
. String data is actually
stored in its bytes
field which is of byte[]
type. This class provides automatic appending of zero byte to the array
when constructed from Java String
type and removing that byte
when converted back to String
. Convertion between Unicode and
8-byte ASCII characters is done using the default system codepage. There
are helper functions to handle this wrapper in C:
getPSZArray()
, getPSZBytes()
and
releasePSZBytes()
, see their description at the top of
structs.h.
C structures are mapped to Java classes (wrappers) with the same name
and fields (their order and names). Primitive fields' types are mapped
according to the rules above. For every structure the following C helper
functions are defined: cacheXXXFids()
(and the correspondig
XXX_FID_CACHE
structure) to cache Java field IDs,
getXXXFields()
to fill the native C structure with values
from the Java wrapper structure before a call to platform API routine and
setXXXFields()
for backward operation after a call.
If some field of the structure is itself a structure or a pointer to a structure, there are two ways to handle this situation:
xxx_yyy
, where xxx
is the
name of the field representing the nested structure and yyy
is substituted with field names of that nested structure. It's useful
when the structure has few fields. As an example, see the
o.e.swt.internal.pm.QMSG
wrapper class.getXXXFields()
and
setXXXFields()
must correctly handle this case. For
example, see TIB
and TIB2
wrappers and their
getters/setters (though TIB2
does not actually need to be
used as a whole, it was done so for clarity). Note that such nested
wrapper is newed (via the initializer expression) in the parent
wrapper's implicit constructor.Some structures have the special field (cb
or
cbFix
) that should be set to the length of the structure
before passing it to a function. In Win32 version this is achieved by
adding an extra field (sizeof
) to Java wrapper of every
structure and then assigning its value to the length field when using the
structure from Java code. In OS/2, this is done not at the Java level, but
in the code of the structure's getter function. Therefore, the need in the
sizeof
field is eliminated (tough,some strucrures will have
this field because there are some cases when arrays of that structures are
given us as a pointer from the C code and we need to know the address of
the particular structure in the array). Also, Java wrappers in OS/2 do not
contain the cb
or cbFix
field since it's useless
from the point of view of data the structure represents.
Since it's too complicated (in particular, requires two transformations
and two times more memory) to convert arrays of C structures to arrays of
their Java wrappers and back, such arrays are mapped to arrays of Java
int
type, where every one or more elements of that array
correspond to a field from the structure, consequtively. Of course, it's
suitable only for structure types where all fields have the size which is
multiple of 32-bit int
type, such as POINTL
and
RECTL
. This is primarily used in Gpi*
API calls.
Hopefully, at the present time there is no need to work with other types
of structures.
For convenience, parameter names in the declaration (in Java) and in the definition (in C) of native methods should be the same as in the toolkit documentation.
SWT compilation is driven by the build.xml located in the src\plugins\org.eclipse.swt.pm directory of the repository. It should not be used directly since there are some prerequisites that are set up from the main build file.
Ant includes in compilation every .java file it finds in the specified source directory. Since we go class by class, but use complete common source directories from the original distribution, we need a way to narrow a list of files to compile. Otherwise we will get many errors because of missing platform dependent classes (which are not yet ready) referenced from common sources during compilation. To exclude temporarily the whole common directory is not a good idea because we might need some classes from there. So, there is a special file called classes.inc in the same directory as the SWT build file that contains a list of all files to be compiled. Paths there are relative to the source directories, so every entry there is just a full name of a class but with dots replaced by slashes in the package name. Although file masks can be used there it should be done carefully because file mask will apply to every source directory (we cannot change this behavior in Ant) and unnecessary files can be included.
Below is the plan and the progress of the SWT porting process. Each step number and objective is a link to the corresponding step description. The names of classes here, in the step descriptions and in some other places follow the rule: the text in [square brackets] after the class name shows the name of the SWT part the class belongs to (according to the table above) and the sharp (#) sign before it means that the class is platform specific, otherwise it is common.
# | Objective | Started | To be done | Actually done |
---|---|---|---|---|
1 | Initial moves. CVS setup, dummy
Device [#Base] implementation. |
2002-10-21 | 2003-01-15 | 2003-01-21 |
2 | Basics of GUI, part 1. Partially
implement the basic hierarchy from the Widget to
Shell . |
2003-01-24 | 2003-02-24 | 2003-03-11 |
3 | Basics of GUI, part 2. Implement drawing facilities | 2003-03-12 | 2003-04-15 | 2003-07-11 |
4 | Layout basics. Implement the basics of the layout management. | 2003-03-12 | 2003-04-15 | 2003-07-11 |
5 | Font handling. Implement font handling. | 2003-07-12 | 2003-08-01 | 2004-02-29 |
6 | Image handling. Implement Image handling | 2003-07-22 | 2003-08-15 | 2003-11-24 |
7 | Buttons. Finish the implementation of
the Button widget |
2004-03-01 | 2004-03-10 | 2004-10-31 |
8 | Standard dialogs. Implement standard
dialogs (Dialog derivants). |
2004-12-09 | ?? | |
9 | Labels. Implement the Label
class. |
2004-12-09 | ?? |