Idea/dream/wish: Exposé for classic Mac OS

PB170

Well-known member
I've been thinking about writing this post for a loooong time, but I've felt that the idea was a bit too far-fetched, and that it would put the biggest effort on someone other than myself, so I've decided to focus on other projects this far. But since @Crutch opened the door for it to perhaps become a reality, then here we go :)

As I've mentioned in a few other posts, I'm using a PowerBook 170 with System 7.1 daily as one of my main computers. Over the years, I've added an assortment of system extensions to keep the system as up to date with modern interface enhancement as possible (like alt+tab and command+h for efficient application switching, proportional scroll bars etc.) to a point where it's now pretty much on par with modern systems for the things I use them for.

The only thing I'm really missing, is the Exposé/Mission Control feature for showing the desktop (probably the single most useful interface addition that has come with OS X, imho). It's the first thing I activate when I use a new computer, and I’ve been dreaming about having it on my PowerBook since shortly after it was introduced in Mac OS X 10.3.

I spent some time during the weekend to put together a visual mock-up of the implementation I've had in mind. (Entirely using Macromind Director 3 on my PowerBook, by the way :)).

Basically, I'm thinking about a direct clone of the OS X implementation, but the way Apple likely would have implemented it if it had been conceived in the System 7 days:

View attachment Exposé mock-up.mp4

I've attached the original QuickTime video if anyone wants view it on real (black and white) hardware (it runs at the full 30 fps on my PowerBook).

I'd be happy to invest as much as I can into this for it to become a reality. The Toolbox/OS skills required to put it together is still way over my head, but I'd be glad to assist with the more general parts. (I'm also a graphic designer by profession so I can take care of the visual stuff :))

Unless you manage to code it all by yourself @Crutch, perhaps we could make it a collaborative effort, if there is enough interest? It would be amazing to create a really polished extension for all versions of classic Mac OS.


To begin with, here's a basic algorithm:
  1. If triggered (by active screen corner or keyboard shortcut, as set up in control panel):
  2. Record/save the areas of all windows (including palettes/floating windows and irregularly shaped windows)
  3. Hide all windows
  4. Calculate angle/axis between center of window and center of screen for each window
  5. Calculate final positions of windows after moved along axises, with edges protruding 3 pixels into the screen (not including frame and shadow)
  6. Draw "zoom rects"* from current window positions to final positions
  7. Draw simplified (inactive) windows in final position
  8. If re-triggered, or if a mouse click occurs anywhere within 5 pixels from the screen's edges, or if one or more documents or applications are set to be opened from the Finder (and thereby leaving the Finder):
  9. Remove simplified windows
  10. Reverse "zoom rect" animation
  11. Restore windows
  12. If leaving the Finder, allow destination application/applications to activate and open any selected documents


* Regarding zoom rects:

I don't know if the algorithm is made available by the system, or if it's documented somewhere, but I'd guess it's proprietary and internal to the Finder. Anyway, I had to recreate it for the mock-up :) Since it's too quick to be able to discern the details directly (at least for me), I took some high speed footage and analyzed it (kind of amusing for a 25 mHz black and white system), and it's surprisingly complex. Here's how it appears to work:
  • There are a total of 12 (or perhaps 11) medium grey rectangles in the animation (drawn, I believe, with the notPatXor transfer mode (same as for the outlines for dragging windows and documents anyway))
  • 3 rectangles appear together on the screen at any one time
  • The duration of the animation is ≈0,23 s (interestingly, I measured the exact same duration for the Exposé animation, so that seems like a suitable speed)
  • The animation is not linear, but accelerated, so the rectangles are not linearly spaced (I'm not clear about the exact formula used, but Director's "accelerated/ease in" in-between animation function resulted in a pretty good approximation)
Under Mac OS 9, the line width for the rectangles is two pixels instead of one, while the number of rectangles as well as duration of the animation is halved to 6 rectangles and 0,12 s.

Here's a combined photo of the animation sequence:

Zoom rect animation.png


And here's a chart with the approximate acceleration/spacing:

Acceleration chart.png

I think this may be enough for one post :)
 

Attachments

  • Exposé mock-up.moov.zip
    119.5 KB · Views: 5

