TIP 536: Improvements to Mac-specific IPC in Tk

Login
Author:         Kevin Walzer <[email protected]>
State:          Final
Type:           Project
Vote:           Done
Created:        29-March-2019
Post-History:   
Keywords:       Tk
Tcl-Version:    8.6
Tk-Branch:      mac_services
Votes-For:      DKF, BG, JN, SL, AK
Votes-Against:  none
Votes-Present:  KBK, FV

Abstract

This TIP proposes to modify Tk on macOS only in the area of Mac-specific IPC (inter-process communication) by adding three new commands; changing the signature of an existing command; and improving the documentation of Mac-based IPC in general in Tk's man pages. These commands implement and improve functionality in the Mac's Services and Apple Event API's.

Rationale

Most Mac applications support platform-native API's that allow them to work cooperatively with other applications in processing data. The two primary API's are the NSServices API and the Apple Events API. The Services API is similar to a Unix pipeline API where data may be sent from one application to another for processing in some form. The Apple Events API is a more complex mechanism where an application will expose one or many commands that can be accessed from other applications for sophisticated scripting. Tk has historically supported many of the Apple Event API's but not the Services API. This TIP proposes to add support for the Services API and additional commands and changes to the Apple Event API. It also proposes a utility command that will support Tk's platform integration. Finally, it proposes additional behaviors to the text, entry, and ttk::entry widgets on macOS to allow them to access the Services menu.

Specification and Documentation

The following commands, which will all be documented in the tk::mac section of the Tk man page, will be added with this TIP:

::tk::mac::PerformService

Executes a Tcl procedure called from the macOS "Services" menu by another application in the Apple menu item. The "Services" menu item allows for inter-application communication; data from one application, such as selected text, can be sent to another application for processing, for example to Safari as a search item for Google, or to TextEdit to be appended to a file. An example of this procedure, which will allow a Tk application to expose "Service" functionality to other programs (it should be customised in a application script; these are inherently non-generic):

proc ::tk::mac::PerformService {} {
	set w [text .t]
	pack $w
	set data [clipboard get]
	$w insert end $data
}

Note that the mechanism for retrieving inter-application data is from the clipboard; there is no other supported way to obtain the data. If the Services process is not desired, the NSServices keys can be deleted from the application's Info.plist file.

The underlying code supporting this command also allows the text, entry and ttk::entry widgets to access/consume services from other applications via the Services menu. In the implementation of this TIP, these widgets have additional bindings on the Mac that will append selected text to the clipboard, making that data available to be sent to other applications via the Services menu.

::tk::mac::LaunchURL URL...

If defined, this command launches a URL within Tk in response to the specific GURL/GURL Apple Event being sent to the application. This would be used if a Tk application wants to handle a URL itself, such as displaying data from an RSS feed, rather than launching a default application to handle the URL, although it can defined as such. It will respond to the "open location" AppleScript command.

::tk::mac::GetAppPath

This utility command will return the current applications's file path. It will do so in a more Mac-specific manner than using Tcl's file commands.

::tk::mac::PrintDocument file...

This command, which is designed to respond to the "print" Apple Event to print a document, is already present in Tk, but is currently broken. The current implementation takes a list of files as its parameter, but instead triggers ::tk::mac::OpenDocument command. This new implementation changes the signature to a single file path and works much more simply and effectively.

Implementation

An implementation of this TIP is present in the mac_services branch.

Testing

To test the new functionality in this TIP, follow these steps:

  1. Build and install the mac_services branch.
  2. Launch Wish, and source the service_test.tcl script (below) to register the new functionality with macOS.
  3. Quit Wish.
  4. Open System Preferences -> Keyboard -> Shortcuts -> Services.
  5. Open the Text tree item, scroll down to "Wish: Display Text Data," and check the box.
  6. Close System Preferences.
  7. Re-launch Wish and source the service_test.tcl script.
  8. Additional directions for user testing the Services API are in the text display of Wish.
  9. Next, test the updated AppleScript API.
  10. Quit Wish, re-launch, and source the service_test.tcl script.
  11. Run the service_test.applescript script (below) with this command in the Terminal: "osascript service_test.applescript."
  12. This will exercise the "open," "print" and "open location" Apple Event commands in the new build.

It is necessary for Wish to be running the sourced script for the new commands to work correctly. The use case for these commands is not running Wish from the command line and simple scripts, but rather as part of standalone applications where these commands are defined and customized for that application's specific functionality.

Support script: service_test.tcl

proc ::tk::mac::LaunchURL {url} {
	tk_messageBox -message "Opened $url"
}

proc ::tk::mac::PrintDocument {args} {
	foreach f $args {
		tk_messageBox -message "print $f"
	}
}

proc ::tk::mac::OpenDocument {args} {
	foreach f $args {
		tk_messageBox -message $f
	}
}

proc ::tk::mac::Quit {args} {
	exit
}

proc appPath {} {
	tk_messageBox -message \
		"The app path is [::tk::mac::GetAppPath]"
}

proc ::tk::mac::PerformService {} {
	set data [clipboard get]
	tk_messageBox -message $data
}

proc main {} {
	wm title . "IPC Test"

	pack [text .t]
	.t insert end "Select this text and select \"Services\"\
		from the Apple menu; make sure Services for the Text\
		Edit app are enabled. (You can select this in the\
		System Preferences app.) You should see an option to\
		\"Open new window in Text Edit with selected text.\"\
		Choose that one.

	Also, from Text Edit, select the same text and choose\
		\"Wish: Display Test Data\" from the Services menu. If\
		you don't see it, you may need to run Wish to active\
		the Services option in the System Preferences menu."

	label .l -text "Enter text in the entry and ttk::entry\
		fields\nbelow, select that text, and then follow\nthe\
		directions above: the text\nshould be displayed in a\
		new Text Edit window." 

	entry .te
	ttk::entry .tte

	button .b -text "Exit" -command ::tk::mac::Quit
	button .e -text "Get App Path" -command appPath
	pack .l .tte .te .e .b 
}

main

Support Script: service_test.applescript

--service_test.applescript
tell application "/Library/Frameworks/Tk.framework/Versions/8.6/Resources/Wish.app"
set theFile to "/Library/Frameworks/Tk.framework/Versions/8.6/Resources/Wish.app/Contents/Info.plist"
open theFile
delay 1
print theFile
delay 1
open location "foo://bar"
end tell

Copyright

This document has been placed in the public domain.