When we started this project, we had two simple goals.
Goal one, make a true Macintosh application. Macintosh Rhino needed to look and feel like it was originally written for the Macintosh. Some programs that are ported from Windows to the Macintosh feel out of place on the Macintosh. This goal meant that we would not use a cross-platform development tool to create the Macintosh version, but instead create a version that used the native Macintosh widgets and development system to create it.
A corollary of this goal requires that we change some of the common Windows Rhino user interface idioms. A Macintosh computer does not automatically have a two button mouse, and requiring a Macintosh application to use a right mouse button would make the application feel strange along side the other Macintosh applications.
Goal two, reuse as much existing code as possible. The Rhino code base has been finely honed over the last ten years, and it is important to retain the accumulated knowledge that the code represents. We have been able to achieve this goal better than we expected at the beginning of the project, and we'll discuss this further later in this article.
For developers, this second goal has a pleasant side-effect. The more code that is reused, the less new code needs to be written and maintained for a single platform. This leads to both quicker development time and more robust code, as the code must work correctly on two platforms using two different development systems.
Rhino Advantages
Windows Rhino has several advantages that allowed a Macintosh version to be developed from the Windows code base.
- Uses OpenNURBS. Rhino already had a cross-platform data file format that works on many different operating systems and computer processors. This is a huge task to undertake when porting an application to a new platform, and the work to be able to share Rhino data files between operating system versions has already been done.
- Uses OpenGL. OpenGL solves the output side of Rhino portability, at least to the Macintosh. OpenGL is the native drawing API on the Macintosh, so every Macintosh computer has excellent support for the OpenGL library. The Macintosh version uses essentially the same OpenGL drawing engine in Rhino that is used in Windows, giving a visual display that is almost identical between the two platforms.
- Uses C++. Windows Rhino is written in C++, a programming language that is also available on the Macintosh. C++ uses classes and objects to represent the internal workings of the program, and features of the C++ language let us adapt the core code for each computer platform.
- Good internal separation between data model and data visualization. By using C++, many of the individual tasks of the Rhino program are handled by custom C++ classes. When a class implementation was very dependent on the underlying Windows operating system, an equivalent class was written for the Macintosh version. Since the core code is factored into many small classes, this technique allowed us to replace just the parts that depend on the operating system, while retaining all the other classes that handle Rhino data model manipulation.
Rhino Challenges
Windows Rhino also provides some challenges when porting the application to another computer platform that are not factors when porting a typical application.
- Rhino SDK. Most programs can be considered a black box. You cannot see inside the box, and have no idea how the program actually works. This is an advantage when porting an application between Windows and OS X, as the developers can make radical internal changes without affecting how the program appears to work on the two different platforms. Windows Rhino provides a very extensive programming SDK that allows third-party developers to write additional software that becomes part of the Rhino application, and the SDK becomes part of the application that must be preserved between the two platforms. Part of this facility depends on a programming API written in C++. Since the Macintosh version is also written in C++, we are able to achieve almost perfect fidelity in the Rhino SDK between the two platforms. The differences between the SDK versions are largely the differences between the platforms themselves.
- Plug-ins. The Rhino SDK is the template that provides third-party interoperability, and Rhino plug-ins are the mechanism that implements the third-party additions. Macintosh OS X is built on top of the Unix operating system, and Unix has a rich library for creating and using shared libraries. Equivalent methods for finding, loading, initializing, and running third-party plug-ins have been developed for the Macintosh, allowing plug-ins to be ported to the Macintosh by writing a small amount of Macintosh-specific glue code for each plug-in.
- Platform API differences (MDI). Some things are just plain different between Windows and OS X. To display more than one Rhino drawing simultaneously, Windows Rhino starts multiple copies of the application. On OS X, a single copy of the application displays all the Rhino drawings. This has deep implications in the underlying code and has presented some of the biggest challenges.
How Did We Do It?
OpenNURBS, OpenGL, and modular C++ classes solved a lot of the porting issues between the two platforms, but did not solve everything. We developed some additional tools to provide the rest of the interoperability.
- Windows API. Rhino was originally written solely for Windows and of course uses the Windows API for operations such as file access. It was not difficult to write work-alike substitute functions on the Macintosh platform. It is trivial to write an equivalent Windows API DeleteFile function on the Macintosh that calls the unlink() function to remove the file. In this way we wrote equivalent Windows API functions for the parts of the Windows API that we use. Some parts of this were more involved, such as providing a replacement for the Windows registry functions, but this was fairly straight-forward work.
- MFC and Dialogs. Windows Rhino uses the MFC library to provide dialog support. All the dialog windows were recreated for the Macintosh platform, but there is important code in the Windows Rhino dialog classes that drives the dialogs. The dialog classes do calculations and drawing manipulations while the dialog is running. We created a thin MFC work-alike layer that allowed us to preserve virtually all of the behavior of the Windows dialog code, yet the code is actually manipulating OS X dialog controls when the same code runs on the Macintosh! Being lazy developers, we had much less work to do, and were able to reuse more of the existing Windows code. To bring a Windows Rhino dialog to the Macintosh, we need to create a couple of support classes for the Macintosh version of the dialog, fill in a couple of data tables, and we are done. The rest of the dialog code runs on both computer platforms.