PB170

Well-known member
Ps. Right click on the video and open it in a separate tab to view it correctly – it seems to get scaled by the forum software, which doesn't work very well with 1-bit graphics…
 

Nixontheknight

Well-known member
I think it would look cool, but I’m not sure if it would work on any processor before the 020, it would be nice to see
 

Crutch

Well-known member
Hey, thanks. Gorgeous mockup! I was thinking the exact same thing with the zoom rects (would love to animate the windows themselves but old Macs are too slow for all that shininess), but I would have spent a week tweaking the exact speeds and animation. So this is extremely helpful.

As spec’ed I don’t think this will sap the processor much at all and should run on any System 7-capable Mac including a Plus. I think.

There is a lot of fun detail here but I think the project is appropriately sized for one person and I am enjoying working on it. I have the bones working now (hide/show all windows in all apps on a keystroke) and think I resolved all the crashes this weekend (stupid _GetProcessInformation trap requiring certain fields to be prepopulated in the buffer you pass in … totally missed that in the docs the first 8 times I skimmed them!!). I think I can have a rough prototype ready to share in a couple weeks and a polished finished product by the end of December.

I am also going to do the app preview thing I mentioned in the other tread, but will focus on the bit you are actually asking for here first!

Questions:
- Should this work in/from any app, or just the Finder?
- Does opening a folder (not an app/document) in the Finder also restore all windows? (If so, this becomes easier to code, I would just watch for a double click, Open menu item, or Cmd-O, probably, instead of having to figure out exactly what the thing being opened is.)
- Does leaving the Finder in any other way (Application menu) also restore all windows?
- Does opening any window in any app generally restore all windows?
- On a multi-monitor setup, do all windows go to the edge of their own monitor? (I assume yes)
 

PB170

Well-known member
Hey, thanks. Gorgeous mockup! I was thinking the exact same thing with the zoom rects (would love to animate the windows themselves but old Macs are too slow for all that shininess), but I would have spent a week tweaking the exact speeds and animation. So this is extremely helpful.
There is a lot of fun detail here but I think the project is appropriately sized for one person and I am enjoying working on it. I have the bones working now (hide/show all windows in all apps on a keystroke) and think I resolved all the crashes this weekend (stupid _GetProcessInformation trap requiring certain fields to be prepopulated in the buffer you pass in … totally missed that in the docs the first 8 times I skimmed them!!). I think I can have a rough prototype ready to share in a couple weeks and a polished finished product by the end of December.

Great! Let me know if it starts to become overwhelming or tedious and I'll try to help the best I can.

It would be cool to have the actual windows move of course, but I prefer to have a consistent interface (an extension exists that enables live dragging of windows, and that alone is rather sluggish – I tried it a long time ago, and I definitely understand why Apple chose outlines until the move to OS X – even the (standard) live dragging of the iTunes window in OS 9 on my G4 Quicksilver is noticeably choppy).


(stupid _GetProcessInformation trap requiring certain fields to be prepopulated in the buffer you pass in … totally missed that in the docs the first 8 times I skimmed them!!).

Haha :giggle:


As spec’ed I don’t think this will sap the processor much at all and should run on any System 7-capable Mac including a Plus. I think.

The only obstacle would perhaps be calculating the angles for all the axis that the windows should move along? Not sure if that would be fast enough with many windows open on slower machines but maybe I'm underestimating the power of 68k processors :)


Answers:
- Should this work in/from any app, or just the Finder? – Definitely from any app! :)
- Does opening a folder (not an app/document) in the Finder also restore all windows? (If so, this becomes easier to code, I would just watch for a double click, Open menu item, or Cmd-O, probably, instead of having to figure out exactly what the thing being opened is.) – Preferably it wouldn't (you're able to navigate the file system and open new windows in OS X). Wouldn't it be possible to just look for an application switch? (that's how I thought about it in the algorithm above)
- Does leaving the Finder in any other way (Application menu) also restore all windows? – Yes (same idea as above)
- Does opening any window in any app generally restore all windows? – No (again, same idea as above :))
- On a multi-monitor setup, do all windows go to the edge of their own monitor? (I assume yes) – I've actually never tried Exposé on a multi-display setup, but I would assume so too. If it adds extra complexity to support multiple displays, maybe it would be ok to just have it work on the main display?
 
