Sign in Join
Wiki copytemporary

Carbon interface internals: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introduction
22
33> **Warning:** With the release of macOS 10.15 Catalina in October 2019, Apple has removed all support for the 32 bit Carbon framework from the operating system in favour of the [[Cocoa Internals|64 bit Cocoa framework]]. Consequently, the Carbon widgetset is no longer being developed.
44
55This page gives an overview of the LCL Carbon interface for macOS and will help new developers.
66
77For installation and creating a first Carbon application refer to [[Carbon Interface]].
88
99## Documentation about Carbon
1010
1111- [Apple Carbon docs](http://developer.apple.com/documentation/Carbon)
1212
1313- [Apple Mailing List search](http://search.lists.apple.com)
1414
1515- [Carbon Book for download](http://www.mactech.com/macintosh-c/downloads.html)
1616
1717- [Carbon Dev](http://www.carbondev.com/site/)
1818
1919- ["Learning Carbon" sample book chapter](http://www.oreilly.com/catalog/learncarbon)
2020
2121- Free Pascal's Carbon API unit is FPCMacOSAll.pas in /usr/local/share/fpcsrc/packages/extra/univint.
2222
2323## Carbon interface development basics
2424
2525- Target Mac Tiger version is 10.4
2626- Avoid using obsolete or deprecated APIs and functions (e. g. QuickDraw vs. *Quartz 2D*)
2727- Use object approach
2828- Check Carbon calls result with OSError function
2929- Assume UTF-8 encoded strings between LCL and Carbon widgetset (for future Lazarus Unicode support)
3030
3131## What is already working ?
3232
3333- Features - see [[Roadmap|Roadmap#Status_of_features_on_each_widgetset]]
3434- Forms and controls - see [[Roadmap|Roadmap#Status_of_components_on_each_widgetset]]
3535- Creating TOpenGLControl with AGL context (see components/opengl/)
3636- Mouse events
3737- Keyboard events
3838
39-<img src="Carbon_interface_show_case.png" title="Working components in the Carbon interface" width="640" />
39+![](/wiki-images/d/d3/Carbon_interface_show_case.png)
4040
4141## What needs to be done next?
4242
4343See [Bug Tracker Carbon open issues](http://www.freepascal.org/mantis/view_all_set.php?type=3&source_query_id=1017)
4444
4545## Carbon IDE Bugs
4646
4747<thead>
4848
4949</thead>
5050<tbody>
5151
5252File New should be Cmd+N<br />
5353File Save As should be Shift+Cmd+S<br />
5454File Quit should be Cmd+Q<br />
5555Search Find should be Cmd+F<br />
5656Search Find Next should be Cmd+G<br />
5757Search Find Previous should be Shift+Cmd+G<br />
5858(Goto line and Procedure List need to be reassigned)<br />
5959Option+Cmd+F does not bring up Search Results<br />
6060F11 behaves weird!</td>
6161
6262</tbody>
6363
6464See [Bug Tracker Carbon IDE open issues](http://www.freepascal.org/mantis/view_all_set.php?type=3&source_query_id=1018)
6565
6666## Implementation Details
6767
6868### Keyboard
6969
7070- Apple Command key is mapped to ssMeta
7171- Apple Control key is mapped to ssCtrl
7272- Apple Option key is mapped to its inscription, i.e. ssAlt
7373- Virtual key codes mapping is not reliable (depends on keyboard language layout!)
7474
7575### Application
7676
7777- Title: You cannot change it at runtime. You have to set it in Application Bundle.
7878- OnDropFiles event is fired when file is dropped on application dock icon or opened via Finder if is associated. You have to enable this event in Application Bundle.
7979
8080### Drawing and measuring text precisely in parts
8181
8282If you want to draw (via TextOut, TextRect) or measure (via TextWidth, TextHeight) text divided into various parts and rely it will be displayed same each time not depending on its division, you have to disable some default typographic features (like kerning, fractional positioning, ...) of Canvas. You can choose one of the following:
8383
8484- CarbonWidgetSet.SetTextFractional(Canvas, False); // from CarbonInt unit
8585- TCarbonCustomControl(CustomControl.Handle).TextFractional := False; // from CarbonPrivate unit
8686- TCarbonDeviceContext(Canvas.Handle)..TextFractional := False; // from CarbonCanvas unit
8787
8888### Screenshot taking
8989
9090The only possible efficient way of taking a screenshot on macOS is using OpenGL. Apple provides a demonstration function which returns a CGImageRef with the screenshot. This function was converted to Pascal and is available on the glgrab.pas unit on the carbon interface directory. This function requires the Apple-specific parts of the Apple OpenGL headers, and those aren't yet on the FPC Packages, so they were translated and are located on lazarus/lcl/interfaces/carbon/opengl.pas until they are released on a stable Free Pascal.
9191
9292After obtaining a CGImageRef, the next step to implement the RawImage_fromDevice method is obtaining a local copy of it's pixel data. It isn't possible to directly access the bytes of a CGImageRef, so the image needs to be drawn to a CGContextRef which uses a memory area allocated by us as buffer. This method has the great advantage of converting from the internal format of the screenshot bytes to the very convenient ARGB, 32-bits depth, 8-bits per channel format that is default to LCL.
9393
9494### Focusing
9595
9696macOS has two possible ways of focusing: Full keyboard navigation (enabled with Accessibility options), Text field navigation (only controls that are to receive text input should be focused). Full keyboard navigation is the same as any other widgetset.
9797
9898There's no way to say if control is text-input or not. Native OS controls will handle focus themselves. The Carbon Widgetset checks if bound LCLObject is SynEdit and will switch the focus to it.
9999
100100### Canvas Clipping
101101
102102Because Quartz 2D's clipping is decreasing, care must be taken for proper clipping region change. There should be no code, that changes CGContext's clip region directly. Carbon widget API's functions should be used instead.
103103
104104## Compatibility issues
105105
106106These are things that will probably never be solved.
107107
108108### Mouse.CursorPos changing does not generate mouse (move) events
109109
110110### Release mouse capture is not supported
111111
112112### Command line parameters
113113
114114Because Carbon applications are executed via Application Bundle, command line parameters are not passed. You have to use OnDropFiles event to detect openning associated files.
115115
116116### Shortcuts indication is not supported
117117
118118### Drawing on Canvas outside OnPaint event is not supported
119119
120120### Drawing on screen device context is not supported
121121
122122### TCustomControl.Color of clBtnFace makes its background transparent
123123
124124### TWinControl.Font fsStrikeOut is not supported
125125
126126### TForm
127127
128128- Icon is not supported
129129- ShowInTaskbar is not supported
130130- TForm.top=0 is not good. Use TForm.top=23
131131
132132### TEdit.PasswordChar different then default is not supported
133133
134134### TMemo.WordWrap when is disabled, does not allow to scroll text horizontally
135135
136136### TListBox.Columns is not supported
137137
138138### TComboBox
139139
140140- DroppedDown does not show drop down list when style is csDropDownList
141141- DropDownCount is not supported
142142- Style: csSimple, csOwnerDrawFixed and csOwnerDrawVariable are not supported
143143
144144### TPanel.Bevelxxx: bvLowered and bvSpace are not supported
145145
146146### TBitBtn.Spacing is not supported
147147
148148### TTrackBar
149149
150150- LineSize is not supported
151151- ScalePos is not supported
152152- TickMarks are not supported
153153
154154### TProgressBar
155155
156156- BarShowText is not supported
157157- Smooth is not supported
158158- Step is not supported
159159
160160### TColorDialog.Title is not supported
161161
162162### macOS System (Windows' and Controls') Handles
163163
164164It’s some times necessary to access system windows or control handles directly. For example, you wish to use some system features that are not available (not yet implemented?) for LCL. It’s very common for LCL Windows developers (and Delphi developers) to use TControl.Handle property, since Handle is a system window handle for Win32 widgetset. It can be used with any system function. But it’s not so for Carbon widgetset. TControl.Handle would return handle native to widgetset not macOS. TControl.Handle would return TCarbonWidget class (declared at CarbonDef unit). This’s a wrapper class, and you can use it to get system handle.
165165
166166``` pascal
167167var
168168 AHiView: HiViewRef;
169169begin
170170...
171171//this is incorrect way of getting system handle for Carbon widgetset
172172//AHiView:= HiViewRef(Button1.Handle)
173173
174174//this is correct way
175175AHiView := TCarbonWidget(Button1.Handle).Widget;
176176...
177177end;
178178```
179179
180180You should also note, that there’s difference in getting WindowRef in LCL versions. If you’re using Lazarus 0.9.26 or earlier you should get WindowRef in the following way:
181181
182182``` pascal
183183var
184184 MacWin: WindowRef;
185185begin
186186...
187187MacWin := TCarbonWidget(Form1.Handle).Widget;
188188...
189189end;
190190```
191191
192192If you’re using svn LCL version (latest trunk), then you should use the following way:
193193
194194``` pascal
195195uses
196196 ..CarbonPrivate..
197197var
198198 MacWin: WindowRef;
199199begin
200200...
201201MacWin := TCarbonWindow(Form1.Handle).Window;
202202...
203203end;
204204```
205205
206206Keep in mind, that LCL is designed to be cross-platform library, and accessing system handles to use with system functions makes you program hardly portable. The way of getting system objects handle might also be changed in future.
207207
208208### TTrayIcon
209209
210210#### Checking if the TTrayIcon menu is visible
211211
212212Unfortunately Apple only added a facility to check menu visibility in Snow Leopard 10.6. To overcome this limitation in a simple way in previous versions one can use the following code, which should work in previous versions using a class method in TCarbonWSCustomTrayIcon:
213213
214214``` pascal
215215uses
216216 CarbonWSExtCtrls;
217217
218218...
219219
220220begin
221221 if TCarbonWSCustomTrayIcon.IsTrayIconMenuVisible(TrayIcon1) then Caption := 'Visible'
222222 else Caption := 'Not visible';
223223end;
224224```
225225
226226## How to add a new control
227227
228228For example TButton.
229229
230230TButton is defined in lcl/buttons.pp. This is the platform independent part of the LCL, which is used by the normal LCL programmer.
231231
232232Its widgetset class is in lcl/widgetset/wsbuttons.pp. This is the platform independent base for all widgetsets (carbon, gtk, win32, ...).
233233
234234Its Carbon interface class is in lcl/interfaces/carbon/carbonwsbuttons.pp:
235235
236236``` pascal
237237TCarbonWSButton = class(TWSButton)
238238private
239239protected
240240public
241241 class function CreateHandle(const AWinControl: TWinControl; const AParams: TCreateParams): TLCLIntfHandle; override;
242242end;
243243```
244244
245245Every WS class that actually implements something must be registered. See the initialization section at the end of the carbonwsXXX.pp unit:
246246
247247``` pascal
248248RegisterWSComponent(TCustomButton, TCarbonWSButton);
249249```
250250
251251TCarbonWSButton overrides CreateHandle to create a Carbon button helper class called TCarbonButton in carbonprivate.pp, which really creates the button control in the Carbon and installs event handlers.
252252
253253---
254254*From the Free Pascal wiki, page "Carbon interface internals", as it stood on 2021-08-04 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history