| <!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> |