Last edited:

Crutch

Well-known member
Great answers. I especially love only worrying about the main screen, I was already thinking handling multi monitors was going to be a pain to implement, annoying to test and offer limited reward …

I took a short break from work today to replace my simple hide/show mockup with moving the windows to the edge of the main screen with a keystroke and it looks & works great! I’ll bake in some of your above thoughts over the next few days and share a video preview sometime soon.
 

Crutch

Well-known member
Do you have a preferred default shortcut keystroke?

Also, I assume if I’m in another app and expose the desktop, it automatically switches the Finder to the front, right?
 

PB170

Well-known member
Great answers. I especially love only worrying about the main screen, I was already thinking handling multi monitors was going to be a pain to implement, annoying to test and offer limited reward …
Haha, good! :) I'm totally fine with that. (But I personally only use a single monitor, so :) )

I took a short break from work today to replace my simple hide/show mockup with moving the windows to the edge of the main screen with a keystroke and it looks & works great! I’ll bake in some of your above thoughts over the next few days and share a video preview sometime soon.
Great, sounds awesome!

Do you have a preferred default shortcut keystroke?
I personally only use the top right screen corner, like in the mockup. Ideally, it would possible to set it up freely in a control panel, but I'm open to any keystroke at the moment – pick your favorite :)

Also, I assume if I’m in another app and expose the desktop, it automatically switches the Finder to the front, right?
Actually, no, that's not how it works in OS X. Which makes sense, in case you only want to check something the desktop without interacting with it.
 

PB170

Well-known member
By the way, double-clicking the icon of a folder that's currently open and hidden will bring that particular window back, while leaving the rest hidden.
 

Crutch

Well-known member
By the way, double-clicking the icon of a folder that's currently open and hidden will bring that particular window back, while leaving the rest hidden.
Oh I like that. That’s subtle. I think we can do that with the following rule: if the Finder calls SelectWindow on a Finder window that’s currently hidden, bring that window back only.

It does mean the notion of “hidden” vs. “normal” state now needs to be window-by-window as opposed to global, so, good to know.
 

PB170

Well-known member
Oh I like that. That’s subtle. I think we can do that with the following rule: if the Finder calls SelectWindow on a Finder window that’s currently hidden, bring that window back only.

It does mean the notion of “hidden” vs. “normal” state now needs to be window-by-window as opposed to global, so, good to know.
I didn't immediately realize that that extra "by the way" makes things quite a bit more complicated, since it means you can't just reverse the animations like I specified in the algorithm. If it makes things too complicated, restoring all windows and just making the selected folder/window the top, active one, would work fine too, I think.
 

kitsunesoba

Well-known member
Very cool idea, and a meaningful experience enhancement for classic Mac OS.

It looks like only the "show desktop" portion of Exposé is covered here, is there any interest in the window overview portion? It might not be as practical on devices with smaller screens due to the number of pixels available to work with (scaled windows might look garbled), but it would be quite nice on any PowerPC mac and probably even the later 68k macs that are hooked up to or equipped with higher resolution color displays.
 

Crutch

Well-known member
I plan to do the window overview thing also. I already have a working prototype of that actually. It will be included in version 1.1.
 

K Trueno

Well-known member
That mock-up is amazing! My only input is to make the corner configurable - even ResEdit on an integer is fine!
 

PB170

Well-known member
@Crutch, I take it from your answer that you don’t know of any way to access the system’s zoom rect algorithm, so I did some further investigation.

An acceleration rate of 1.18 seems to match the original animation quite well.

To minimize the number of calculations, I’m thinking it’s better to hard code the acceleration values and number of steps, and maybe also the duration, into the function and just have it be something like:

void ZoomRects( Rect source, Rect destination )

Here’s an array with values for the 12 steps (I made some more tests, and the animation consists of 12 rectangles, not 11, although it sometimes is a bit hard to tell):

{
0,​
0.01,​
0.03,​
0.05,​
0.08,​
0.11,​
0.17,​
0.25,​
0.35,​
0.50,​
0.71,​
1.00​
}

