| <!doctype html public "-//w3c//dtd html 4.0 transitional//en"> | |
| <html> | |
| <head> | |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
| <title>SWT: The Standard Widget Toolkit</title> | |
| <link rel="stylesheet" href="../default_style.css"> | |
| </head> | |
| <body> | |
| <div align="right"> | |
| <font face="Times New Roman, Times, serif" size="2">Copyright | |
| © 2001 Object Technology International, Inc.</font> | |
| | |
| <table BORDER=0 CELLSPACING=0 CELLPADDING=2 WIDTH="100%" > | |
| <tr> | |
| <td ALIGN=LEFT VALIGN=TOP COLSPAN="2" BGCOLOR="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF"> Eclipse | |
| Corner Article</font></font></b></td> | |
| </tr> | |
| </table> | |
| </div> | |
| <h1> | |
| <img SRC="idea.jpg" height=86 width=120 align=CENTER></h1> | |
| <center> | |
| <h1> | |
| SWT: The Standard Widget Toolkit</h1></center> | |
| <center> | |
| <h3> | |
| PART 1: Implementation Strategy for Java™ Natives</h3></center> | |
| <center><i>The first in a series of articles about the design ideas behind | |
| SWT.</i></center> | |
| <blockquote><b>Summary</b> | |
| <br>SWT is the software component that delivers native widget functionality | |
| for the Eclipse platform in an operating system independent manner. | |
| It is analogous to AWT/Swing in Java with a difference - SWT uses a rich set of native | |
| widgets. Even in an ideal situation, industrial strength cross platform | |
| widget libraries are very difficult to write and maintain. This is | |
| due to the inherent complexity of widget systems and the many subtle differences | |
| between platforms. There are several basic approaches that have helped | |
| significantly to reduce the complexity of the problem and deliver high | |
| quality libraries. This article discusses one of them, the low level | |
| implementation techniques used to implement SWT on different platforms. | |
| Examples are drawn from the Windows® and Motif implementations. | |
| <p><b>By Steve Northover, OTI</b> | |
| <br> | |
| <font size="-1">March 22, 2001</font> </p> | |
| </blockquote> | |
| <p> | |
| <hr WIDTH="100%"> | |
| <h3> | |
| Portable and Native - It Can't Be Done!</h3> | |
| Developers demand portable graphics and widgets to allow them to build | |
| user interfaces that are competitive with shrink-wrapped applications built | |
| using platform specific tools. They need access to platform specific | |
| features, with well defined API boundaries. SWT delivers this functionality | |
| using a small and consistent API. This API is implemented on different | |
| platforms using a combination of Java code and JNI natives specific to | |
| each platform. | |
| <p>SWT is implemented entirely in one language: Java. How can this | |
| be true when SWT uses native widgets that provide an API in C? The | |
| answer is that Java provides a native interface to C (JNI) that is used | |
| by SWT to invoke the operating system from Java code. JNI is the | |
| standard mechanism used by all Java programs to invoke code written in | |
| C. SWT goes one step further by enforcing a<i> one-to-one mapping</i> between | |
| Java native methods and operating system calls. The fact that this | |
| mapping is strictly enforced is one of the most critical factors in the | |
| success of SWT. | |
| <h3> | |
| A Tale of Two Implementations</h3> | |
| Let's take a look at the implementation of SWT <i>Text</i> widget on two | |
| different platforms. The <i>Text</i> widget provides the ability | |
| to set the selection. SWT application code that uses this API might | |
| look something like this: | |
| <p><tt> /* Select positions 2 to 5 */</tt> | |
| <br><tt> text.setText ("0123456780");</tt> | |
| <br><tt> text.setSelection (2, 5);</tt> | |
| <p>The method signature for <b>setSelection</b> in class <i>Text</i> looks | |
| like this: | |
| <p><tt> /**</tt> | |
| <br><tt> * Sets the selection.</tt> | |
| <br><tt> * <p></tt> | |
| <br><tt> * Indexing is zero based. The range of | |
| a selection is from 0..N</tt> | |
| <br><tt> * where N is the number of characters in the | |
| widget.</tt> | |
| <br><tt> */</tt> | |
| <br><tt> public void setSelection (int start, int end)</tt> | |
| <p>Here is the Windows implementation of <b>setSelection</b>: | |
| <p><tt> public void setSelection (int start, int end) | |
| {</tt> | |
| <br><tt> OS.SendMessage (handle, | |
| OS.EM_SETSEL, start, end);</tt> | |
| <br><tt> }</tt> | |
| <p>What are <b>SendMessage</b> and <b>EM_SETSEL</b>? Windows programmers | |
| recognize this right away. It's <b>SendMessage</b>, the mechanism | |
| that is used to talk to Windows controls. <b>EM_SETSEL</b> is the message | |
| that tells the text control to set the selection. It's not easy reading, | |
| but it is familiar to a Windows programmer. The rest of us get to | |
| read the Microsoft® Developer Network (MSDN) Library! | |
| <p>Here is the Java code for the SWT class <i>OS</i> on Windows: | |
| <p><tt> class OS {</tt> | |
| <br><tt> public static final | |
| int EM_SETSEL = 0xB1;</tt> | |
| <br><tt> public static final | |
| native int SendMessage (int hWnd, int Msg, int wParam, int lParam);</tt> | |
| <br><tt> ...</tt> | |
| <br><tt> }</tt> | |
| <p>How is the <b>SendMessage</b> native implemented? Here is the | |
| C code on Windows: | |
| <p><tt> JNIEXPORT jint JNICALL Java_org_eclipse_swt_internal_win32_OS_SendMessage__IIII</tt> | |
| <br><tt> (JNIEnv *env, jclass that, jint | |
| hWnd, jint Msg, jint wParam, jint lParam)</tt> | |
| <br><tt> {</tt> | |
| <br><tt> return (jint) SendMessage((HWND)hWnd, | |
| Msg, wParam, lParam);</tt> | |
| <br><tt> }</tt> | |
| <p>Notice that <i>the only thing this native does</i> is pass the Java | |
| call straight on through to the Windows API. But what about other operating | |
| systems? Does the fact that <b>setSelection</b> is implemented in | |
| terms of the Windows API mean that SWT is not portable? While it | |
| is true that the Windows implementation of <i>Text</i> is not portable, | |
| application code that uses <i>Text</i> is. How is this achieved? | |
| SWT provides a different <i>Text</i> class for each platform, but the signature | |
| of every public method is the same. Java code that calls SWT does | |
| not know or care which <i>Text</i> class is referenced at run time. | |
| <b>SendMessage</b> | |
| is not SWT API. | |
| <p>Here is the implementation of <b>setSelection</b> on Motif (Java and | |
| JNI C): | |
| <p><tt> public void setSelection (int start, int end) | |
| {</tt> | |
| <br><tt> int xDisplay = OS.XtDisplay | |
| (handle);</tt> | |
| <br><tt> if (xDisplay == 0) return;</tt> | |
| <br><tt> OS.XmTextSetSelection | |
| (handle, start, end, OS.XtLastTimestampProcessed (xDisplay));</tt> | |
| <br><tt> OS.XmTextSetInsertionPosition | |
| (handle, end);</tt> | |
| <br><tt> }</tt> | |
| <p><tt> class OS {</tt> | |
| <br><tt> public static final | |
| native void XmTextSetSelection (int widget, int first, int last, int time);</tt> | |
| <br><tt> public static final | |
| native int XtLastTimestampProcessed (int display);</tt> | |
| <br><tt> public static final | |
| native void XmTextSetInsertionPosition (int widget, int position);</tt> | |
| <br><tt> public static final | |
| native int XtDisplay (int widget);</tt> | |
| <br><tt> ...</tt> | |
| <br><tt> }</tt> | |
| <p><tt> JNIEXPORT void JNICALL Java_org_eclipse_swt_internal_motif_OS_XmTextSetSelection</tt> | |
| <br><tt> (JNIEnv *env, jclass that, jint | |
| widget, jint first, jint last, jint time)</tt> | |
| <br><tt> {</tt> | |
| <br><tt> XmTextSetSelection((Widget)widget, | |
| first, last, time);</tt> | |
| <br><tt> }</tt> | |
| <p><tt> ...</tt> | |
| <p>What are <b>XtDisplay</b>, <b>XmTextSetSelection</b>, <b>XtLastTimestampProcessed</b> | |
| and <b>XmTextSetInsertionPosition</b>? They don't mean much to a | |
| Windows programmer, but they are familiar to anyone who has ever programmed | |
| Motif. Now it's the Windows programmer's turn to consult the Motif | |
| man pages! | |
| <p>The example code above was taken directly from SWT but has been simplified | |
| by removing range and error checking code for the sake of the example. | |
| However, the code that is doing the real work - setting the selection - | |
| is identical to that found in the product. | |
| <h3> | |
| One to One Mapping - No Custom Natives</h3> | |
| Take a moment to review the Java and C code for <b>setSelection</b> in | |
| the previous section. Wouldn't it be easier to implement one <i>Text</i> | |
| class for all SWT platforms and hide the platform differences in the natives? | |
| Such an implementation might look like this: | |
| <p><tt> public void setSelection (int start, int end) | |
| {</tt> | |
| <br><tt> nativeSetSelection (start, | |
| end)</tt> | |
| <br><tt> }</tt> | |
| <br><tt> static final native void nativeSetSelection (int | |
| start, int end);</tt> | |
| <p><tt> JNIEXPORT void JNICALL Java_org_eclipse_swt_widgets_text_nativeSetSelection</tt> | |
| <br><tt> (JNIEnv *env, jclass that, jobject | |
| widget, jint first, jint last)</tt> | |
| <br><tt> {</tt> | |
| <br><tt> #ifdef WINDOWS</tt> | |
| <br><tt> HWND hWnd = SWTGetHandleFromJavaWidget | |
| (widget);</tt> | |
| <br><tt> SendMessage(hWnd, Msg, | |
| wParam, lParam);</tt> | |
| <br><tt> #endif</tt> | |
| <br><tt> #ifdef MOTIF</tt> | |
| <br><tt> Widget *w = SWTGetHandleFromJavaWidget | |
| (widget);</tt> | |
| <br><tt> Display xDisplay = XtDisplay | |
| (w);</tt> | |
| <br><tt> if (xDisplay == NULL) | |
| return;</tt> | |
| <br><tt> XmTextSetSelection (w, | |
| start, end, XtLastTimestampProcessed (xDisplay));</tt> | |
| <br><tt> XmTextSetInsertionPosition | |
| (w, end);</tt> | |
| <br><tt> #endif</tt> | |
| <br><tt> }</tt> | |
| <p>Isn't this easier than having a different <i>Text</i> class on each | |
| platform? The answer is a resounding "<i>No</i>". Why? | |
| In the case of the <i>Text</i> widget, the code to set the selection is | |
| pretty simple but even this causes problems. Before we get into the | |
| discussion, consider this: | |
| <ul> | |
| <li> | |
| The non-public native interface must be identical on all platforms. | |
| This means there needs to be an implementation of <b>nativeSetSelection</b> | |
| everywhere. We also still need to provide the public SWT API. | |
| So we need to implement <b>setSelection</b> everywhere. Why would we want | |
| to write two portable APIs instead of just one? One API is hard enough | |
| to specify and maintain!</li> | |
| <li> | |
| Java is a powerful high level language with features that promote robust | |
| code and program stability. It contains reusable class libraries | |
| for high level data types such as hash tables and vectors as well as efficient | |
| primitive types. Why use C?</li> | |
| </ul> | |
| Calling the operating system directly from Java helps with debugging. The | |
| following problem occurred in an early version of SWT for Windows: | |
| when the selection was set in the text widget, sometimes the widget did | |
| not scroll to show the i-beam. Where was the problem? The code | |
| that demonstrated the problem was complicated, but it was clear from stepping | |
| through the Java code and consulting the MSDN Library that the Java implementation | |
| of <b>setSelection</b> was correct. In fact, because of the <i>one-to-one | |
| mapping</i> between our Java natives and C, it was possible to write a | |
| simple C example to help isolate the problem and submit a bug report to | |
| Microsoft. Why was this so easy? Because, as we have said before, | |
| nothing extra ever happens in an SWT native. The Java call is passed right | |
| on through to the operating system. This means that C code is guaranteed | |
| to exhibit the same behavior. This is great news for debugging and | |
| maintenance. | |
| <p>Performance problems are legendary in widget toolkits and finding them | |
| is a black art. Where is the performance problem? Is it in | |
| the Java code or the natives? Fortunately, SWT natives can't be the | |
| problem. We are guaranteed that once we are in a native, the limiting factor | |
| is the speed of the operating system - something beyond our control. | |
| This is great news for performance tuning: look at the Java code. | |
| In fact, one quickly develops a sense of which operating system operations | |
| are expensive and which are cheap. Best of all, this knowledge is accurate. | |
| A C program that makes the same sequence of operating system calls will | |
| exhibit the same performance characteristics. This is a feature of | |
| the one-to-one mapping. | |
| <p>What happens when you try to debug a segment fault (or GP)? It's | |
| easy enough to step into a Java method and examine arguments but not possible | |
| to step into a native. Fortunately, nothing special happens in SWT | |
| natives so it's easy enough to isolate the code that is causing the problem. | |
| While on the subject of GPs, wouldn't it make sense for SWT natives to | |
| check their parameters before making the operating system call? It's | |
| tempting to check for NULL or -1 to avoid the crash. On the surface, | |
| this seems to make sense - after all, who wants to GP? The answer, | |
| of course, is that this would violate the one-to-one mapping and would | |
| mean that an equivalent C program would not crash in the same place. | |
| That's bad news for debugging and isolating a problem. | |
| <p>For someone implementing and maintaining SWT, the one-to-one mapping | |
| is extremely valuable. For example, a Windows programmer knows right | |
| away how <b>setSelection</b> works, just by looking at the Java code. | |
| Everyone else needs to read the MSDN Library. It's not light reading, | |
| but the information is there. The same thing is true for a Motif | |
| programmer for SWT on Motif, and for the other supported operating systems. | |
| In fact, it's clear exactly how existing features work and new features | |
| are to be implemented. The critial point here is that the documentation | |
| for the operating system applies to all SWT natives because they are a | |
| one-to-one mapping. | |
| <p>Adding new native features to SWT is a straightforward and well defined | |
| process. For example, implementing drag and drop and integrating it with | |
| the widgets was not difficult, despite the fact that these are two independent | |
| services. Why was this so easy? Nothing is hidden in the C | |
| code. All of the operating system resources needed to implement SWT | |
| are manifested as simple Java objects making it easy to understand how | |
| SWT works and to make changes. This allows SWT to be customized to | |
| support new platform dependent widgets and operating system services as | |
| they become available. | |
| <p>One last point: JNI is rich and powerful. It allows you to allocate | |
| Java objects in C, get and set Java fields, invoke new VMs and throw exceptions. | |
| The operating system, on the other hand, is typically more primitive. | |
| For example, most operating system calls that access memory require you | |
| to allocate the buffer and pass in the size. Java arrays know their | |
| size, so why do we need to pass it in? JNI allows us to allocate | |
| objects in C, so why not allocate buffers in the C code? Wouldn't | |
| it be better to try and "fix" the operating system API to make it more | |
| Java friendly? The answer again is "<i>No</i>". Any deviation | |
| from the one-to-one rule means that our Java code no longer behaves the | |
| same as the equivalent C code. For example, allocating objects in | |
| JNI could introduce a hidden performance problem for Java code inside a | |
| tight loop. Also, it may make sense to allocate one large buffer | |
| and pass in a smaller size, or reuse a buffer. It's tempting to use | |
| JNI | |
| features to attempt to "fix" the operating system API but this is a huge | |
| mistake. | |
| <h3> | |
| Conclusion</h3>All of the natives in SWT are implemented using this simple and consistent strategy. There is no C code to hide the low level details of the operating system such as the event loop, callbacks or the thread model. No code reaches back into Java from C to get a field or invoke a method. Nothing is magic - everything is coded in Java using the terminology and documentation of the operating system. Why is this such a big deal? Some might claim that all SWT does is use JNI to invoke the operating system - nothing fancy. But that's the whole point. Without a simple set of rules and a sense of restraint - a characteristic of SWT - it's just too easy for a widget toolkit to collapse under its own weight.<br> | |
| <p><small>Java and all Java-based trademarks and logos are trademarks or registered | |
| trademarks of Sun Microsystems, Inc. in the United States, other countries, or | |
| both.</small></p> | |
| <p><small>Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both.</small></p> | |
| </body> | |
| </html> |