4 This is the README file for the macOS/Darwin version of Tcl/Tk.
6 1. Where to go for support
7 --------------------------
9 - The tcl-mac mailing list on sourceforge is the best place to ask questions
10 specific to Tcl & Tk on macOS:
11 http://lists.sourceforge.net/lists/listinfo/tcl-mac
12 (this page also has a link to searchable archives of the list, please check them
13 before asking on the list, many questions have already been answered).
15 - For general Tcl/Tk questions, the newsgroup comp.lang.tcl is your best bet:
16 http://groups.google.com/group/comp.lang.tcl/
18 - The Tcl'ers Wiki also has many pages dealing with Tcl & Tk on macOS, see
19 https://wiki.tcl-lang.org/page/MacOS
21 - Please report bugs with Tk on macOS to the tracker:
22 https://core.tcl-lang.org/tk/reportlist
24 2. Using Tcl/Tk on macOS
25 ---------------------------
27 - There are two versions of Tk available on macOS: TkAqua using the native
28 aqua widgets and look&feel, and TkX11 using the traditional unix X11 widgets.
29 TkX11 requires an X11 server to be installed, such as XQuartz (available from www.xquartz.org).
30 TkAqua and TkX11 can be distinguished at runtime via [tk windowingsystem].
32 - At a minimum, macOS 10.3 is required to run Tcl and TkX11.
33 TkAqua requires macOS 10.6 or later.
35 - Unless weak-linking is used, Tcl/Tk built on macOS 10.x will not run on
36 10.y with y < x; on the other hand Tcl/Tk built on 10.y will always run on 10.x
37 with y <= x (but without any of the fixes and optimizations that would be
38 available in a binary built on 10.x).
39 Weak-linking is available on OS X 10.2 or later, it additionally allows Tcl/Tk
40 built on 10.x to run on any 10.y with x > y >= z (for a chosen z >= 2).
42 - Wish checks the Resources/Scripts directory in its application bundle for a
43 file called AppMain.tcl, if found it is used as the startup script and the
44 Scripts folder is added to the auto_path. This can be used to emulate the old
47 - If standard input is a special file of zero length (e.g. /dev/null), Wish
48 brings up the Tk console window at startup. This is the case when double
49 clicking Wish in the Finder (or using 'open Wish.app' from the Terminal).
51 - Tcl extensions can be installed in any of:
52 $HOME/Library/Tcl /Library/Tcl
53 $HOME/Library/Frameworks /Library/Frameworks
54 (searched in that order).
55 Given a potential package directory $pkg, Tcl on OSX checks for the file
56 $pkg/Resources/Scripts/pkgIndex.tcl as well as the usual $pkg/pkgIndex.tcl.
57 This allows building extensions as frameworks with all script files contained in
58 the Resources/Scripts directory of the framework.
60 - The 'deploy' target of macosx/GNUmakefile installs the html manpages into the
61 standard documentation location in the Tcl/Tk frameworks:
62 Tcl.framework/Resources/Documentation/Reference/Tcl
63 Tk.framework/Resources/Documentation/Reference/Tk
64 No nroff manpages are installed by default by the GNUmakefile.
66 - The Tcl and Tk frameworks can be installed in any of the system's standard
67 framework directories:
68 $HOME/Library/Frameworks /Library/Frameworks
70 - ${prefix}/bin/wish8.x is a script that calls a copy of 'Wish' contained in
71 Tk.framework/Resources
73 - if 'Wish' is started from the Finder or via 'open', $argv may contain a
74 "-psn_XXXX" argument. This is the process serial number, you may need to filter
75 it out for cross platform compatibility of your scripts.
77 - the env array is different when Wish is started from the Finder (i.e. via
78 LaunchServices) than when it (or tclsh) is invoked from the Terminal, in
79 particular PATH may not be what you expect. (Wish started by LaunchServices
80 inherits loginwindow's environment variables, which are essentially those set in
81 $HOME/.MacOSX/environment.plist, and are unrelated to those set in your shell).
83 - TkAqua provides access to native OS X images via the Tk native bitmap facility
84 (including any image file readable by NSImage). A native bitmap name is
85 interpreted as follows (in order):
86 - predefined builtin 32x32 icon name (stop, caution, document, etc)
87 - name defined by [tk::mac::iconBitmap]
88 - NSImage named image name
90 - 4-char OSType of IconServices icon
91 the syntax of [tk::mac::iconBitmap] is as follows:
92 tk::mac::iconBitmap name width height -kind value
94 -file icon of file at given path
95 -fileType icon of given file type
96 -osType icon of given 4-char OSType file type
97 -systemType icon for given IconServices 4-char OSType
98 -namedImage named NSImage for given name
99 -imageFile image at given path
100 This support was added with the Cocoa-based Tk 8.5.7.
102 - TkAqua cursor names are interpred as follows (in order):
103 - standard or platform-specific Tk cursor name (c.f. cursors.n)
104 - @path to any image file readable by NSImage
105 - NSImage named image name
106 Support for the latter two was added with the Cocoa-based Tk 8.5.7.
108 - The standard Tk dialog commands [tk_getOpenFile], [tk_chooseDirectory],
109 [tk_getSaveFile] and [tk_messageBox] all take an additional optional -command
110 parameter on TkAqua. If it is present, the given command prefix is evaluated at
111 the global level when the dialog closes, with the dialog command's result
112 appended (the dialog command itself returning an emtpy result). If the -parent
113 option is also present, the dialog is configured as a modeless (window-modal)
114 sheet attached to the parent window and the dialog command returns immediately.
115 Support for -command was added with the Cocoa-based Tk 8.5.7.
117 - The TkAqua-specific [tk::mac::standardAboutPanel] command brings the standard
118 Cocoa about panel to the front, with all its information filled in from your
119 application bundle files (i.e. standard about panel with no options specified).
120 See Apple Technote TN2179 and the AppKit documentation for -[NSApplication
121 orderFrontStandardAboutPanelWithOptions:] for details on the Info.plist keys and
122 app bundle files used by the about panel.
123 This support was added with the Cocoa-based Tk 8.5.7.
125 - TkAqua has three special menu names that give access to the standard
126 Application, Window and Help menus, see menu.n for details. By default, the
127 platform-specific standard Help menu item "YourApp Help" performs the default
128 Cocoa action of showing the Help Book configured in the application's
129 Info.plist (or displaying an alert if no Help Book is set). This action can be
130 customized by defining a procedure named [tk::mac::ShowHelp]. If present, this
131 procedure is invoked instead by the standard Help menu item. Support for the
132 Window menu and [tk::mac::ShowHelp] was added with the Cocoa-based Tk 8.5.7.
134 - The TkAqua-specific command [tk::unsupported::MacWindowStyle style] is used to
135 get and set macOS-specific toplevel window class and attributes. Note that
136 the window class and many attributes have to be set before the window is first
137 mapped for the change to have any effect.
138 The command has the following syntax:
139 tk::unsupported::MacWindowStyle style window ?class? ?attributes?
140 The 2 argument form returns a list of the current class and attributes for the
141 given window. The 3 argument form sets the class for the given window using the
142 default attributes for that class. The 4 argument form sets the class and the
143 list of attributes for the given window.
145 document, modal, floating, utility, toolbar, simple, help, overlay
146 Window attribute names:
147 standardDocument, standardFloating, resizable, fullZoom, horizontalZoom,
148 verticalZoom, closeBox, collapseBox, toolbarButton, sideTitlebar,
149 noTitleBar, unifiedTitleAndToolbar, metal, hud, noShadow, doesNotCycle,
150 noActivates, hideOnSuspend, inWindowMenu, ignoreClicks, doesNotHide,
151 canJoinAllSpaces, moveToActiveSpace, nonActivating
153 Note that not all attributes are valid for all window classes. Support for the
154 3 argument form was added with the Cocoa-based Tk 8.5.7, at the same time
155 support for some legacy Carbon-specific classes and attributes was removed
156 (they are still accepted by the command but no longer have any effect).
158 - Another command available in the tk::unsupported::MacWindowStyle namespace is:
159 tk::unsupported::MacWindowStyle tabbingid window ?newId?
160 which can be used to get or set the tabbingIdentifier for the NSWindow
161 associated with a Tk Window. See section 3 for details.
164 tk::unsupported::MacWindowStyle appearance window ?newAppearance?
165 is available when Tk is built and run on macOS 10.14 (Mojave) or later. In
166 that case the Ttk widgets all support the "Dark Mode" appearance which was
167 introduced in 10.14. The command accepts the following values for the optional
168 newAppearance option: "aqua", "darkaqua", or "auto". If the appearance is set
169 to aqua or darkaqua then the window will be displayed with the corresponding
170 appearance independent of any preferences settings. If it is set to "auto"
171 the appearance will be determined by the preferences. This command can be
172 used to opt out of Dark Mode on a per-window basis. It may be best to run the "update" command before setting the appearance property, to allow the event loop to run.
175 - To determine the current appearance of a window in macOS 10.14 (Mojave) and
176 higher, one can use the command:
177 tk::unsupported::MacWindowStyle isdark window?
178 The boolean return value is true if the window is currently displayed with the
181 - If you want to use Remote Debugging with Xcode, you need to set the
182 environment variable XCNOSTDIN to 1 in the Executable editor for Wish. That will
183 cause us to force closing stdin & stdout. Otherwise, given how Xcode launches
184 Wish remotely, they will be left open and then Wish & gdb will fight for stdin.
186 3. FullScreen, Split View and Tabbed Windows
187 --------------------------------------------
189 Since the release of OSX 10.6 (Snow Leopard) a steadily expanding sequence of
190 high level window operations have been added to Apple's window manager. These
191 operations are launched by user actions which are handled directly by the
192 window manager; they are not initiated by the application. In some, but not
193 all cases, the application is notified before and after the operations are
196 In OSX releases up to and including 10.6 there were three buttons with
197 stoplight colors located on the left side of a window's title bar. The
198 function of the green button was to "zoom" or "maximize" the window, i.e. to
199 expand the window so that it fills the entire screen, while preserving the
200 appearance of the window including its title bar. The release of OSX 10.7
201 (Lion) introduced the "FullScreen" window which not only filled the screen but
202 also hid the window's title bar and the menu bar which normally appears at the
203 top of the screen. These hidden objects would only become visible when the
204 mouse hovered near the top of the screen. FullScreen mode was initiated by
205 pressing a button showing two outward pointing arrows located on the right side
206 of the title bar; it was terminated by pressing a similar button with inward
207 pointing arrows on the right hand side of the menu bar. In OSX 10.10
208 (Yosemite) the FullScreen button was removed. The green button was repurposed
209 to cause a window to become a FullScreen window. To zoom a window the user had
210 to hold down the option key while pressing the green button. The release of
211 OSX 10.11 added a third function to the green button: to create two half-screen
212 windows with hidden title bars and a hidden menu bar, called Split View
213 windows. If the green button is held down for one second its window expands to
214 fill half of the screen. It can be moved to one side or the other with the
215 mouse. The opposite side shows thumbnail images of other windows. Selecting
216 one of the thumbnails expands its window to fill that half of the screen. The
217 divider between the two windows can be moved to adjust the percentage of the
218 screen occupied by each of the two tiles. In OSX 10.12 (Sierra) Tabbed windows
219 were introduced. These allow an application with multiple windows to display
220 its windows as tabs within a single window frame. Clicking on a tab brings its
221 window into view. Tabs can be rearranged by dragging. Dragging a tab to the
222 desktop turns it into a separate window. Items in the Window menu can be used
223 to cycle through the tabs, move tabbed windows to separate windows, or merge a
224 set of separate windows as tabs in the same window frame.
226 Tk now fully supports all of these high level window operations on any system
227 where the operation exists. The FullScreen and Split View windows are handled
228 automatically with no action required on the part of the programmer. Tabbed
229 windows, on the other hand, require some attention from the programmer.
230 Because many of the operations with tabs are handled through the application's
231 Window menu, it is essential that an application provide a Windows menu to
232 avoid presenting a confusing interface to the user. This cannot be ignored, in
233 part because the systemwide Dock Preferences offers an option to always attempt
234 to open application windows as tabs. An application which does not provide a
235 Window menu will necessarily present a confusing interface to any user who has
236 selected this option.
238 A further complication is that it is not neccessarily appropriate for all of an
239 application's windows to be grouped together as tabs in the same frame. In
240 fact, the Apple guidelines insist that windows which are grouped together as
241 tabs should be similar to each other. The mechanism provided for arranging
242 this was to assign to each NSwindow a tabbingIdentifier, and to require that
243 all windows grouped together as tabs in the same window frame must have the
244 same tabbingIdentifier. A tabbingIdentifier is implemented as an arbitrary
245 string, and a system-generated default tabbingIdentifier is provided to all new
248 Tk provides a means for getting and setting the tabbingIdentifier of
249 the NSWindow underlying a Tk Window. This is handled by the command
251 tk::unsupported::MacWindowStyle tabbingid window ?newId?
253 (This command generates an error if used on OSX 10.11 or earlier, since the
254 tabbingIdentifier does not exist on those systems.) The command returns the
255 tabbingIdentifier which had been assigned to the window prior to execution of
256 the command. If the optional newId argument is omitted, the window's
257 tabbingIdentifier is not changed. Otherwise it is set to the string specified
260 Since NSWindows can only be grouped together as tabs if they all have the same
261 tabbingIdentifier, one can prevent a window from becoming a tab by giving it a
262 unique tabbingIdentifier. This is independent of any preferences setting. To
263 ensure that we maintain consistency, changing the tabbingIdentifier of a window
264 which is already displayed as a tab will also cause it to become a separate
267 4. Ttk, Dark Mode and semantic colors
268 ---------------------------------------
270 With the release of OSX 10.14 (Mojave), Apple introduced the DarkAqua
271 appearance. Part of the implementation of the Dark Mode was to make
272 some of the named NSColors have dynamic values. Apple calls these
273 "semantic colors" because the name does not specify a specific color,
274 but rather refers to the context in which the color should be used.
275 Tk now provides the following semantic colors as system colors:
276 systemTextColor, systemTextBackgroundColor, systemSelectedTextColor,
277 systemSelectedTextBackgroundColor, systemControlTextColor,
278 systemDisabledControlTextColor, systemLabelColor, systemLinkColor, and
279 systemControlAccentColor. All of these except the last three were
280 present in OSX 10.0 (and those three are simulated in systems where they
281 do not exist). The change in 10.14 was that the RGB color value of
282 these colors became dynamic, meaning that the color value can change
283 when the application appearance changes. In particular, when a user
284 selects Dark Mode in the system preferences these colors change
285 appearance. For example systemTextColor is dark in Aqua and light in
286 DarkAqua. One additional color, systemSelectedTabTextColor, does not
287 exist in macOS but is used by Tk to match the different colors used
288 for Notebook tab text in different OS versions.
290 The default background and foreground colors of most of the Tk widgets
291 have been set to semantic colors, which means that the widgets will change
292 appearance, and remain usable, when Dark Mode is selected in the system
293 preferences. However, to get a close match to the native Dark Mode style it
294 is recommended to use Ttk widgets when possible.
296 Apple's tab view and GroupBox objects delimit their content by
297 displaying it within a rounded rectangle with a background color that
298 contrasts with the background of the containing object. This means
299 that the background color of a Ttk widget depends on how deeply it is
300 nested inside of other widgets that use contrasting backgrounds. To
301 support this, there are 8 contrasting system colors named
302 systemWindowBackgroundColor, and systemWindowBackgroundColor1 - 7.
303 The systemWindowBackgroundColor is the standard background for a
304 dialog window and the others match the contrasting background colors
305 used in ttk::notebooks and ttk::labelframes which are nested to the
308 5. Building Tcl/Tk on macOS
309 ------------------------------
311 - macOS 10.6 is required to build TkAqua and TkX11. The XCode application provides everything needed to build Tk, but it is not necessary to install the full XCode.
312 It suffices to install the Command Line Tools package, which can be done
313 by running the command:
314 xcode-select --install
316 - Tcl/Tk are most easily built as macOS frameworks via GNUmakefile in
317 tcl/macosx and tk/macosx (see below for details), but can also be built with the
318 standard unix configure and make buildsystem in tcl/unix resp. tk/unix as on any
319 other unix platform (indeed, the GNUmakefiles are just wrappers around the unix
321 The macOS specific configure flags are --enable-aqua, --enable-framework and
322 --disable-corefoundation (which disables CF and notably reverts to the standard
323 select based notifier). Note that --enable-aqua is incompatible with
324 --disable-corefoundation (for both Tcl and Tk configure).
326 - It was once possible to build with the Xcode IDE via the projects in
327 tk/macosx, but this has not been tested recently. Take care to use the
328 project matching your DevTools and OS version:
329 Tk.xcode: for Xcode 3.1 on 10.5
330 Tk.xcodeproj: for Xcode 3.2 on 10.6
331 These have the following targets:
332 Tk: calls through to tk/macosx/GNUMakefile,
333 requires a corresponding build of the Tcl
334 target of tcl/macosx/Tcl.xcode.
335 tktest: static build of TkAqua tktest for debugging.
336 tktest-X11: static build of TkX11 tktest for debugging.
337 The following build configurations are available:
338 Debug: debug build for the active architecture,
339 with Fix & Continue enabled.
340 Debug clang: use clang compiler.
341 Debug llvm-gcc: use llvm-gcc compiler.
342 Debug gcc40: use gcc 4.0 compiler.
343 DebugNoGC: disable Objective-C garbage collection.
344 DebugNoFixAndContinue: disable Fix & Continue.
345 DebugUnthreaded: disable threading.
346 DebugNoCF: disable corefoundation (X11 only).
347 DebugNoCFUnthreaded: disable corefoundation an threading.
348 DebugMemCompile: enable memory and bytecode debugging.
349 DebugLeaks: define PURIFY.
350 DebugGCov: enable generation of gcov data files.
351 Debug64bit: configure with --enable-64bit (requires
352 building on a 64bit capable processor).
353 Release: release build for the active architecture.
354 ReleaseUniversal: 32/64-bit universal build.
355 ReleaseUniversal clang: use clang compiler.
356 ReleaseUniversal llvm-gcc: use llvm-gcc compiler.
357 ReleaseUniversal gcc40: use gcc 4.0 compiler.
358 ReleaseUniversal10.5SDK: build against the 10.5 SDK (with 10.5
360 Note that the non-SDK configurations have their deployment target set to
361 10.5 (Tk.xcode) resp. 10.6 (Tk.xcodeproj).
362 The Xcode projects refer to the toplevel tcl and tk source directories via the
363 the TCL_SRCROOT and TK_SRCROOT user build settings, by default these are set to
364 the project-relative paths '../../tcl' and '../../tk', if your source
365 directories are named differently, e.g. '../../tcl8.6' and '../../tk8.6', you
366 need to manually change the TCL_SRCROOT and TK_SRCROOT settings by editing your
367 ${USER}.pbxuser file (located inside the Tk.xcodeproj bundle directory) with a
370 - To enable weak-linking, set the MACOSX_DEPLOYMENT_TARGET environment variable
371 to the minimal OS version the binaries should be able to run on, e.g:
372 export MACOSX_DEPLOYMENT_TARGET=10.6
373 This requires at least gcc 3.1; with gcc 4 or later, set/add to CFLAGS instead:
374 export CFLAGS="-mmacosx-version-min=10.6"
375 Support for weak-linking was added with 8.4.14/8.5a5.
377 Detailed Instructions for building with macosx/GNUmakefile
378 ----------------------------------------------------------
380 - Unpack the Tcl and Tk source release archives and place the tcl and tk source
381 trees in a common parent directory.
382 [ If you don't want have the two source trees in one directory, you'll need to ]
383 [ create the following symbolic link for the build to work as setup by default ]
384 [ ln -fs /path_to_tcl/build /path_to_tk/build ]
385 [ (where /path_to_{tcl,tk} is the directory containing the tcl resp. tk tree) ]
386 [ or you can pass an argument of BUILD_DIR=/somewhere to the tcl and tk make. ]
388 - The following instructions assume the Tcl and Tk source trees are named
389 "tcl${ver}" and "tk${ver}" (where ${ver} is a shell variable containing the
390 Tcl/Tk version number, e.g. '8.6').
391 Setup this shell variable as follows:
393 If you are building from CVS, omit this step (CVS source tree names usually do
394 not contain a version number).
396 - Setup environment variables as desired, e.g. for a universal build on 10.5:
397 CFLAGS="-arch i386 -arch x86_64 -arch ppc -mmacosx-version-min=10.5"
400 - Change to the directory containing the Tcl and Tk source trees and build:
401 make -C tcl${ver}/macosx
402 make -C tk${ver}/macosx
404 - Install Tcl and Tk onto the root volume (admin password required):
405 sudo make -C tcl${ver}/macosx install
406 sudo make -C tk${ver}/macosx install
407 if you don't have an admin password, you can install into your home directory
408 instead by passing an INSTALL_ROOT argument to make:
409 make -C tcl${ver}/macosx install INSTALL_ROOT="${HOME}/"
410 make -C tk${ver}/macosx install INSTALL_ROOT="${HOME}/"
412 - The default GNUmakefile targets will build _both_ debug and optimized versions
413 of the Tcl and Tk frameworks with the standard convention of naming the debug
414 library Tcl.framework/Tcl_debug resp. Tk.framework/Tk_debug.
415 This allows switching to the debug libraries at runtime by setting
416 export DYLD_IMAGE_SUFFIX=_debug
417 (c.f. man dyld for more details)
419 If you only want to build and install the debug or optimized build, use the
420 'develop' or 'deploy' target variants of the GNUmakefile, respectively.
421 For example, to build and install only the optimized versions:
422 make -C tcl${ver}/macosx deploy
423 make -C tk${ver}/macosx deploy
424 sudo make -C tcl${ver}/macosx install-deploy
425 sudo make -C tk${ver}/macosx install-deploy
427 - The GNUmakefile can also build a version of Wish.app that has the Tcl and Tk
428 frameworks embedded in its application package. This allows for standalone
429 deployment of the application with no installation required, e.g. from read-only
430 media. To build & install in this manner, use the 'embedded' variants of
431 the GNUmakefile targets.
432 For example, to build a standalone 'Wish.app' in ./emb/Applications/Utilities:
433 make -C tcl${ver}/macosx embedded
434 make -C tk${ver}/macosx embedded
435 sudo make -C tcl${ver}/macosx install-embedded INSTALL_ROOT=`pwd`/emb/
436 sudo make -C tk${ver}/macosx install-embedded INSTALL_ROOT=`pwd`/emb/
438 * if you've already built standard TclTkAqua, building embedded does not
439 require any new compiling or linking, so you can skip the first two makes.
440 (making relinking unnecessary was added with 8.4.2)
441 * the embedded frameworks include only optimized builds and no documentation.
442 * the standalone Wish has the directory Wish.app/Contents/lib in its
443 auto_path. Thus you can place tcl extensions in this directory (i.e. embed
444 them in the app package) and load them with [package require].
446 - It is possible to build Tk against an installed Tcl.framework; but you will
447 still need a tcl sourcetree in the location specified in TCL_SRC_DIR in
448 Tcl.framework/tclConfig.sh. Also, linking with Tcl.framework has to work exactly
449 as indicated in TCL_LIB_SPEC in Tcl.framework/tclConfig.sh.
450 If you used non-default install locations for Tcl.framework, specify them as
451 make overrides to the tk/macosx GNUmakefile, e.g.
452 make -C tk${ver}/macosx \
453 TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin
454 sudo make -C tk${ver}/macosx install \
455 TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin
456 The Makefile variables TCL_FRAMEWORK_DIR and TCLSH_DIR were added with Tk 8.4.3.
458 - To build a Tcl.framework and Tk.framework for use as subframeworks in another
459 framework, use the install-embedded target and set SUBFRAMEWORK=1. Set the
460 DYLIB_INSTALL_DIR variable to the path which should be the install_name path of
461 the shared library and set the DESTDIR variable to the pathname of a staging
462 directory where the frameworks will be written. The Tcl framework must be
464 For example, running the commands:
465 make -C ../tcl8.6/macosx install-embedded SUBFRAMEWORK=1 DESTDIR=/tmp/tcltk \
466 DYLIB_INSTALL_DIR=/Library/Frameworks/Some.framework/Versions/X.Y/Frameworks/Tcl.framework
467 make -C macosx install-embedded SUBFRAMEWORK=1 DESTDIR=/tmp/tcltk \
468 DYLIB_INSTALL_DIR=/Library/Frameworks/Some.framework/Versions/X.Y/Frameworks/Tk.framework
469 will produce a Tcl.framework and a Tk.framework usable as subframeworks of
470 Some.framework. The frameworks will be found in /tmp/tcltk/Frameworks/
472 5. Details regarding the macOS port of Tk.
473 -------------------------------------------
475 5.1 About the event loop
476 ~~~~~~~~~~~~~~~~~~~~~~~~
478 The main program in a typical OSX application looks like this (see
479 https://developer.apple.com/library/mac/documentation/Cocoa/\
480 Reference/ApplicationKit/Classes/NSApplication_Class)
482 void NSApplicationMain(int argc, char *argv[]) {
483 [NSApplication sharedApplication];
484 [NSBundle loadNibNamed:@"myMain" owner:NSApp];
487 Here NSApp is a standard global variable, initialized by the OS, which
488 points to an object in a subclass of NSApplication (called
489 TKApplication in the case of the macOS port of Tk).
491 The [NSApp run] method implements the event loop for a typical Mac
492 application. There are three key steps in the run method. First it
493 calls [NSApp finishLaunching], which creates the bouncing application
494 icon and does other mysterious things. Second it creates an
495 NSAutoreleasePool. Third, it starts an event loop which drains the
496 NSAutoreleasePool every time the queue is empty, and replaces the
497 drained pool with a new one. This third step is essential to
498 preventing memory leaks, since the internal methods of Appkit objects
499 all assume that an autorelease pool is in scope and will be drained
500 when the event processing cycle ends.
502 The macOS Tk application does not call the [NSApp run] method at
503 all. Instead it uses the event loop built in to Tk. So the
504 application must take care to replicate the important features of the
505 method ourselves. The way that autorelease pools are handled is
506 discussed in 5.2 below. Here we discuss the event handling itself.
508 The Tcl event loop simply consists of repeated calls to TclDoOneEvent.
509 Each call to TclDoOneEvent begins by collecting all pending events from
510 an "event source", converting them to Tcl events and adding them
511 to the Tcl event queue. For macOS, the event source is the NSApp
512 object, which maintains an event queue even though its run method
513 will never be called to process them. The NSApp provides methods for
514 inspecting the queue and removing events from it as well as the
515 [NSApp sendevent] which sends an event to all of the application's
516 NSWindows which can then send it to subwindows, etc.
518 The event collection process consists of first calling a platform
519 specific SetupProc and then a platform specific CheckProc. In
520 the macOS port, these are named TkMacOSXEventsSetupProc and
521 TkMacOSXEventsCheckProc.
523 It is important to understand that the Apple window manager does not
524 have the concept of an expose event. Their replacement for an expose
525 event is to have the window manager call the [NSView drawRect] method
526 in any situation where an expose event for that NSView would be
527 generated in X11. The [NSView drawRect] method is a no-op which is
528 expected to be overridden by any application. In the case of Tcl, the
529 replacement [NSView drawRect] method creates a Tcl expose event
530 for each dirty rectangle of the NSView, and then adds the expose
531 event to the Tcl queue.
534 5.2 Autorelease pools
535 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
537 In order to carry out the job of managing autorelease pools, which
538 would normally be handled by the [NSApp run] method, a private
539 NSAutoreleasePool* property is added to the TkApplication subclass of
540 NSApplication. The TkpInit function calls [NSApp _setup] which
541 initializes this property by creating an NSAutoreleasePool prior to
542 calling [NSApp finishLaunching]. This mimics the behavior of the
543 [NSApp run] method, which calls [NSApp finishLaunching] just before
544 starting the event loop.
546 Since the CheckProc function gets called for every Tk event, it is an
547 appropriate place to drain the main NSAutoreleasePool and replace it
548 with a new pool. This is done by calling the method [NSApp
549 _resetAutoreleasePool], where _resetAutoreleasePool is a method which
550 we define for the subclass. Unfortunately, by itself this is not
551 sufficient for safe memory managememt because, as was made painfully
552 evident with the release of OS X 10.13, it is possible for calls to
553 TclDoOneEvent, and hence to CheckProc, to be nested. Draining the
554 autorelease pool in a nested call leads to crashes as objects in use
555 by the outer call can get freed by the inner call and then reused later.
556 One particular situation where this happens is when a modal dialogue
557 gets posted by a Tk Application. To address this, the NSApp object
558 also implements a semaphore to prevent draining the autorelease pool
559 in nested calls to CheckProc.
561 One additional minor caveat for developers is that there are several
562 steps of the Tk initialization which precede the call to TkpInit.
563 Notably, the font package is initialized first. Since there is no
564 NSAutoreleasePool in scope prior to calling TkpInit, the functions
565 called in these preliminary stages need to create and drain their own
566 NSAutoreleasePools whenever they call methods of Appkit objects
569 5.3 Clipping regions and "ghost windows"
570 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
572 Another unusual aspect of the macOS port is its use of clipping
573 regions. It was part of Daniel Steffen's original design that the
574 TkWindowPrivate struct maintains three HIShapeRef regions, named
575 visRgn, aboveVisRgn and drawRgn. These regions are used as clipping
576 masks whenever drawing into an NSView. The visRgn is the bounding box
577 of the window with a rectangle removed for each subwindow and for each
578 sibling window at a higher stacking level. The drawRgn is the
579 intersection of the visRgn with the clipping rectangle of the
580 window. (Normally, the clipping rectangle is the same as the bounding
581 rectangle, but drawing can be clipped to a smaller rectangle by
582 calling TkpClipDrawableToRect.) The aboveVisRgn is the intersection of
583 the window's bounding rectangle with the bounding rectangle of the
584 parent window. Much of the code in tkMacOSXSubwindows.c is devoted to
585 rebuilding these clipping regions whenever something changes in the
586 layout of the windows. This turns out to be a tricky thing to do and
587 it is extremely prone to errors which can be difficult to trace.
589 It is not entirely clear what the original reason for using these
590 clipping regions was. But one benefit is that if they are correctly
591 maintained then it allows windows to be drawn in any order. You do
592 not have to draw them in the order of the window hierarchy. Each
593 window can draw its entire rectangle through its own mask and never
594 have to worry about drawing in the wrong place. It is likely that
595 the need for using clipping regions arose because, as Apple explicitly
596 states in the documentation for [NSView subviews],
598 "The order of the subviews may be considered as being
599 back-to-front, but this does not imply invalidation and drawing
602 In the early versions of the macOS port, buttons were implemented as
603 subviews of class TkButton. This probably exacerbated the likelihood
604 that Tk windows would need to be drawn in arbitrary order.
606 The most obvious side effect caused by not maintaining the clipping
607 regions is the appearance of so-called "ghost windows". A common
608 situation where these may arise is when a window containing buttons
609 is being scrolled. A user may see two images of the same button on
610 the screen, one in the pre-scroll location and one in the post-scroll
613 To see how these 'ghost windows' can arise, think about what happens if
614 the clipping regions are not maintained correctly. A window might
615 have a rectangle missing from its clipping region because that
616 rectangle is the bounding rectangle for a subwindow, say a button.
617 The parent should not draw in the missing rectangle since doing so
618 would trash the button. The button is responsible for drawing
619 there. Now imagine that the button gets moved, say by a scroll, but
620 the missing rectangle in the parent's clipping region does not get
621 moved correctly, or it gets moved later on, after the parent has
622 redrawn itself. The parent would still not be allowed to draw in the
623 old rectangle, so the user would continue to see the image of the
624 button in its old location, as well as another image in the new
625 location. This is a prototypical example of a "ghost window".
626 Anytime you see a "ghost window", you should suspect problems with the
627 updates to the clipping region visRgn. It is natural to look for
628 timing issues, race conditions, or other "event loop problems". But
629 in fact, the whole design of the code is to make those timing issues
630 irrelevant. As long as the clipping regions are correctly maintained
631 the timing does not matter. And if they are not correctly maintained
632 then you will see "ghost windows".
634 It is worth including a detailed description of one specific place
635 where the failure to correctly maintain clipping regions caused "ghost
636 window" artifacts that plagued the macOS port for years. These
637 occurred when scrolling a Text widget which contained embedded
638 subwindows. It involved some specific differences between the
639 low-level behavior of Apple's window manager versus those of the other
640 platforms, and the fix ultimately required changes in the generic Tk
641 implementation (documented in the comments in the DisplayText
644 The Text widget attempts to improve perfomance when scrolling by
645 minimizing the number of text lines which need to be redisplayed. It
646 does this by calling the platform-specific TkScrollWindow function
647 which uses a low-level routine to map one rectangle of the window to
648 another. The TkScrollWindow function returns a damage region which is
649 then used by the Text widget's DisplayText function to determine which
650 text lines need to be redrawn. On the unix and win platforms, this
651 damage region includes bounding rectangles for all embedded windows
652 inside the Text widget. The way that this works is system dependent.
653 On unix, the low level scrolling is done by XCopyRegion, which
654 generates a GraphicsExpose event for each embedded window. These
655 GraphicsExposed events are processsed within TkScrollWindow, using a
656 special handler which adds the bounding rectangle of each subwindow to
657 the damage region. On the win platform the damage region is built by
658 the low level function ScrollWindowEx, and it also includes bounding
659 rectangles for all embedded windows. This is possible because on X11
660 and Windows every Tk widget is also known to the window manager as a
661 window. The situation is different on macOS. The underlying object
662 for a top level window on macOS is the NSView. However, Apple
663 explicitly warns in its documentation that performance degradation
664 occurs when an NSView has more than about 100 subviews. A Text widget
665 with thousands of lines of text could easily contain more than 100
666 embedded windows. In fact, while the original Cocoa port of Tk did
667 use the NSButton object, which is derived from NSView, as the basis
668 for its Tk Buttons, that was changed in order to improve performance.
669 Moreover, the low level routine used for scrolling on macOS, namely
670 [NSView scrollrect:by], does not provide any damage information. So
671 TkScrollWindow needs to work differently on macOS. Since it would be
672 inefficient to iterate through all embedded windows in a Text widget,
673 looking for those which meet the scrolling area, the damage region
674 constructed by TkScrollWindow contains only the difference between the
675 source and destination rectangles for the scrolling. The embedded
676 windows are redrawn within the DisplayText function by some
677 conditional code which is only used for macOS.
679 6.0 Virtual events on macOS 10.14 and later
680 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
682 The 10.14 release added support for system appearance changes,
683 including a "Dark Mode" that renders all window frames and menus in
684 dark colors. Tk 8.6 provides three virtual events <<LightAqua>>,
685 <<DarkAqua>> and <<AppearanceChanged>>, to allow you to update your Tk
686 app's appearance when the system appearance changes. These events are
687 generated in [NSView effectiveAppearanceChanged], which is called by
688 the Apple window manager when the General Preferences is changed
689 either by switching between Light Mode and Dark Mode or by changing
690 the Accent Color or Highlight Color.
692 The <<AppearanceChanged>> virtual event has a data string which can be
693 accessed with the %d substitution. The format of the data string is
694 that it consists of 6 words:
695 "Appearance XXXX Accent YYYY Highlight ZZZZ"
696 For example, the following code will print the current appearance
697 name, accent color and highlight color when the <<AppearanceChanged>>
700 bind . <<AppearanceChanged>> {
701 array set data [split %d]
702 puts " Appearance: $data(Appearance)"
703 puts " Accent: $data(Accent)"
704 puts " Highlight: $data(Highlight)\n"
710 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
712 With 8.6.10, Tk supports the Mac's NSServices API, documented at
713 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/SysServices/introduction.html#//apple_ref/doc/uid/10000101-SW1
714 and in TIP 536 and Tk's man page. Tk presents a simple,
715 straightforward API to implement the Services functionality.
717 The Tk implementation of the NSServices API is intended for standalone
718 applications, such as one wrapped by the standalone version of Wish
719 and re-named into a different application. In particular such an
720 application would specify its own unique CFBundleIdentifier in its
721 Info.plist file. During development, however, if Wish itself is being
722 used as the receiver, it may be necessary to take some care to ensure
723 that the correct version of Wish.app is available as a receiver of
726 When one macOS app uses NSServices to send data to another app that is
727 not running, LaunchServices will launch the receiver. LaunchServices
728 assumes that the CFBundleIdentifier uniquely identifies an app among
729 all of the apps installed on a system. But this may not be the case
730 for Wish.app if, for example, you have compiled Tk from source at some
731 time in the past. In that case the Tk build directory will contain
732 its own copy of Wish.app that will be visible to LaunchServices. It
733 may be necessary when testing your app to take some steps to ensure
734 that LaunchServices is launching the correct Wish.app. Instructions
735 for doing this are provided below.
737 The command line tool which manages the LaunchServices database has
738 an amazingly unwieldy path name. So, first, run this command:
740 alias lsregister='/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister'
742 Then you can reset the LaunchServices database like this:
747 To find out which versions of Wish.app have been located by
750 $ lsregister -dump | grep path | grep Wish
752 If more than one version of Wish is showing up in this list, eliminate
753 all of the unintended targets by running
755 lsregister -u /path/to/bad/Wish.app
757 Continue this until only the correct version of Wish shows up in the