In Mac OS 8 and later, there are 6 rectangles with double the line width. They also differ in placement depending on whether a folder is opened or closed (though I don't really see why, so I think we can just ignore that):

OS 8–9 opening.jpg

OS 8–9 closing.jpg

The easiest thing would be to check for Mac OS 8 or newer and then just double the line width and skip every other step.

Have you started looking at the function or do you want me to make an attempt at it?
 
Last edited:

Crutch

Well-known member
Thanks! The way I’ve built this, when triggered the extension needs to walk through every window, compute the correct rect for each once hidden, save the original rect, then zoom all the rects together, and move all the windows. So the actual zooming function will take two arrays of rects as inputs. I will probably just take the acceleration of 1.18 (thanks!) or (1.18^2 for MacOS 8) as constant and precompute your array of fractional distances once on load. Coding the actual zoom rects function will be easy, I’ll do it this weekend. It’s also frankly the fun part so I am looking forward to doing it 😀 I’ll use fixed point math for the multiplies to keep it snappy.

My current bigger problem is moving windows properly all at once without calling MoveWindow repeatedly, which is visually too slow on a Mac II series. I hope to work this out tonight. It looks like the answer might involve nested calls to SaveOld and DrawNew and offsetting the structure and content regions manually followed by a call to MovePortTo. Advice welcome on a better way.
 
Last edited:

PB170

Well-known member
Thanks! The way I’ve built this, when triggered the extension needs to walk through every window, compute the correct rect for each once hidden, save the original rect, then zoom all the rects together, and move all the windows. So the actual zooming function will take two arrays of rects as inputs. I will probably just take the acceleration of 1.18 (thanks!) or (1.18^2 for MacOS 8) as constant and precompute your array of fractional distances once on load. Coding the actual zoom rects function will be easy, I’ll do it this weekend. It’s also frankly the fun part so I am looking forward to doing it 😀 I’ll use fixed point math for the multiplies to keep it snappy.
All right, that makes more sense! They all have to animate together of course. Easier when you have the full picture. I'll leave the coding to you then :D

My current bigger problem is moving windows properly all at once without calling MoveWindow repeatedly, which is visually too slow on a Mac II series. I hope to work this out tonight. It looks like the answer might involve nested calls to SaveOld and DrawNew and offsetting the structure and content regions manually followed by a call to MovePortTo. Advice welcome on a better way.
My idea here was to just hide all windows, draw the zoom rects and then create new, empty, inactive windows with the same sizes (or just build the shapes manually using QuickDraw, although that would become more complex on color systems and OS 8+) since only a few pixels of them are visible when at the edges of the display. I imagined that would be much faster than having to redraw all windows and the contents inside them at the new positions.
 
Last edited:

Crutch

Well-known member
That’s a good idea. (My initial prototype indeed just hid all the windows with _ShowHide, which is pretty fast, and didn’t draw anything on the edges — I might end up making that an option or defaulting to it for slower machines. By the way, the System 7 “Hide Others” command seems to “magically” hide all the windows instantly … I stepped into the code with Macsbug and am pretty sure it just erases all the windows using QuickDraw then forces a redraw of the desktop.)

I think drawing the partial windows with QuickDraw would be cumbersome if I want them to look like the edges of real windows including any design elements (like, in the Finder, showing a bit of the double line dividing the window “header” area from the main section, which will not exist in all windows). Creating new tiny empty inactive windows might not be any faster than just moving the original windows (probably actually slower due to the _NewWindow overhead). But there might be a split-the-difference solution: I could just erase the windows at their existing location using QuickDraw, update the WindowRecords manually like I said above, then let the Window Manager redraw everything at the new locations calling _PaintBehind or something. I will give it a try tonight, thanks for that thought.
 
Last edited:

cheesestraws

Well-known member
I could just erase the windows at their existing location using QuickDraw, update the WindowRecords manually like I said above, then let the Window Manager redraw everything at the new locations calling _PaintOneBehind or something. I will give it a try tonight, thanks for that thought.

I like this idea. Not too complicated, doesn't require you to keep pretend state in sync with real state, etc.
 
Top