Mac OS X Porting - Carbon Documentation

From OpenOffice.org Wiki

Jump to: navigation, search

The purpose of this page is to regroup information about the evolution of the Mac OS X port of OpenOffice.org. This will allow the current team to have a centralized place to which they can communicate/add/read new information about what they did, their findings, advices, suggestions etc.. The first part of the documentation has been compiled from information given by Eric Bachard. Feel free to add and modify it.

Contents

Carbon vs. Cocoa

The first step that the newly created OS X porting team had to take, was to decide which native API to choose, Carbon or Cocoa? At first Cocoa looked like the right choice since it is recommended by Apple and its libraries are object oriented. But after a testing period, the Carbon API was chosen to implement the OS X port of OOo. Here are some reasons why:

  • Apple itself is telling Win32 porters: "Wherever possible, it is recommended that you develop applications using the Cocoa environment. However, Carbon is a good choice when your application is already implemented in C or C++ code that has been written in a procedural (as opposed to object-oriented) style." [1] This exactly applies to OOo.
  • Reading in the Carbon-Cocoa Integration Guide and knowing from my own personal experience as a mainly Cocoa programmer it's very easy to intermix Carbon and Cocoa if you respect some rules. Even using UI elements from the other framework is possible. [2]
  • Apple has to support Carbon in the future because heavy weight applications like MS Office for Mac OS X and Adobe Photoshop are written in Carbon too as well as libraries like Qt. Those big players certainly don't want Apple to FORCE them into another transition after 68k -> PPC, Classic Mac OS -> Mac OS X (Carbon), PPC -> Intel.
  • Using Cocoa as the porting base has already failed once. So let's try a different route now.
  • The Carbon API is very similar to the Win32 API. VCL was designed with the Win32 API in mind. We can "copy" a lot from the Win32 API implementation.
  • The user doesn't care if the implementation uses Carbon or Cocoa.

[1] http://developer.apple.com/documentation/Porting/Conceptual/win32Porting/win32porting.html
[2] http://developer.apple.com/documentation/Cocoa/Conceptual/CarbonCocoaDoc/Articles/CocoaUIInCarbon.html

Related Sites
1. Carbon Guides

Svdem

The first short term goal is to get svdem (a small sample application based on VCL) up and running - that means svdem comes up with a native Carbon window which is resizable and can already react on paint messages. The general approach is as follows VCL consists of a platform independent and a platform dependent part. We have to implement the platform dependent part based on Carbon (setting up a native message loop, dispatching events, implementing some basic stuff like SalGraphics in order to be able to draw etc.). Svdem serves as an manageable application to get those primitives up and running before we try to go further.

Painting and Drawing

MacOSX typically relies on indirect painting, i.e. you declare a region of your window as being invalid and at some time later you will receive the request to draw into that invalid area. This means th OS decides the time when something should be drawn and not the application. OOo however, uses immediate drawing quite often so I tried to get it working. I added a handler for mouse events and put some code in svdem that reacts on mouse clicks and just draws a small rectangle at the point where you clicked. So the drawing code is executed immediately upon receiving the mouse click.

The methods of SalGraphics with a capital letter are wrappers that we need for coordinate mirroring when using right-to-left user interfaces like hebrew or arabic. The real code is implemented in the methods with the lower case letter, and in fact only those are pure virtual.

In order to get a Quartz 2D context I only found one solution that appeared useful to me and this is using the way described in the Quartz 2D programming Guide, 'Creating a window graphics context'. It uses the QDBegin/EndCGContext API which appeared to me a little bit like the GetDC() from Windows. It allows for immediate retrieval of the context for a given window, drawing onto it, flushing and releasing it. May be this is expensive but it was sufficient to get started. I changed the Begin/EndGraphics() helper functions in salgdiutils.cxx accordingly and added a window and graphics context member to AquaSalGraphics.

Events and Time Manager

Events

The path of events should be:

  • Our handler gets the event (in this case KEventWindowClose).
  • The corresponding SalEvent is sent to the SalFrame's callback function.
  • Sal in turn will call the SalFrame's Close method which closes the window.

What was learned until now:

  • The real message loop will be run by VCL itself, that's why we cannot rely on Carbon's RunApplicationMessageLoop API. I changed AquaApplication::Yield so that it manually receives and dispatches events using Carbons ReceiveNextEvent etc. API.
  • Ending the application works like this according to my current understanding:

In the close handler of one of the Windows in the inheritance hierarchy (I don't remeber correctly if it was SystemWindow or which other window) it will check if it is the main application window that is being closed. If yes an Application::Quit will be called which forwards this quit request as a user event to the window (PostUserEvent...). In the message handler of the Window this event finally arrives and will be forwarded via CallCallback to the corresponding Frame. Finally the event arrives somewhere in the Application class (if I remember correctly) where a quit method exists in which a flag QuitApplication will be set to 'true' which finally ends the message loop driven by VCL.

Time Manager

The timer. I saw there are two possibilities, using the TimeManager (my favourite) or the EventManager. The EventManager installs a timer in the event loop and my feeling was that we probably have none (of course we have one, but it is implemented manually). So the required GetMainEventLoop() call might probably fail. The TimerManager however seems to be interrupt driven and thus much more precise.

Related sites

1. Carbon Event Manager Programming Guide

[FIX: add more details about events handling with Carbon]

Personal tools
Create a book