Saturday, October 14, 2023

Teaching Apple Cyberdog 1.0 new tricks (featuring OpenDoc)

In the distant nethermists of time, documents once briefly ruled the earth (or at least Mac OS), and to that end I managed to find a verrry interesting book recently, complete with an unopened CD-ROM. But seriously, though: what was Apple thinking with that name?
It's not this cyberdog (image credit).
Not that cyberdog, though this is one of my favourite weirdo pinball machines.
Definitely not that cyberdog.
But thanks to all those other cyberdogs, Apple's own Cyberdog — a seemingly ordinary web browser and Internet suite with some unusual capabilities — has since slid into search engine obscurity. Apple had some big plans for it, though, and even wanted to give developers a way to develop their own components they could run inside of it. Not just plugins, either: we're talking viewers, UI elements and even entire protocol handlers, implemented using Apple's version of OpenDoc embedding.

Cyberdog's initial release belied some wild changes afoot, for in the world it inhabited, the document told the system what it needed to display, and the parts within the document displayed it. The document ruled all. The document was king.

And all of this came together in a special Cyberdog software development kit, complete with example code and the very first 1.0 release of the browser. Now that we have our PowerBook Duo 2300 rehabilitated, guess what we're gonna look at?

At its core, Cyberdog was really just an overgrown demonstration of OpenDoc. We're going to necessarily explore OpenDoc's underpinnings in this article, but OpenDoc's basic idea grew from what was initially just a standardized compound document format to defining an entire object-oriented approach, where reusable viewer and editor components could be pulled as instances into such a document and maintain their own views and state. The document, not the app you were running, thus determined its own functionality. Under John Sculley this concept became "Project Amber" in 1993 as a stepping stone to fully realizing Apple's flavour of Taligent, the bundle of next-generation technologies to run on top of their intended successor to System 7, Pink. Eventually Taligent was to use it as its primary compound document format.

Taligent's cross-platform focus made the Project Amber concept cross-platform too: if you had the components, an OpenDoc document theoretically could work the same on any OpenDoc platform. In June 1993 Apple, Novell, IBM and Borland officially announced OpenDoc, and in September Apple, IBM and Novell, along with a host of secondary partners like Adobe, Lotus, Oracle and Taligent itself, established Component Integration Laboratories (CI Labs for short) as a technology provider to support OpenDoc and other component technologies like CORBA. Apple subsequently released the first Developer Release of OpenDoc for System 7.5 in December 1994, with later versions developed by IBM for Windows 95, NT, OS/2 and AIX.

As System 7 grew long in the tooth, Pink transitioned to Copland and became notorious for its deeply protracted development cycle. Years before its formal demise, it was already painfully clear to Apple management that Copland would never be suitable to run Taligent on, and Apple could not financially sustain the effort. After over $100 million then invested to date in Taligent by the Apple-IBM-HP partnership ($200 million in 2023), Apple cut itself loose and sold their stake in Taligent to IBM in December 1995. OpenDoc thus became the remnant and at the time the only modern object-oriented framework Apple had left. Apple shipped OpenDoc 1.0 in November 1995.

Despite Apple's efforts to seed the technology, OpenDoc didn't get a lot of traction with developers and arguably even less with users, who didn't understand what it was good for. It was also seen as a direct competitor to Microsoft's Object Linking and Embedding (OLE) technology, causing Microsoft to wield its formidable internal synergies and powers of FUD to slow OpenDoc's market progress further, irking IBM. Apple began work on OpenDoc Essentials, a suite of basic components for drawing, text and multimedia, but this in and of itself didn't make a compelling show compared to existing software. On the other hand, Internet suites were the new hotness with end users and could provide developers with a new sales segment, so a parallel group worked on something something OpenDoc something Internet. After all, what could be a hotter document than an Internet document?

Thus was the nucleus of Cyberdog (named for the famous New Yorker cartoon), which entered public beta in February 1996. OpenDoc was of course an absolute requirement, and no 68K Macs need apply: its first release was strictly for Power Macintosh.

This is a screenshot from the beta showing Apple's "Internet Pathfinder" home screen in Cyberdog, captured off some of Apple's example developer videos. As was fashionable at the time, the suite had its own internal "home page," centering around Apple-provided services (primarily hosted on the now-defunct cyberdog.apple.com, including the cyberdog.* Usenet hierarchy), but also collecting user bookmarks and history into what Cyberdog referred to as a notebook and log respectively. We'll explore those concepts a little later.

Unlike most Internet suites that either used a pre-rendered image or some sort of markup language, the Pathfinder was nothing less than an OpenDoc document with buttons and GUI elements composed from standard components. Sharp eyes will have noticed that the standard Macintosh File menu has been replaced with Document. We'll come back to that as well.

The core of Cyberdog beta was the browser, here viewing agate.apple.com (the name likely coming as a riff on Amber; this host is now lost, and not in the Wayback Machine), which appears to have been cyberdog.apple.com's direct ancestor. At first blush this doesn't look too different from any other mid-1990's web browser, and in basic operation it largely isn't. Where the experience gets divergent is when it gets embedded. Also take note of the Cyberdog, Mail/News and Navigate menus, which are in fact provided and driven by the browser's components.
It wouldn't be an Internet suite without other protocols. We're big fans of Gopherspace around here, though Cyberdog wasn't actually a particularly good Gopher client even for the time and isn't the main point of this article. Nevertheless, out of the box Cyberdog supported Gopher, E-mail, Telnet, Usenet news and FTP as well. Later versions could also view resources made available via AppleTalk — no HTTP required.

