Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch OSX_redraw_artifacts Excluding Merge-Ins
This is equivalent to a diff from f1083eed to 05ad96e1
2017-11-07
| ||
20:55 | Merge OSX_redraw_artifacts branch. This fixes [fab5fed65e], [40a9abb9db] and [92e614e612]. Great thanks to Marc Culler! check-in: 4986b4e5 user: fvogel tags: core-8-6-branch | |
20:50 | Fix utterly minor formatting things in macosx/README Closed-Leaf check-in: 05ad96e1 user: fvogel tags: OSX_redraw_artifacts, bug-92e614e612 | |
16:14 | Update of the macosx README file to include explanations of the changes added to address crashes that appeared with the release of OSX 10.13 as well as the drawing artifacts being dealt with in the OSX_redraw_artifacts branch. check-in: bd1e6669 user: culler tags: OSX_redraw_artifacts, bug-92e614e612 | |
2017-11-03
| ||
22:42 | Fix [0ef1c52736]: OS X - tests menu-22.[345] hang check-in: 64e85211 user: fvogel tags: core-8-6-branch | |
22:41 | Fix [0ef1c52736]: OS X - tests menu-22.[345] hang Closed-Leaf check-in: 2a9e1859 user: fvogel tags: bug-0ef1c52736 | |
2017-11-01
| ||
20:01 | merge core-8-6-branch check-in: 7dde71b3 user: fvogel tags: OSX_redraw_artifacts, bug-92e614e612 | |
2017-10-28
| ||
13:51 | Fix [ce62c81bae] text-37.1 fails check-in: c88ea97b user: fvogel tags: trunk | |
13:51 | Fix [ce62c81bae] text-37.1 fails check-in: f1083eed user: fvogel tags: core-8-6-branch | |
2017-10-27
| ||
14:13 | tkWinMenu.c: In function ‘TkWinMenuKeyObjCmd’: tkWinMenu.c:2085:16: warning: result of ‘273 << 29’ requires 39 bits to represent, but ‘int’ only has 32 bits [-Wshift-overflow=] | (0x111 << 29) | (1 << 30) | (1 << 31)); ^~ check-in: 139b66da user: jan.nijtmans tags: core-8-6-branch | |
Changes to generic/tkTextDisp.c.
︙ | ︙ | |||
4270 4271 4272 4273 4274 4275 4276 | dlPtr->oldY = dlPtr->y; if (dlPtr->nextPtr == dlPtr2) { break; } dlPtr = dlPtr->nextPtr; } | < | 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 | dlPtr->oldY = dlPtr->y; if (dlPtr->nextPtr == dlPtr2) { break; } dlPtr = dlPtr->nextPtr; } /* * Scan through the lines following the copied ones to see if we are * going to overwrite them with the copy operation. If so, mark them * for redisplay. */ for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) { |
︙ | ︙ | |||
4294 4295 4296 4297 4298 4299 4300 | * calling TextInvalidateRegion to mark the display blocks as stale. */ damageRgn = TkCreateRegion(); if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC, dInfoPtr->x, oldY, dInfoPtr->maxX-dInfoPtr->x, height, 0, y-oldY, damageRgn)) { | < < | 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 | * calling TextInvalidateRegion to mark the display blocks as stale. */ damageRgn = TkCreateRegion(); if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC, dInfoPtr->x, oldY, dInfoPtr->maxX-dInfoPtr->x, height, 0, y-oldY, damageRgn)) { TextInvalidateRegion(textPtr, damageRgn); } numCopies++; TkDestroyRegion(damageRgn); } /* * Clear the REDRAW_PENDING flag here. This is actually pretty tricky. We |
︙ | ︙ | |||
4438 4439 4440 4441 4442 4443 4444 4445 4446 | #ifndef TK_NO_DOUBLE_BUFFERING Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap); #endif /* TK_NO_DOUBLE_BUFFERING */ return; } dlPtr->oldY = dlPtr->y; dlPtr->flags &= ~(NEW_LAYOUT | OLD_Y_INVALID); } else if (dlPtr->chunkPtr != NULL && ((dlPtr->y < 0) || (dlPtr->y + dlPtr->height > dInfoPtr->maxY))) { | > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | > > > > | 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 | #ifndef TK_NO_DOUBLE_BUFFERING Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap); #endif /* TK_NO_DOUBLE_BUFFERING */ return; } dlPtr->oldY = dlPtr->y; dlPtr->flags &= ~(NEW_LAYOUT | OLD_Y_INVALID); #ifdef MAC_OSX_TK } else if (dlPtr->chunkPtr != NULL) { /* * On macOS we need to redisplay all embedded windows which * were moved by the call to TkScrollWindows above. This is * not necessary on Unix or Windows because XScrollWindow will * have included the bounding rectangles of all of these * windows in the damage region. The macosx implementation of * TkScrollWindow does not do this. It simply generates a * damage region which is the scroll source rectangle minus * the scroll destination rectangle. This is because there is * no efficient process available for iterating through the * subwindows which meet the scrolled area. (On Unix this is * handled by GraphicsExpose events generated by XCopyArea and * on Windows by ScrollWindowEx. On macOS the low level * scrolling is accomplished by calling [view scrollRect:by:]. * This method does not provide any damage information and, in * any case, could not be aware of Tk windows which were not * based on NSView objects. * * On the other hand, this loop is already iterating through * all embedded windows which could possibly have been moved * by the scrolling. So it is as efficient to redisplay them * here as it would have been if they had been redisplayed by * the call to TextInvalidateRegion above. */ #else } else if (dlPtr->chunkPtr != NULL && ((dlPtr->y < 0) || (dlPtr->y + dlPtr->height > dInfoPtr->maxY))) { /* * On platforms other than the Mac: * * It's the first or last DLine which are also overlapping the * top or bottom of the window, but we decided above it wasn't * necessary to display them (we were able to update them by * scrolling). This is fine, except that if the lines contain * any embedded windows, we must still call the display proc * on them because they might need to be unmapped or they * might need to be moved to reflect their new position. * Otherwise, everything else moves, but the embedded window * doesn't! * * So, we loop through all the chunks, calling the display * proc of embedded windows only. */ #endif register TkTextDispChunk *chunkPtr; for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL); chunkPtr = chunkPtr->nextPtr) { int x; if (chunkPtr->displayProc != TkTextEmbWinDisplayProc) { continue; } |
︙ | ︙ | |||
4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 | * as being off-screen to the left (the displayProc * may not be able to tell if something is off to the * right). */ x = -chunkPtr->width; } TkTextEmbWinDisplayProc(textPtr, chunkPtr, x, dlPtr->spaceAbove, dlPtr->height-dlPtr->spaceAbove-dlPtr->spaceBelow, dlPtr->baseline - dlPtr->spaceAbove, NULL, (Drawable) None, dlPtr->y + dlPtr->spaceAbove); } | > > > > > > < | 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 | * as being off-screen to the left (the displayProc * may not be able to tell if something is off to the * right). */ x = -chunkPtr->width; } if (tkTextDebug) { char string[TK_POS_CHARS]; TkTextPrintIndex(textPtr, &dlPtr->index, string); LOG("tk_textEmbWinDisplay", string); } TkTextEmbWinDisplayProc(textPtr, chunkPtr, x, dlPtr->spaceAbove, dlPtr->height-dlPtr->spaceAbove-dlPtr->spaceBelow, dlPtr->baseline - dlPtr->spaceAbove, NULL, (Drawable) None, dlPtr->y + dlPtr->spaceAbove); } } } #ifndef TK_NO_DOUBLE_BUFFERING Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap); #endif /* TK_NO_DOUBLE_BUFFERING */ } |
︙ | ︙ |
Changes to library/demos/twind.tcl.
︙ | ︙ | |||
79 80 81 82 83 84 85 | $t insert end "You can also create multiple text widgets each of which " $t insert end "display the same underlying text. Click this button to " $t window create end \ -create {button %W.peer -text "Make A Peer" -command "textMakePeer %W" \ -cursor top_left_arrow} -padx 3 $t insert end " widget. Notice how peer widgets can have different " $t insert end "font settings, and by default contain all the images " | | | | | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | $t insert end "You can also create multiple text widgets each of which " $t insert end "display the same underlying text. Click this button to " $t window create end \ -create {button %W.peer -text "Make A Peer" -command "textMakePeer %W" \ -cursor top_left_arrow} -padx 3 $t insert end " widget. Notice how peer widgets can have different " $t insert end "font settings, and by default contain all the images " $t insert end "of the 'parent', but that the embedded windows, " $t insert end "such as buttons may not appear in the peer. To ensure " $t insert end "that embedded windows appear in all peers you can set the " $t insert end "'-create' option to a script or a string containing %W. " $t insert end "(The plot above and the 'Make A Peer' button are " $t insert end "designed to show up in all peers.) A good use of " $t insert end "peers is for " $t window create end \ -create {button %W.split -text "Split Windows" -command "textSplitWindow %W" \ -cursor top_left_arrow} -padx 3 $t insert end " \n\n" $t insert end "Users of previous versions of Tk will also be interested " |
︙ | ︙ | |||
108 109 110 111 112 113 114 115 116 117 118 119 120 121 | $t insert end "text widget (\"Default\" restores the color to " $t insert end "its default). If you click on the button labeled " $t insert end "\"Short\", it changes to a longer string so that " $t insert end "you can see how the text widget automatically " $t insert end "changes the layout. Click on the button again " $t insert end "to restore the short string.\n" button $t.default -text Default -command "embDefBg $t" \ -cursor top_left_arrow $t window create end -window $t.default -padx 3 global embToggle set embToggle Short checkbutton $t.toggle -textvariable embToggle -indicatoron 0 \ -variable embToggle -onvalue "A much longer string" \ | > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | $t insert end "text widget (\"Default\" restores the color to " $t insert end "its default). If you click on the button labeled " $t insert end "\"Short\", it changes to a longer string so that " $t insert end "you can see how the text widget automatically " $t insert end "changes the layout. Click on the button again " $t insert end "to restore the short string.\n" $t insert end "\nNOTE: these buttons will not appear in peers!\n" "peer_warning" button $t.default -text Default -command "embDefBg $t" \ -cursor top_left_arrow $t window create end -window $t.default -padx 3 global embToggle set embToggle Short checkbutton $t.toggle -textvariable embToggle -indicatoron 0 \ -variable embToggle -onvalue "A much longer string" \ |
︙ | ︙ | |||
159 160 161 162 163 164 165 | $t window create end -window $t.bigP $t window create end -window $t.smallP $t insert end "\n\nFinally, images fit comfortably in text widgets too:" $t image create end -image \ [image create photo -file [file join $tk_demoDirectory images ouster.png]] | < | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | $t window create end -window $t.bigP $t window create end -window $t.smallP $t insert end "\n\nFinally, images fit comfortably in text widgets too:" $t image create end -image \ [image create photo -file [file join $tk_demoDirectory images ouster.png]] proc textWindBigB w { $w configure -borderwidth 15 } proc textWindBigH w { $w configure -highlightthickness 15 |
︙ | ︙ | |||
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 | set n 1 while {[winfo exists .peer$n]} { incr n } set w [toplevel .peer$n] wm title $w "Text Peer #$n" frame $w.f -highlightthickness 1 -borderwidth 1 -relief sunken set t [$parent peer create $w.f.text -yscrollcommand "$w.scroll set" \ -borderwidth 0 -highlightthickness 0] pack $t -expand yes -fill both ttk::scrollbar $w.scroll -command "$t yview" pack $w.scroll -side right -fill y pack $w.f -expand yes -fill both } proc textSplitWindow {textW} { if {$textW eq ".twind.f.text"} { if {[winfo exists .twind.peer]} { destroy .twind.peer } else { set parent [winfo parent $textW] set w [winfo parent $parent] set t [$textW peer create $w.peer \ -yscrollcommand "$w.scroll set"] $w.pane add $t } } else { return } } | > > | 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | set n 1 while {[winfo exists .peer$n]} { incr n } set w [toplevel .peer$n] wm title $w "Text Peer #$n" frame $w.f -highlightthickness 1 -borderwidth 1 -relief sunken set t [$parent peer create $w.f.text -yscrollcommand "$w.scroll set" \ -borderwidth 0 -highlightthickness 0] $t tag configure peer_warning -font boldFont pack $t -expand yes -fill both ttk::scrollbar $w.scroll -command "$t yview" pack $w.scroll -side right -fill y pack $w.f -expand yes -fill both } proc textSplitWindow {textW} { if {$textW eq ".twind.f.text"} { if {[winfo exists .twind.peer]} { destroy .twind.peer } else { set parent [winfo parent $textW] set w [winfo parent $parent] set t [$textW peer create $w.peer \ -yscrollcommand "$w.scroll set"] $t tag configure peer_warning -font boldFont $w.pane add $t } } else { return } } |
Changes to macosx/README.
|
| | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | Tcl/Tk macOS README ---------------------- This is the README file for the macOS/Darwin version of Tcl/Tk. 1. Where to go for support -------------------------- - The tcl-mac mailing list on sourceforge is the best place to ask questions specific to Tcl & Tk on macOS: http://lists.sourceforge.net/lists/listinfo/tcl-mac (this page also has a link to searchable archives of the list, please check them before asking on the list, many questions have already been answered). - For general Tcl/Tk questions, the newsgroup comp.lang.tcl is your best bet: http://groups.google.com/group/comp.lang.tcl/ - The Tcl'ers Wiki also has many pages dealing with Tcl & Tk on macOS, see http://wiki.tcl.tk/_/ref?N=3753 http://wiki.tcl.tk/_/ref?N=8361 - Please report bugs with Tk on macOS to the tracker: http://core.tcl.tk/tk/reportlist 2. Using Tcl/Tk on macOS --------------------------- - There are two versions of Tk available on macOS: TkAqua using the native aqua widgets and look&feel, and TkX11 using the traditional unix X11 wigets. TkX11 requires an X11 server to be installed, such as Apple's X11 (which is available as an optional or default install on recent macOS). TkAqua and TkX11 can be distinguished at runtime via [tk windowingsystem]. - At a minimum, macOS 10.3 is required to run Tcl and TkX11. TkAqua requires macOS 10.5 or later (starting with the Cocoa-based Tk 8.5.7). - Unless weak-linking is used, Tcl/Tk built on macOS 10.x will not run on 10.y with y < x; on the other hand Tcl/Tk built on 10.y will always run on 10.x with y <= x (but without any of the fixes and optimizations that would be available in a binary built on 10.x). Weak-linking is available on OS X 10.2 or later, it additionally allows Tcl/Tk built on 10.x to run on any 10.y with x > y >= z (for a chosen z >= 2). - Wish checks the Resources/Scripts directory in its application bundle for a |
︙ | ︙ | |||
58 59 60 61 62 63 64 | $pkg/Resources/Scripts/pkgIndex.tcl as well as the usual $pkg/pkgIndex.tcl. This allows building extensions as frameworks with all script files contained in the Resources/Scripts directory of the framework. - [load]able binary extensions can linked as either ordinary shared libraries (.dylib) or as MachO bundles (since 8.4.10/8.5a3); bundles have the advantage that they are [load]ed more efficiently from a tcl VFS (no temporary copy to the | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | $pkg/Resources/Scripts/pkgIndex.tcl as well as the usual $pkg/pkgIndex.tcl. This allows building extensions as frameworks with all script files contained in the Resources/Scripts directory of the framework. - [load]able binary extensions can linked as either ordinary shared libraries (.dylib) or as MachO bundles (since 8.4.10/8.5a3); bundles have the advantage that they are [load]ed more efficiently from a tcl VFS (no temporary copy to the native filesystem required), and prior to macOS 10.5, only bundles can be [unload]ed. - The 'deploy' target of macosx/GNUmakefile installs the html manpages into the standard documentation location in the Tcl/Tk frameworks: Tcl.framework/Resources/Documentation/Reference/Tcl Tk.framework/Resources/Documentation/Reference/Tk No nroff manpages are installed by default by the GNUmakefile. |
︙ | ︙ | |||
159 160 161 162 163 164 165 | application's Info.plist (or displaying an alert if no Help Book is set). This action can be customized by defining a procedure named [tk::mac::ShowHelp], if present, this procedure is invoked instead by the standard Help menu item. Support for the Window menu and [tk::mac::ShowHelp] was added with the Cocoa-based Tk 8.5.7. - The TkAqua-specific command [tk::unsupported::MacWindowStyle style] is used to | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | application's Info.plist (or displaying an alert if no Help Book is set). This action can be customized by defining a procedure named [tk::mac::ShowHelp], if present, this procedure is invoked instead by the standard Help menu item. Support for the Window menu and [tk::mac::ShowHelp] was added with the Cocoa-based Tk 8.5.7. - The TkAqua-specific command [tk::unsupported::MacWindowStyle style] is used to get and set macOS-specific toplevel window class and attributes. Note that the window class and many attributes have to be set before the window is first mapped for the change to have any effect. The command has the following syntax: tk::unsupported::MacWindowStyle style window ?class? ?attributes? The 2 argument form returns a list of the current class and attributes for the given window. The 3 argument form sets the class for the given window using the default attributes for that class. The 4 argument form sets the class and the |
︙ | ︙ | |||
212 213 214 215 216 217 218 | magenta #ff00ff orange #ff8000 purple #800080 brown #996633 clear systemTransparent - The Cocoa-based TkAqua can be distinguished from the older Carbon-based | | | | | | | | 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | magenta #ff00ff orange #ff8000 purple #800080 brown #996633 clear systemTransparent - The Cocoa-based TkAqua can be distinguished from the older Carbon-based version via the [winfo server .] command, example output on macOS 10.5.7: Cocoa-based: CG409.3 Apple AppKit GC 949.46 macOS 1057 Carbon-based: QD10R30 Apple 1057 - If you want to use Remote Debugging with Xcode, you need to set the environment variable XCNOSTDIN to 1 in the Executable editor for Wish. That will cause us to force closing stdin & stdout. Otherwise, given how Xcode launches Wish remotely, they will be left open and then Wish & gdb will fight for stdin. 3. Building Tcl/Tk on macOS ------------------------------ - At least macOS 10.3 is required to build Tcl and TkX11, and macOS 10.5 is required to build TkAqua. Apple's Xcode Developer Tools need to be installed (only the most recent version matching your OS release is supported), the Xcode installer is available on Mac OS X install media or may be present in /Applications/Installers on Macs that came with OS X preinstalled. The most recent version can always be downloaded from the ADC website http://connect.apple.com (free ADC membership required). - Tcl/Tk are most easily built as macOS frameworks via GNUmakefile in tcl/macosx and tk/macosx (see below for details), but can also be built with the standard unix configure and make buildsystem in tcl/unix resp. tk/unix as on any other unix platform (indeed, the GNUmakefiles are just wrappers around the unix buildsystem). The macOS specific configure flags are --enable-aqua, --enable-framework and --disable-corefoundation (which disables CF and notably reverts to the standard select based notifier). Note that --enable-aqua is incompatible with --disable-corefoundation (for both Tcl and Tk configure). - It is also possible to build with the Xcode IDE via the projects in tk/macosx, take care to use the project matching your DevTools and OS version: Tk.xcode: for Xcode 3.1 on 10.5 |
︙ | ︙ | |||
288 289 290 291 292 293 294 | directories are named differently, e.g. '../../tcl8.6' and '../../tk8.6', you need to manually change the TCL_SRCROOT and TK_SRCROOT settings by editing your ${USER}.pbxuser file (located inside the Tk.xcodeproj bundle directory) with a text editor. - To build universal binaries outside of the Xcode IDE, set CFLAGS as follows: export CFLAGS="-arch i386 -arch x86_64 -arch ppc" | | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | directories are named differently, e.g. '../../tcl8.6' and '../../tk8.6', you need to manually change the TCL_SRCROOT and TK_SRCROOT settings by editing your ${USER}.pbxuser file (located inside the Tk.xcodeproj bundle directory) with a text editor. - To build universal binaries outside of the Xcode IDE, set CFLAGS as follows: export CFLAGS="-arch i386 -arch x86_64 -arch ppc" This requires macOS 10.4 and Xcode 2.4 (or Xcode 2.2 if -arch x86_64 is omitted, but _not_ Xcode 2.1) and will work on any architecture (on PowerPC Tiger you need to add "-isysroot /Developer/SDKs/MacOSX10.4u.sdk"). Note that configure requires CFLAGS to contain a least one architecture that can be run on the build machine (i.e. ppc on G3/G4, ppc or ppc64 on G5, ppc or i386 on Core and ppc, i386 or x86_64 on Core2/Xeon). Universal builds of Tcl TEA extensions are also possible with CFLAGS set as above, they will be [load]able by universal as well as thin binaries of Tcl. |
︙ | ︙ | |||
385 386 387 388 389 390 391 | make overrides to the tk/macosx GNUmakefile, e.g. make -C tk${ver}/macosx \ TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin sudo make -C tk${ver}/macosx install \ TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin The Makefile variables TCL_FRAMEWORK_DIR and TCLSH_DIR were added with Tk 8.4.3. | | | | > > > | > > > > > | | | | | | | > > | > > > > > > > > > > > > > | > > > > > > > > > | | > > > > > > | | | | < < < < < < < < < | | | > > > > > > > > > | | | | | > > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 | make overrides to the tk/macosx GNUmakefile, e.g. make -C tk${ver}/macosx \ TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin sudo make -C tk${ver}/macosx install \ TCL_FRAMEWORK_DIR=$HOME/Library/Frameworks TCLSH_DIR=$HOME/usr/bin The Makefile variables TCL_FRAMEWORK_DIR and TCLSH_DIR were added with Tk 8.4.3. 4. Details regarding the macOS port of Tk. ------------------------------------------- 4.1 About the event loop ~~~~~~~~~~~~~~~~~~~~~~~~ The main program in a typical OSX application looks like this (see https://developer.apple.com/library/mac/documentation/Cocoa/\ Reference/ApplicationKit/Classes/NSApplication_Class) void NSApplicationMain(int argc, char *argv[]) { [NSApplication sharedApplication]; [NSBundle loadNibNamed:@"myMain" owner:NSApp]; [NSApp run]; } Here NSApp is a standard global variable, initialized by the OS, which points to an object in a subclass of NSApplication (called TKApplication in the case of the macOS port of Tk). The [NSApp run] method implements the event loop for a typical Mac application. There are three key steps in the run method. First it calls [NSApp finishLaunching], which creates the bouncing application icon and does other mysterious things. Second it creates an NSAutoreleasePool. Third, it starts an event loop which drains the NSAutoreleasePool every time the queue is empty, and replaces the drained pool with a new one. This third step is essential to preventing memory leaks, since the internal methods of Appkit objects all assume that an autorelease pool is in scope and will be drained when the event processing cycle ends. The macOS Tk application does not call the [NSApp run] method at all. Instead it uses the event loop built in to Tk. So the application must take care to replicate the important features of the method ourselves. The way that autorelease pools are handled is discussed in 4.2 below. Here we discuss the event handling itself. The Tcl event loop simply consists of repeated calls to TclDoOneEvent. Each call to TclDoOneEvent begins by collecting all pending events from an "event source", converting them to Tcl events and adding them to the Tcl event queue. For macOS, the event source is the NSApp object, which maintains an event queue even though its run method will never be called to process them. The NSApp provides methods for inspecting the queue and removing events from it as well as the [NSApp sendevent] which sends an event to all of the application's NSWindows which can then send it to subwindows, etc. The event collection process consists of first calling a platform specific SetupProc and then a platform specific CheckProc. In the macOS port, these are named TkMacOSXEventsSetupProc and TkMacOSXEventsCheckProc. It is important to understand that the Apple window manager does not have the concept of an expose event. Their replacement for an expose event is to have the window manager call the [NSView drawRect] method in any situation where an expose event for that NSView would be generated in X11. The [NSView drawRect] method is a no-op which is expected to be overridden by any application. In the case of Tcl, the replacement [NSView drawRect] method creates a Tcl expose event for each dirty rectangle of the NSView, and then adds the expose event to the Tcl queue. 4.2 Autorelease pools ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In order to carry out the job of managing autorelease pools, which would normally be handled by the [NSApp run] method, a private NSAUtoreleasePool* property is added to the TkApplication subclass of NSApplication. The TkpInit function calls [NSApp _setup] which initializes this property by creating an NSAutoreleasePool. A bit later on, TkpInit calls [NSAPP _setupEventLoop] which in turn calls the [NSApp finishLaunching] method. Since the CheckProc function gets called for every Tk event, it is an appropriate place to drain the main NSAutoreleasePool and replace it with a new pool. This is done by calling the method [NSApp _resetAutoreleasePool], where _resetAutoreleasePool is a method which we define for the subclass. Unfortunately, by itself this is not sufficient for safe memory managememt because, as was made painfully evident with the release of OS X 10.13, it is possible for calls to TclDoOneEvent, and hence to CheckProc, to be nested. Draining the autorelease pool in a nested call leads to crashes as objects in use by the outer call can get freed by the inner call and then reused later. One particular situation where this happens is when a modal dialogue gets posted by a Tk Application. To address this, the NSApp object also implements a semaphore to prevent draining the autorelease pool in nested calls to CheckProc. One additional minor caveat for developers is that there are several steps of the Tk initialization which precede the call to TkpInit. Notably, the font package is initialized first. Since there is no NSAUtoreleasePool in scope prior to calling TkpInit, the functions called in these preliminary stages need to create and drain their own NSAutoreleasePools whenever they call methods of Appkit objects (e.g. NSFont). 4.3 Clipping regions and "ghost windows" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Another unusual aspect of the macOS port is its use of clipping regions. It was part of Daniel Steffen's original design that the TkWindowPrivate struct maintains three HIShapeRef regions, named visRgn, aboveVisRgn and drawRgn. These regions are used as clipping masks whenever drawing into an NSView. The visRgn is the bounding box of the window with a rectangle removed for each subwindow and for each sibling window at a higher stacking level. The drawRgn is the intersection of the visRgn with the clipping rectangle of the window. (Normally, the clipping rectangle is the same as the bounding rectangle, but drawing can be clipped to a smaller rectangle by calling TkpClipDrawableToRect.) The aboveVisRgn is the intersection of the window's bounding rectangle with the bounding rectangle of the parent window. Much of the code in tkMacOSXSubindows.c is devoted to rebuilding these clipping regions whenever something changes in the layout of the windows. This turns out to be a tricky thing to do and it is extremely prone to errors which can be difficult to trace. It is not entirely clear what the original reason for using these clipping regions was. But one benefit is that if they are correctly maintained then it allows windows to be drawn in any order. You do not have to draw them in the order of the window hierarchy. Each window can draw its entire rectangle through its own mask and never have to worry about drawing in the wrong place. It is likely that the need for using clipping regions arose because, as Apple explicitly states in the documentation for [NSView subviews], "The order of the subviews may be considered as being back-to-front, but this does not imply invalidation and drawing behavior." In the early versions of the macOS port, buttons were implemented as subviews of class TkButton. This probably exacerbated the likelihood that Tk windows would need to be drawn in arbitrary order. The most obvious side effect caused by not maintaining the clipping regions is the appearance of so-called "ghost windows". A common situation where these may arise is when a window containing buttons is being scrolled. A user may see two images of the same button on the screen, one in the pre-scroll location and one in the post-scroll location. To see how these 'ghost windows' can arise, think about what happens if the clipping regions are not maintained correctly. A window might have a rectangle missing from its clipping region because that rectangle is the bounding rectangle for a subwindow, say a button. The parent should not draw in the missing rectangle since doing so would trash the button. The button is responsible for drawing there. Now imagine that the button gets moved, say by a scroll, but the missing rectangle in the parent's clipping region does not get moved correctly, or it gets moved later on, after the parent has redrawn itself. The parent would still not be allowed to draw in the old rectangle, so the user would continue to see the image of the button in its old location, as well as another image in the new location. This is a prototypical example of a "ghost window". Anytime you see a "ghost window", you should suspect problems with the updates to the clipping region visRgn. It is natural to look for timing issues, race conditions, or other "event loop problems". But in fact, the whole design of the code is to make those timing issues irrelevant. As long as the clipping regions are correctly maintained the timing does not matter. And if they are not correctly maintained then you will see "ghost windows". It is worth including a detailed description of one specific place where the failure to correctly maintain clipping regions caused "ghost window" artifacts that plagued the macOS port for years. These occurred when scrolling a Text widget which contained embedded subwindows. It involved some specific differences between the low-level behavior of Apple's window manager versus those of the other platforms, and the fix ultimately required changes in the generic Tk implementation (documented in the comments in the DisplayText function). The Text widget attempts to improve perfomance when scrolling by minimizing the number of text lines which need to be redisplayed. It does this by calling the platform-specific TkScrollWindow function which uses a low-level routine to map one rectangle of the window to another. The TkScrollWindow function returns a damage region which is then used by the Text widget's DisplayText function to determine which text lines need to be redrawn. On the unix and win platforms, this damage region includes bounding rectangles for all embedded windows inside the Text widget. The way that this works is system dependent. On unix, the low level scrolling is done by XCopyRegion, which generates a GraphicsExpose event for each embedded window. These GraphicsExposed events are processsed within TkScrollWindow, using a special handler which adds the bounding rectangle of each subwindow to the damage region. On the win platform the damage region is built by the low level function ScrollWindowEx, and it also includes bounding rectangles for all embedded windows. This is possible because on X11 and Windows every Tk widget is also known to the window manager as a window. The situation is different on macOS. The underlying object for a top level window on macOS is the NSView. However, Apple explicitly warns in its documentation that performance degradation occurs when an NSView has more than about 100 subviews. A Text widget with thousands of lines of text could easily contain more than 100 embedded windows. In fact, while the original Cocoa port of Tk did use the NSButton object, which is derived from NSView, as the basis for its Tk Buttons, that was changed in order to improve performance. Moreover, the low level routine used for scrolling on macOS, namely [NSView scrollrect:by], does not provide any damage information. So TkScrollWindow needs to work differently on macOS. Since it would be inefficient to iterate through all embedded windows in a Text widget, looking for those which meet the scrolling area, the damage region constructed by TkScrollWindow contains only the difference between the source and destination rectangles for the scrolling. The embedded windows are redrawn within the DisplayText function by some conditional code which is only used for macOS. |
Changes to macosx/tkMacOSXDraw.c.
︙ | ︙ | |||
1537 1538 1539 1540 1541 1542 1543 | /* Compute the damage. */ dmgRgn = HIShapeCreateMutableWithRect(&srcRect); extraRgn = HIShapeCreateWithRect(&dstRect); ChkErr(HIShapeDifference, dmgRgn, extraRgn, (HIMutableShapeRef) dmgRgn); result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; | | < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 | /* Compute the damage. */ dmgRgn = HIShapeCreateMutableWithRect(&srcRect); extraRgn = HIShapeCreateWithRect(&dstRect); ChkErr(HIShapeDifference, dmgRgn, extraRgn, (HIMutableShapeRef) dmgRgn); result = HIShapeIsEmpty(dmgRgn) ? 0 : 1; /* Convert to Tk coordinates, offset by the window origin. */ TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); if (extraRgn) { CFRelease(extraRgn); } /* Scroll the rectangle. */ [view scrollRect:scrollSrc by:NSMakeSize(dx, -dy)]; } } else { dmgRgn = HIShapeCreateEmpty(); TkMacOSXSetWithNativeRegion(damageRgn, dmgRgn); } if (dmgRgn) { |
︙ | ︙ |
Changes to macosx/tkMacOSXPrivate.h.
︙ | ︙ | |||
340 341 342 343 344 345 346 | @interface TKContentView(TKKeyEvent) - (void) deleteWorkingText; @end @interface TKContentView(TKWindowEvent) - (void) drawRect: (NSRect) rect; - (void) generateExposeEvents: (HIShapeRef) shape; | < | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | @interface TKContentView(TKKeyEvent) - (void) deleteWorkingText; @end @interface TKContentView(TKWindowEvent) - (void) drawRect: (NSRect) rect; - (void) generateExposeEvents: (HIShapeRef) shape; - (void) viewDidEndLiveResize; - (void) tkToolbarButton: (id) sender; - (BOOL) isOpaque; - (BOOL) wantsDefaultClipping; - (BOOL) acceptsFirstResponder; - (void) keyDown: (NSEvent *) theEvent; @end |
︙ | ︙ |
Changes to macosx/tkMacOSXWindowEvent.c.
︙ | ︙ | |||
359 360 361 362 363 364 365 | event.xany.display = Tk_Display(winPtr); event.type = Expose; event.xexpose.x = damageBounds.origin.x - bounds.origin.x; event.xexpose.y = damageBounds.origin.y - bounds.origin.y; event.xexpose.width = damageBounds.size.width; event.xexpose.height = damageBounds.size.height; event.xexpose.count = 0; | | | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 | event.xany.display = Tk_Display(winPtr); event.type = Expose; event.xexpose.x = damageBounds.origin.x - bounds.origin.x; event.xexpose.y = damageBounds.origin.y - bounds.origin.y; event.xexpose.width = damageBounds.size.width; event.xexpose.height = damageBounds.size.height; event.xexpose.count = 0; Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); #ifdef TK_MAC_DEBUG_DRAWING NSLog(@"Expose %p {{%d, %d}, {%d, %d}}", event.xany.window, event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height); #endif /* |
︙ | ︙ | |||
911 912 913 914 915 916 917 | /* Core method of this class: generates expose events for redrawing. If the * Tcl_ServiceMode is set to TCL_SERVICE_ALL then the expose events will be * immediately removed from the Tcl event loop and processed. Typically, they * should be queued, however. */ - (void) generateExposeEvents: (HIShapeRef) shape { | < < < < < < < > | 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 | /* Core method of this class: generates expose events for redrawing. If the * Tcl_ServiceMode is set to TCL_SERVICE_ALL then the expose events will be * immediately removed from the Tcl event loop and processed. Typically, they * should be queued, however. */ - (void) generateExposeEvents: (HIShapeRef) shape { unsigned long serial; CGRect updateBounds; int updatesNeeded; TkWindow *winPtr = TkMacOSXGetTkWindow([self window]); if (!winPtr) { return; } /* Generate Tk Expose events. */ HIShapeGetBounds(shape, &updateBounds); |
︙ | ︙ |
Changes to tests/textDisp.test.
︙ | ︙ | |||
944 945 946 947 948 949 950 951 952 953 954 955 956 957 | set scrollInfo unchanged .t insert end xxxxxxxxx\n .t insert end xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n .t insert end xxxxxxxxxxxxxxxxxxxxxxxxxx update set scrollInfo } [list 0.0 [expr {4.0/11}]] # The following group of tests is marked non-portable because # they result in a lot of extra redisplay under Ultrix. I don't # know why this is so. .t configure -bd 2 -relief raised -wrap char .t delete 1.0 end | > > > > > > > > > > > > > > > > > > > > > > | 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 | set scrollInfo unchanged .t insert end xxxxxxxxx\n .t insert end xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n .t insert end xxxxxxxxxxxxxxxxxxxxxxxxxx update set scrollInfo } [list 0.0 [expr {4.0/11}]] test textDisp-6.10 {DisplayText, redisplay embedded windows after scroll.} {aqua} { .t configure -wrap char .t delete 1.0 end .t insert 1.0 "Line 1" foreach i {2 3 4} { .t insert end "\nLine $i" } .t insert end "\n" .t window create end -create { button %W.button_one -text "Button 1"} .t insert end "\nLine 6\n" .t window create end -create { button %W.button_two -text "Button 2"} .t insert end "\nLine 8\n" .t window create end -create { button %W.button_three -text "Button 3"} update .t delete 2.0 3.0 update list $tk_textEmbWinDisplay } {{4.0 6.0}} # The following group of tests is marked non-portable because # they result in a lot of extra redisplay under Ultrix. I don't # know why this is so. .t configure -bd 2 -relief raised -wrap char .t delete 1.0 end |
︙ | ︙ |