However, one interesting trick it's showing in this clip is the ability to drag and drop files from a Gopher server directly onto the desktop as downloads. The same thing works for browsing FTP sites, which from an interface perspective shouldn't be very surprising, since Gopher was (at least initially) intended to be a friendlier FTP.

To the best I can determine, Cyberdog was never part of the Apple Internet Connection Kit CD-ROM, but it was nonetheless intended to integrate with it (here surfing an early version of AICK's hub site WebCity). Notably, this build still has the traditional File, Edit and View menus which were later eliminated in the beta. Incidentally, if this looks like the prototype Cyberdog is running on Mac OS 8, you're right, except this video is dated over a year before Mac OS 8 was formally released — Apple wasn't nearly so secretive about future product leaks in those days. Also note that whatever pre-release build of Mac OS 8 (7.7?) this was, it still had the System 7-era help icon instead of 8.0's text Help menu, and it certainly wasn't Copland.

Cyberdog reached 1.0 in May 1996, but before we get to actually running it, let's now spend a moment talking about OpenDoc's internals.

OpenDoc components, in OpenDoc lingo, are called "parts." At the system level, an OpenDoc part was based on Apple's implementation of IBM's System Object Model, a language-independent specification for object-oriented programming. IBM intended to use SOM throughout its entire product line, most notably OS/2 where it is an integral portion of the Workplace Shell, and some portion of it remains in z/OS via OS/390. It was designed for a pervasively object-oriented world where applications written in one language would need to instantiate objects potentially written in another, and applications using such libraries would need to have a solution for the fragile binary interface problem or those libraries could not be easily upgraded. To deal with these and other pitfalls, SOM had an interface definition language for describing methods, made late-binding mandatory so that the runtime linker could automatically apply fixups (at a startup performance cost), provided more object-oriented features than competing technologies such as Microsoft COM — a/k/a ActiveX — like multiple inheritance, metaclasses and dynamic dispatch, and supported bindings theoretically in any programming language (though C was the only one ever implemented). There was a lot of potential there, but many developers didn't take advantage of those features, and development of a component could involve a lot of boilerplate code. We'll see this soon enough.

Apart from more abstract classes, concrete OpenDoc parts fell chiefly into two categories, namely editors and viewers. Parts could be hosted within container applications that presented the document which the part instances inhabited. Within such documents, they had a stacking order and could even clip or mask each other for complex effects. However, this also meant container apps at minimum had to be OpenDoc-aware and regular applications couldn't use them without modification, one of OpenDoc's many problems, though OpenDoc provided a default shell out of the box that parts could run in to avoid a chicken-and-egg situation. Part interfaces weren't limited to their spatial boundaries within the document; an active part could also create global UI elements, most notably menus. The Cyberdog, Mail/News and Navigate menus above were from Cyberdog's navigator part running in a document that contained only it. In more complex documents, OpenDoc parts had to all play nice with each other much like regular Mac OS applications, which tended to magnify the already existing deficiencies in the classic Mac OS's limited multitasking.

It was also possible to bootstrap a document from scratch with an active part ready to go. System 7 introduced the concept of stationery, where a document could be saved as a template to create other documents, and OpenDoc expanded on this notion. For this and most of the rest of the article, we'll demonstrate on the Duo 2300, running Mac OS 8.1, which bundles OpenDoc 1.2.1 as part of the operating system.

Let's take the venerable BBEdit Lite as an example, which briefly existed as a fully-functional OpenDoc editor part called BBEdit Lite·od. The Editors folder inside the System Folder contains the various active OpenDoc components. You can just drag libraries in and out of it and OpenDoc will update its parts inventory as necessary; you don't need to restart the Mac. From BBEdit Lite·od, which doesn't exactly roll off the tongue, we dropped in the editor part and the Mercutio shell plugin it requires (equivalent with the Mercutio MDEF used for enhanced pull-down menus), and copied over the included stationery to the OpenDoc Stationery folder.
Like regular Mac OS stationary acts as a document factory (like tearing sheets off a steno pad), OpenDoc stationery acts as an instance factory. Stationery can be dragged ("torn off") into existing documents or, if supported, directly opened to make new documents containing them. Conveniently you can create documents from stationery right in the Apple menu, or from the OpenDoc Stationery folder: since BBEdit Lite·od is a fully-fledged editor, opening its stationery will pop open a brand new document entirely driven by the BBEdit part.
While OpenDoc has provided the new document with a default container application (called the document shell), this document has just one part, and that part is all BBEdit. The editor part is able to present nearly its entire standard interface, even an About item in the Apple menu.

Still, to users a process like this would look only like a new and weird way to create documents; there's nothing "compound" about what we just did and no new capabilities were obviously unlocked. While you could certainly put a BBEdit part into another compound document, that kind of defeats the purpose of a simple text editor. Bare Bones Software never went any further with the proof of concept, even before OpenDoc started circling the drain, but Apple had other demos to show off.

In this demonstration, an interactive physics demo is part of a compound document with a live view and a "movie controller" that manipulates the simulation's time index. This product was called Dock'Em and was actually sold and used in education. Take that, Jupyter notebooks.
Another demonstration document was this one using a live database viewer part; the other UI elements on the document were also parts. Here Cyberdog's browser component has additionally been embedded to allow the user to look up the shoes listed in the database table (using jedi.apple.com, Jedi being another early code name for OpenDoc).
An extreme example might be this one: this screenshot shows two pre-release packages, the OpenDoc-enabled version of ClarisWorks 5.0, codenamed "Mars" (eventually scuttled in this form and reworked into the non-OpenDoc AppleWorks 5.0 in 1997) running on the pre-release version of Mac OS 8 we saw before. There are two Cyberdog-provided sections here, the browser and a button above it, plus the other pieces of the document created conventionally in ClarisWorks. We'll play with both of those parts later on.
This leads to an obvious question: what if you get a document and don't have the parts installed on your computer to view what's embedded in it? This is the same situation people would have confronted with embedded web content requiring a separate plugin; you'd need a plugin finder service to see what could handle that particular MIME type. Products like Kantara's Part Merchant were intended to act like an app store for OpenDoc parts, allowing developers to purchase and download parts on demand, while their PM Finder would dynamically hunt down compatible viewers and editors when an unsupported portion of a document was encountered.

This screenshot was strictly a mockup from one of their product circulars; PartMerchant transformed into PartBank, which offered not only OpenDoc parts — here's their entry for BBEdit — but also Java, ActiveX and Netscape NSAPI Plugin downloads. It fizzled out late in 1997, becoming Java specialist Flashline, and was subsequently bought out by Sun.

Failing those sorts of services, such content could be referred to an existing part on the system that advertises support for it (multiple parts to handle the same sort of content could be present on one computer), or the content might include cached data in a standard format such as PICT that the system could display, or fallback text explaining the needed part could also be shown to the user.

Now that we understand a bit more of what's happening under the hood, let's get back to Cyberdog. However, first we'll need to make it a bit more stable. While OpenDoc was already somewhat memory-hungry, you will find the Cyberdog experience substantially less bumpy with bigger allocations.

Before doing anything else, go to the OpenDoc Setup control panel and increase the default document size to at least 1440K, preferably 2048K. This will still fit into our Duo 2300 with a full 56MB of physical RAM and virtual memory pumped up to 64MB. Don't start OpenDoc at system setup in case something goes wrong.

We'll now double-click on the Cyberdog Starting Point (replacing the Internet Pathfinder) document's goofy dog icon to open it from the Finder. The Starting Point is an OpenDoc document that contains the home screen then in vogue for Internet suites, but note well: you could open any document and use that as your starting point because we're not explicitly opening a Cyberdog application. There is no spoon application. The document drives all by invoking Cyberdog parts within it.

In fact, there's actually no way to open Cyberdog 1.0 directly except with a document containing a Cyberdog part. This was the last Cyberdog release where that was true, by the way.

Aside from a new, possibly less charming name, the buttons and the things they connect to are the same. Again, since Apple has long since decommissioned the Cyberdog home page and server, most of the buttons don't do anything anymore.
For example, this was the Cyberdog 1.0 home page on Apple's servers. The Wayback Machine doesn't have a copy of this earliest incarnation, so these screenshots from the developer documentation are all that survive from it.

Internally, arbitrary individual browser windows are OpenDoc documents and rendered by the Cyberdog Navigator part, which resides in the Cyberdog Libraries folder within the Editors folder we saw before. Parts can have subparts; Cyberdog's HTML viewer part is the primary subpart within this window's navigator, but its satellite widgets are also parts.

This window also points out the current item it's displaying, a core Cyberdog class that contains information about a given resource (web page, Usenet newsgroup, E-mail address, and so on). You can think of it as a "URL on steroids": it necessarily includes a URL, but also the title of the resource and an icon. Items can be saved to the Finder, where they act rather like bookmarks, or committed to other sorts of storage. When an item is opened, the item determines what Cyberdog part needs to be instantiated to handle the resource the item refers to.

Those "other sorts of storage" include Cyberdog notebooks and the log, which are the bottom two windows in this shot. Like the navigator, the log and notebook are Cyberdog parts as well. The default notebook is more or less your bookmarks (to any protocol or service that Cyberdog supports), and the log contains your browsing history. Each terminal entry in the log and in a notebook is a Cyberdog item. A "finger" in the log indicates the user's current position.

The top window was the Apple-provided search page at the time which just forwarded through to Digital's AltaVista, then the state of the art in web searches. (Remember the Internet before Google? Cyberdog remembers.)

The Document menu is the true core of Cyberdog, and we'll look at what it means to create a New Document further in, but the Cyberdog menu is how you operate the browser itself.

Notice there is no Quit in the Document menu: you quit by closing the document. The document, once again, is everything.

If you press Command-T or choose it from the Cyberdog menu, the Connect window appears. You can select any supported protocol from the Service box, or simply go to "URL" and enter a URL.
And here is Cyberdog 1.0 surfing the Floodgap home page, which I intentionally maintain to be compatible down to Netscape Navigator 3.0 or so. Although Cyberdog has SSL support, it does not support TLS (as Cyberdog predates TLS 1.0 in 1999), and I was not able to find a simple way to substitute in Crypto Ancienne. 1.0 is known to have a notorious bug with proxies and secure sites anyway, which would cause problems regardless for Crypto Ancienne's proxied TLS approach, so perhaps we'll hack in this support in a future article.

If we select Document Info from the Document menu, it explains more about the window we're looking at. Cyberdog Navigator is listed as both its "kind" and editor. Kind, in this case, is OpenDoc's equivalent of a filetype, usually internally specified to the system as a structured string like +//ISO 9070/ANSI::113722::US::CI LABS::Apple:Cyberdog:SomePart:Kind:SomeThing (along with another string to be shown to the user, as in this case). There are kinds listed for all the subparts as well. The Size button lets you adjust the document's memory allocation, blurring the lines further between document and regular Mac application.

Since Cyberdog was OpenDoc's most high-profile demonstration application, and everything Internet was hot, Apple published an SDK shortly after 1.0's release in an attempt to build a Cyberdog-centric ecosystem around it specifically. Called the Cyberdog Programmer's Kit, that was the book I showed you back at the beginning.
The Cyberdog 1.0 release we just played briefly with, plus all the movies with the beta screenshots, came on the included disc with an icon that looks like Cujo on dope. It also includes sample code in C++, headers and libraries. Because Cyberdog 1.0 is PowerPC-only, it only supports generating PowerPC OpenDoc parts with a suitable version of CodeWarrior or MPW. As the disc includes CodeWarrior project files, CodeWarrior Gold 9 will do nicely for our purposes, which was also released in 1996.

Cyberdog has five chief classes, among others: the global session (surfaced as an OpenDoc part with no interface) subsuming all open Cyberdog documents but not Cyberdog parts in other non-Cyberdog documents, items, services to specify a particular protocol, streams to pull down data for that protocol over the network, and display parts to show that data. Since Internet suites generally subdivide their functionality by protocol, let's start with how a service in Cyberdog for an arbitrary protocol might be implemented.

When I want to do a simple Internet TCP socket test, my favourite service for example purposes is the finger protocol as set forth in RFC 742 et seq.: we connect to the server, we send a single ASCII line, the server emits a reply, and then the server disconnects. (In a very real implementational sense, Gopher is essentially the next step up, as it is also a single command and reply but adds a structured menu format. Some of you will recall exploiting this property to build a Gopher client on the Alpha Micro from a hacked finger client.) It's an easy protocol to understand and build. Apple must have thought so too, because they include just such an example.

The Finger Service example is divided into three sections: the finger service proper, FSConnect as its linkage into the Cyberdog Connect window to request a hostname and username, and FSPrefs for exposing its single preference stored in Internet Config (to open the finger connection synchronously or asynchronously, which for the purposes of this article we'll simply handwave away).

Before trying to build it, copy the examples to your hard disk — I just copied the entire disc over — and fix the access paths in CodeWarrior, which by default appear to be completely blank. All of the necessary libraries and headers are included as either part of CodeWarrior Gold 9 or on the SDK disc itself.
We'll start with FingerService. This contains everything you would think would be needed: a subclassed item for references to finger resources and support for understanding finger URLs, the stream class itself, and a resource and handler for an example menu item. The component is linked against various stub dynamic libraries, notably Cyberdog's stub shared library itself, and a number of OpenDoc stub shlbs. So far, so good.
But it's what else gets rolled into it that raises eyebrows somewhat, and these pieces are unexpectedly linked in statically. Network access, rather than using MacTCP or OpenTransport directly, uses Cyberdog-specific classes such as a specialized domain name resolver and the tcp_endpoint, which is more or less a socket-like entity. These pieces don't appear to be part of Cyberdog's shlb — we have to compile them in. OpenDoc is even worse, with a mass of utility classes that also aren't part of its own stubs.
The included resource file defines a srvc resource which points to standard STR# string resources to provide the protocol's name and URL scheme (finger), the finger service's C++ class name (AppleCyberdog::FingerService), the finger item's class name (AppleCyberdog::FingerItem), and the OpenDoc kinds for its Connect interface ("+//ISO 9070/ANSI::113722::US::CI LABS::Apple:Cyberdog:FingerSample:Kind:ConnectPanel") and preferences interface ("+//ISO 9070/ANSI::113722::US::CI LABS::Apple:Cyberdog:FingerSample:Kind:PrefsPanel"). It also specifies strings for a single menu item, called "Finger Beep."
And that item beeps. Despite this being a C++ file, and despite implementing a C++ object backing the finger service, this function is not C++. FingerService__DoMenuItemSelectednot FingerService::DoMenuItemSelected — is actually called through SOM to override that method in SimpleCyberService (of which FingerService is a subclass), which is why we use a C declaration because the name mangling is different and SOM bindings only exist for C. Anything that needs to be accessible to a SOM call will need a SOM-compatible declaration. Fortunately, at least the pre-generated header files and stub glue code are all provided for you with the SDK, so you don't need the SOM IDL compiler merely to build these demo projects.

This function takes an AppleCyberdog_FingerService (basically a this pointer), an SOM Environment pointer, and then the index of the selected menu item relative to this part's menu (starting from 0), a pointer to the OpenDoc frame (ODFrame) associated with the menu event, and a pointer to the service's menu data (CyberMenuData). Represented in SOM Object IDL, the function declaration is void DoMenuItemSelected (in long index, in ODFrame frame, in CyberMenuData menuData); (the self and environment pointers are implied arguments to every SOM call).

Setting up menus themselves is no picnic either. The Cyberdog programmer's manual says that "[y]ou must anticipate changes in the menu bar between activations of your part. You should re-create the menu bar each time your part is activated or its menus are adjusted." You also have to handle deactivating or removing the Cyberdog menu when your part loses focus (and activating it again when it does), and you have to allow menu items to adjust themselves and handle their own events. This turned out to be an exceptionally bad design choice for all kinds of reasons, which we'll revisit at the end.

But once we're past all that, the actual code to handle the menu event is very simple and much like handling pull-down menus otherwise: if the index is 0, which is the only menu item we provided in the srvc resource, then we call the Toolbox SysBeep routine. If we wanted more or different menu items, we'd add more strings to the specified menu item STR# resource, and check for and handle those indices here.

The implementation of the finger URL is pretty straightforward.
But then we start getting into a blizzard of SOM methods again when we implement the Cyberdog finger item. There is a C++ backing object, but everything else is SOM, subclassing CyberItem. Everything gets overridden. Everything. (To add further insult, ctopstr and pstrcpy handle conversion between Macintosh Pascal strings and regular C strings. You'd think that would get handled for you in this brave new OpenDoc world, but noooo.)
It gets even worse when we move to FSConnect. Since this has to worm itself into the Cyberdog Connect window, you would expect a lot of SOM-compliant methods. And that's what you're gonna get, all right, but we have to start with its underlying C++ class first.
This C++ class handles all the OpenDoc code to maintain and display its part within that window, and it's a doozy. Did you expect this many methods? I haven't even shown you them all.
But wait, there's more! Now (because SOM doesn't have direct bindings for C++) we have to provide the SOM glue to actually call those C++ methods. The IDL compiler would generate the scaffolding but you'd still have to labouriously hook them all up.
There are also various utility methods ...
... a list iterator (we have to provide our own list iterator?) ...
... and then this little blob of SOM glue for when the finger CyberItem is actually generated by the Connect window, so we can do something with it. Notice the pre-processor macros for SOM's particular flavour of exception handling.
It's the same story when we get to FSPrefs: a big C++ class and a lot of SOM glue, just to wedge our own trivial interface pane into Cyberdog's.
I'll spare you the entirety of it here. Just know, like with FSConnect, nearly all these C++ methods also have SOM glue, and of course there's the rest of the boilerplate you saw in the project.
Having built all three projects (or copied the pre-built ones), we deposit the resulting binaries into the Cyberdog Libraries folder. The Cyberdog library itself, containing nearly the entirety of the browser and its component parts, is a relatively svelte 2.8MB. Our overweight, freshly built and thoroughly flabby little finger service, on the other hand, summed over all three libraries is 427K — and 334K of that is just the pieces for the Connect and Preferences windows. We might blame some of this on debugging and maybe some on CodeWarrior, but we can't ascribe all of it.
Our "Finger Beep" menu item now duly appears in the Cyberdog menu, and does beep.
Here's what all that code for the Connect window actually bought us — two lousy text fields. (T-shirt idea: I compiled FSPrefs and all I got was a label and a check box!) They really are lousy too, because if you press RETURN in them, they don't seem to recognize they shouldn't be multi-line. The code then takes the results from those fields and generates a Cyberdog item to display finger information, which it then opens.
Fortunately, opening the item works pretty much seamlessly, here fingering root on one of my internal servers. The name in the title bar is the name stored in the generated item. I'll reproduce part of the code from the FingerService here that opens the finger item, using the synchronous version for clarity.

SOM_Scope void  SOMLINK FingerItem__Open
                                    (AppleCyberdog_FingerItem *somSelf,
                                     Environment *ev,
                                     ParameterSet* theParams)
{
 ...
 // Extract the opener part (if any) from the parameter set.
 ODPart* openerPart = kODNULL;
 if(theParams)
   if(!theParams->GetParameter(ev, kCDInitialOpenerPartKey,
                               &openerPart))
     theParams->GetParameter(ev, kCDObtainedOpenerPartKey,
                             &openerPart);

 // Create the right kind of part in the right document.
 CyberSession* cyberSession = ::GetCyberSession(ev);
 TempODPart cyberPart = cyberSession->CreateCyberPart(ev,
                                                      openerPart, kTextPlainKind, kODNULL);

 // If the display part handles the kTextPlain kind of data,
 // create a download part instead.
 if(cyberPart == kODNULL)
   cyberPart = cyberSession->CreatePartInCyberDocument(ev,
                                                       kDownloadPartKind, kODNULL);

 // Open the Cyberdog item.
 if(cyberPart)
 {
   TempCyberPartExtension cyberPartExt(cyberPart, kCyberPartExtension);
   cyberPartExt->OpenCyberItem(ev, fingerItem, openerPart, params);
 }
 ...
}

Among other things, this first tries to figure out what wants to open it, and then calls for a new part to display the data to be received. Notice how we're treating these calls like regular C++, which will give us this as the first argument for free, but we still must pass the SOM environment pointer.

CreateCyberPart is a method provided by the global Cyberdog session that determines what document to insert a new display part into, be it within or without the current Cyberdog session proper, with an optional editor argument (here we've passed a null pointer to use the default). If there's no document to insert it into, we'll create a "download part" to stream the data directly into a plain text display part in a brand new document. We then open the item, which starts the network transfer.

Asynchronous opening would spin off a separate thread to do most of this. If you were connecting to the world's slowest finger server and wanted to cancel the connection before it timed out, you can check for posted stop events with a Cyberdog progress broadcaster while your network transaction runs, which is what you'd do normally for user-facing code.

If all that for all that seemed as excessive to you as it did to me, it apparently did so to Apple as well. But instead of trying to streamline the API and make it less verbose, Apple's solution was to ... semi-automate generating the bullshi boilerplate instead. Apart from their implementation of the SOM IDL compiler — which initially only ran as an MPW tool, not in CodeWarrior — Apple provided a third-party tool for this purpose called CodeSampler, which takes a part template you want to modify and spits out a new one with basic changes that you can then go and edit.
I say "semi-automate" because the template only handles basic bikeshedding, and only for the templates that are included. Even on those that are, you still have to deal with looking at all the methods it subclassed, though at least you'd have a (theoretically) working scaffold to start from rather than writing every agonizing bit from scratch.

Naturally, there were more practical reasons to create a Cyberdog service. One of Cyberdog's rather obnoxious deficiencies was the inability to directly open a file from the UI; files could only be referenced by typing in file: URLs (on locally mounted disks, or via AppleTalk and/or Apple Filing Protocol in later releases) or opening an item referencing it that already existed. The OpenFileService part provided a menu option which would dynamically generate a Cyberdog item to a file reference using the Standard Open dialogue, and you've now seen enough from the finger service example to understand generally how that would be implemented. Unfortunately, this rather useful component was stored on the Cyberdog FTP server, which I could find no existing mirrors or archives of, and thus to the best of my knowledge is now lost. Also notice this component says it was 68K compatible — hold that thought.

For the next project in the SDK, we'll first move on to Cyberdog's most notable, most unique and probably most underutilized feature: creating OpenDoc documents that could embed buttons and browser views. We start up DocBuilder by creating a new document from the Document menu.

DocBuilder came with every version of Cyberdog from the very beginning, and is nothing less than a full-blown OpenDoc document editor lurking within Cyberdog. It was written for Apple under contract by 6prime (who also wrote CodeSampler), a small software company founded specifically to cater to OpenDoc on the Mac by Eric Soldan and Tantek Çelik, who previously worked on OpenDoc as an Apple employee. 6prime's key product was REV, an auto-versioning tool that got bought out by Aladdin Systems in 1997. Çelik ended up at Microsoft where he developed Internet Explorer for the Mac, and is now the Web standards lead at Mozilla.
Unfortunately, DocBuilder is just as memory-hungry as everything else, and you'll definitely want 2MB for the document if you didn't already specify it (from Document Info, click the Size button).
Save the blank document to disk so that the memory allocation sticks (I closed and reopened it for good measure). Notice that you could save OpenDoc documents as stationery too. How meta.
Here is a sample document I created in DocBuilder's layout mode (where the dog is "laying" down, get it?). In addition to the standard drawing tools on the upper floating palette, DocBuilder provides three core parts offered on the lower one. The text labels were created with the text editor part (left), which can also include scrolling content (here I haven't, and I've also made them read-only). The dog button comes from the button part (middle), which oddly can only show graphics in Cyberdog 1.0; I pasted in the goofy dog icon from the Finder and added the explanation as a text blurb. You can also embed notebooks in the window to provide a hierarchical, collapsable list of links (right), as well as drag or paste PICTs directly into the document. The Starting Point is merely a Cyberdog document created exactly thus, and you could also use the DocBuilder for constructing rich E-mail messages — as long as the person on the other end also used Cyberdog.
Embedding a live browser, however, requires slightly more work: you drag the desired URL or item from a separate navigator into the window and then adjust ad lib. OpenDoc is "semi-modal" in the sense that you have to activate — not just select, notice the unusually thick frame — a part to bring up its menu(s), which cancels menus from other parts and here as a side effect hides the palettes until the overall document regains focus. The select-versus-activate distinction was yet another one of OpenDoc's interface faults. Here I've turned off the controls and location bar in the embedded navigator, though you can still click on links and use forms.
Since browser parts will automatically try to connect to their embedded locations as soon as the document is opened, buttons serve as "deferred links" (well, really, just buttons) to avoid cheesing off the dialup users. Buttons have a limited number of things they will connect to, mostly URLs/items and some internal components, and they weren't capable of scripted behaviour.
Interestingly, the Help menu is live, and actually brings up an Apple Guide which is dynamically populated based on what parts are present in the document. This would probably be more useful in practice if the parts were better documented, but it's another example of how OpenDoc was designed to blur the borders between document and app.
When you click the running dog (to run), the layout grid goes away and the document becomes conventionally interactive. You can save it to disk and share it with your small number of friends who had OpenDoc, Cyberdog or something compatible and the same parts you used, which for many people was approximately zero, even in 1996.
One of the demonstration videos on the CD showed off this sample document embedding a live, interactive view of many top-level Usenet newsgroup hierarchies, showing the concept was by no means limited to typical Web, Gopher or FTP sites. What exactly this had to do with Apple technical support was not explained.

The fact that buttons only took graphical images in Cyberdog 1.0 was a strange limitation in my view, and defeated creating traditional-looking links in Cyberdog documents. But the second example we'll look at in the Cyberdog SDK seemed to address this, called the CybTxtBtn (short for Cyberdog text button).

This component is actually a modified example object from the OpenDoc SDK using its generic SamplePart project. I won't go through the code in great detail except to show you the finger service was not an aberration: this codebase is just as verbose.
With the library built (remember to fix the project paths) and installed in the Editors folder, drop the library on the OpenDoc document shell hidden in the System Folder to emit stationery, and then put the resulting stationery in its usual place, though in practice it should work from anywhere.
Now drag the stationery to the document to generate a new instance. You'll see a text URL appear, by default the Cyberdog home page.
It's a working link, so if you click on it, it opens the referenced page like regular buttons do. However, it can hold any item, and will display the title of the item it represents (or if no title is available, the URL). Here, we'll drop a Gopher item from a navigator window on the text button ...
... and it obligingly changes, taking its new title from the title bar of the window we dragged the item from. This part appears to be the only way you could generate such a link in a Cyberdog document with 1.0, and it never shipped with Cyberdog itself.

Apple did try to encourage building Cyberdog documents and shared some they found particularly noteworthy, but these documents were also stored on the Apple Cyberdog FTP server and are also believed lost. Some of Apple's "Cyberdog Dogshow" winners are listed here.

Additionally, the whole docu-centrism concept got rather muddled with Cyberdog 1.1 in September 1996, which turned things on its head. Instead of, or at least in addition to, you opening the Starting Point document to open Cyberdog, 1.1 now had a stub app that you opened conventionally and it opened the Starting Point (now "Cyberdog Tour"). To make the sea change even clearer, the Document menu reverted to a standard File menu and stayed so for the rest of Cyberdog's existence, while simultaneously the explicit Quit option returned. It was all a subtle yet stinging retcon of the OpenDoc way.

Cyberdog 1.1 required the new OpenDoc 1.1, a speed and bugfix release that came out just before in August of that year. But a bigger change in Cyberdog 1.1, besides support for NSAPI plugins, multiple pages in Cyberdog documents, accessing resources over AppleTalk and a basic AppleScript dictionary, was compatibility with 68K Macintoshes (68030 or better). The SDK was duly updated to include support for 68K, but the 68K stub library, headers and updated examples were all on the defunct Cyberdog FTP server too. That means our PowerPC-only CD here is apparently the only version of the SDK that appears to still exist, and while components we build with it should work fine with later versions of PPC Cyberdog, they will never run on a 68K machine.

There are two other examples on the CD which I'm simply going to mention rather than demonstrate, as they're really intended as templates and introduce no obvious new functionality. CybTxtViewer is a Cyberdog display part that displays plain text; this is not too useful as is, as Cyberdog already displays plain text just fine, and is obviously intended to be used as scaffolding for other things (via CodeSampler). The other one, CybTxtNavViewer, shows how you would embed such a part in a Cyberdog navigator, including getting status messages from the navigator and enabling saving and printing.
Regardless of all Cyberdog 1.1's systems changes, however, the most visible one was a far more refined look. Gone was the dorky dog in the beta and 1.0; in came the dignified painterly hound most people who have used Cyberdog associate with the browser today. This new look coalesced around a superbly elegant new Cyberdog Tour (replacing the Starting Point) which cleverly hid the rectangular buttons by dressing them with irregular hand-drawn borders, pasted in as static graphics. There was little new functionality other than some additional buttons, but the visual effect is exquisite and easily the most artistic chrome style from that era.
Cyberdog 1.2 was released in December 1996. It was primarily a bug-fix and polish release, but also expanded its AppleScript and drag and drop capabilities, offered support for buttons with labels for the first time, and even could display transparent buttons. Cyberdog 1.2.1 fixed a few additional bugs and appeared with Mac OS 7.6 in January 1997, shown running here on my clock-chipped Quadra 800 with Mac OS 8.1. OpenDoc came as part of the Mac OS starting with 7.6, but that galling File menu in what should have been its killer app doc reminds you that Apple's relationship with it was increasingly complicated.

The Cyberdog SDK was also updated both for 1.1 and 1.2, and new with the 1.2 SDK was specific support for CodeWarrior's new Direct-To-SOM compiler, which dramatically cut down the need to manually write SOM glue methods. This piece of the Cyberdog SDK is lost too.

In the meantime, Copland made its disastrous developer début (as the infamous "Developer Release 0") in August 1996, and mindful of its mortally flawed history, Gil Amelio hired Ellen Hancock as CTO to evaluate its future. Hancock quickly concluded Copland would never be able to ship and Amelio duly cancelled it, leaving a void to be ultimately filled by NeXT as Apple's next-generation OS. With Rhapsody's Yellow Box (i.e., Cocoa in Mac OS X) poised to become the unifying object-oriented framework Apple always wanted, there was no further need for OpenDoc at Apple — or to show it off with Cyberdog. (This realization can't be merely coincidental with Cyberdog 1.1 turning about so suddenly on 1.0's docu-centrism: it was now time to turn Cyberdog into "just another application.")

To that end, on April 11, 1997 Apple said the quiet part out loud and cut loose their implementation of OpenDoc. While OpenDoc would remain compatible with the Blue Box compatibility environment (and indeed remains compatible with Classic under Tiger 10.4 and earlier), it would not be part of Rhapsody. When the recently returned Steve Jobs himself was asked at WWDC'97 "what about OpenDoc?" he famously quipped, "What about it?" Although he called out Apple's faults in engineering management and acknowledged the work people in the audience had done with it and other failed Apple technologies, he refused to backtrack on OpenDoc's cancellation, telling attendees that "focusing is about saying no."

And so the last Cyberdog (really a roll-up release) was pushed out later that month, tagged as final version 2.0. During the alpha releases 2.0a2 had a holiday theme which sadly never made it to the release.

Cyberdog 2 was the first version I ever personally used, and I have it installed here on my clock-chipped Quadra 800 also running Mac OS 8.1. I've already bumped up the OpenDoc document size for the screenshots, as without it I would get terrible crashes and system errors when OpenDoc shut down while quitting Cyberdog.

Cyberdog 2.0's main advance was a substantially improved Web browser component. It now supported frames, animated GIFs, client pulls, some extra tags and attributes and variable text encoding, was faster, and understood cookies. The mail and news support was more tunable for better performance, and the default notebook was subtly rearranged for no obvious reason compared to 1.2.1.
Otherwise, it supported what 1.2 did: FTP, Gopher, mail/news and AppleTalk were all still first-class citizens.
The interface was also virtually unchanged, but the rendering is quite quick, and noticeably faster on the 36MHz Q800 than on the 100MHz Duo 2300. Part of this was the Duo's gimped system bus and part of this is the Quadra has over double the RAM in the Duo, but any way you sliced it (especially since both these systems have the same version of OpenDoc), Cyberdog 2.0 was a lot faster than 1.x.
You could still create documents in 2.0 with DocBuilder, but the File menu was the File menu, and there was no going back. (Focus!) Notice the page indicator, introduced back in 1.1, which could appear as (nothing), arrows or a dog-eared page. Somehow the dog-ear sounds right.

The swan song was OpenDoc 1.2 (1.2.1 for Mac OS 8.0), which came out in September 1997 as the last version Apple would release and what our demonstration systems have been running here. And, well, maybe there was another good reason for OpenDoc to die: it turned out users just didn't get it. Almost as a post-mortem, Apple published an August 1997 journal article on "The Role of User Studies in the Design of OpenDoc" (Dykstra-Erickson and Curbow, doi:10.1145/263552.263588). Among its notable observations was that users didn't really understand the concept of stationery (in System 7 or OpenDoc), the select-versus-activate distinction was the root of many user interface problems, and inexperienced users tended to imagine tasks as less complex than they actually were.

But I think the real bullet in OpenDoc's head (as Jobs put it) was the authors' discovery that the most adept users had a menu of strategies to randomly try and just ended up doing whatever worked, which is damning evidence even those skilled users didn't truly grok OpenDoc, let alone have a functioning mental model of its operation. As the authors observed, "Users figure out their own way of doing things and don’t adhere to our expectations for how they will work with OpenDoc. ... Recognizing a useful strategy and employing it without [emphasis theirs] a firm grasp of the underlying mental model requires the user to rethink strategies every time they are faced with a problem." When I was building the Cyberdog Humane Society in DocBuilder, it certainly took a lot of screwing around for me to get what I wanted, and some of the steps I ended up taking absolutely felt arbitrary. The authors concluded, "We have yet to determine 1) how to increase user comprehension of the model; 2) how complete the user’s mental model of OpenDoc needs to be in order to use it and feel good about it; and 3) what instrument is best to measure learnability."

Those problems would never be addressed, for Cyberdog 2.0 was really OpenDoc's last stand on any platform, not just the Mac. While you could still download Cyberdog for some period afterwards, Apple never released any followups or bug fixes, and OpenDoc no longer came bundled from Mac OS 8.5 on. SOM objects remained an integral part of OS/2, but the Windows and OS/2 releases of OpenDoc proper were quietly dropped by IBM as well, and thus the brief 1990s tyranny of the document came to an ignominious end. Apps, once again, were king.

Never forget. If you copied, or know where to find, the contents of the Cyberdog FTP server from any point in its prior existence, please post in the comments or E-mail me privately at ckaiser at floodgap dawt com.

1 comment:

  1. I tried out Cyberdog back in the day, including the word-processing component. I'm not a developer, and it was a little complicated to use, but fun.

    ReplyDelete

Comments are subject to moderation. Be nice.