• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

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

Crutch

Well-known member
OK this actually works!

1. Move every window manually by calling MovePortTo, then OffsetRgn on both the structure and content regions
2. UnionRgn the "before" and "after" regions of each window's strucRgn into a big cumulative updateRgn
3. Then, for each Layer*, call PaintBehind then CalcVisBehind on FrontWindow using the updateRgn

That's it! It takes care of all updating, including the desktop areas. It is MUCH MUCH faster than calling MoveWindow a bunch of times, presumably because now I am only updating the exposed regions once per layer instead of once per moved window. Excellent.

*This is necessary because otherwise PaintBehind and CalcVisBehind ignore background windows for other layers, leaving weird artifacts on the desktop. To do this I used the GetCurLayer and SetCurLayer calls from the Layer Manager header you awesomely showed me @cheesestraws ... which work precisely as one would expect them to. Thanks again.

I will clean this up a little over the next few days, then add the zoom rects and post a little preview video showing it in action.
 

PB170

Well-known member
Wonderful news!

That's a bit more QuickDraw terminology than I'm used to, but after consulting the eminent Think Reference for a while I believe I follow along pretty well :)

Fantastic that you found an efficient way to do the screen updates! Can't wait to see the first demo!

Did you manage to find out more about the Finder's "Hide others" command by the way? It is indeed instant.
 

Crutch

Well-known member
Did you manage to find out more about the Finder's "Hide others" command by the way? It is indeed instant.

I did. Documenting here for the record, this is very wonky …

It seems that the Finder (running 7.5.5) eventually gets an update event and redraws anything on the desktop whenever a window above it is moved … but it can take up to a second.

However it instantly gets an update event if a window above it is hidden (not moved) and it still hidden the next time the Finder gets time (via _WaitNextEvent).

So here’s how I got an instant refresh to work:
  • Hide each window with _ShowHide
  • Move each window
  • Call WaitNextEvent(0, …) with a nonzero sleep. The ‘0’ is so that I don’t actually intercept any events. I have to do this from inside my patch of _GetNextEvent (which is called by _WaitNextEvent) but I think it will be OK, I set a flag to prevent endless recursion.
  • Show each window again with _ShowHide
It seems to work! I think “Hide Others” is instant simply because it hides each window, keeps it hidden the presumably someone calls _WaitNextEvent soon thereafter. I stepped through all the code and there doesn’t seem to be much any other magic happening in there.

Weirdly, reordering this does NOT work. If you show the window before calling WaitNextEvent, the refresh occurs only after a ~1-second delay. The delay is random, almost as if the Finder checks for windows above it moving on a timer (but again, finds out about hidden windows instantly).

I would like to understand this better. Why doesn’t _MoveWindow cause the Finder to instantly refresh stuff on the desktop?
 

Crutch

Well-known member
Update - it seems the issue is that if any foreground app has a pending update event, the Finder doesn't get an update event right away. This may be because of how _CheckUpdate works (it only gives the Window Manager the first update event it can find). I think any pending foreground app update event "blocks" the Finder from seeing that it needs to update the desktop.

Calling _ValidRect to wipe out any pending update events in the foreground app seems to enable the Finder to update the desktop on the next _WaitNextEvent after a _MoveWindow, just as with _ShowHide.

Anyway .... more to come.
 

PB170

Well-known member
I'm not sure if I follow you here…

I got the impression that you had everything working in your post on Thursday. Is this just a side investigation like you said at the beginning, or did you have to deviate from your first solution?

This probably doesn't add much of value, but on my PowerBook I don't think I see the delay you mention. The redraw of icons and windows is not instant, but seems to occur immediately regardless of whether windows are moved manually or hidden with the "Hide others" command.

By the way, after some extra testing, it seems that the "Hide others" command is instant only if all windows belong to the same app. Hiding ten windows in one app is instant, but when hiding just two windows belonging to two separate apps, there's a small delay between them. Related to the layer stuff, I assume :)

Ah, the Toolbox… reminds me of my interactions with it (and ResEdit…:mad:🙂) earlier this year. "This should work…" "Eh no, that's not implemented." "Ok, this should work then". "No, not implemented like that." "All right, how about this then?" "Implemented, but not entirely like it should have been." "Ok, this should work at least!" "That's implemented, but there's a bug in the implementation." "All right, I'll code it myself…!" 🙂

Sorry that I can't help you out more here. Maybe cheesestraws or someone else can provide more insight. (Although you do seem to solve most things by yourself after a while 🙂)
 

PB170

Well-known member
Took some time during the week to create a design for the icon and control panel:
Design.png
I'm personally mostly interested in the reveal desktop part, so I would prefer a separate control panel just for it. (I certainly wouldn't mind creating a design for another one that combines both though, if we could find an adaption of the app windows feature that looks good and works well!)

The idea behind the "disable animation" checkbox is to disable the "zoom rect" animation. UX-wise the windows should still be displayed at the edge of the screen, to indicate that they didn't disappear entirely (not a huge user base for classic Mac OS any longer of course, but let's just pretend there is 🙂).

Also, holding down the option key in the control panel should change "Set…" to "Clear" (also available more clearly in the alert box).

I've included a resource file with all icons and pictures.

I hope I'm not putting to much work on you here @Crutch 🙂 If you'd share the resource file with me later on, I could help put together and/or tweak the window layout and the dialog box.
 

Attachments

  • Exposé desktop.rsrc.sit
    2.6 KB · Views: 2

Crutch

Well-known member
You’re brilliant! I was literally just thinking to myself, hmmm I’m not great at icon design I should ask @PB170 for any ideas :)

This will definitely save some time. I haven’t started working on the cdev at all yet, I will share a working prototype soon (was hoping tonight but the weekend is almost over already) which will have a locked-in keyboard shortcut (probably Cmd-Option-D, but feel free to name your preference) and a locked-in hot corner (probably top left).

On the other topic above, yeah I did a ton of research into the circumstances where the Finder does and does not refresh desktop icons after windows are moved out of the way. (My initial working prototype sometimes saw them refresh only after about a second.). Basically the Finder instantly refreshes in the following circumstances: if (1) another app is in front, (2) that app does not have a pending update event that hasn’t been processed (this can be forced by calling ValidRect on all its windows) and (3) the Finder gets time with a WaitNextEvent that doesn’t get “eaten” by another app with a pending update event. Using this knowledge I am now able to force the Finder to update after a hide event is triggered but before the zoom rects animate (which was my goal), unless the Finder is in front. That case is much more complicated and will probably require again refreshing from an offscreen buffer, or the user will just have to wait for the zoom rects to finish before seeing those icons.

All of the above applies with the keystroke trigger or when moving the mouse in a hot corner while not dragging. Today I just added the ability to trigger a hide event by moving the mouse to a hot corner during a drag. This is the hardest part of this by far because I have to somehow get control continually throughout the drag while in a spot where it’s OK to move windows around (e.g., not from a VBL task). The 7.5.5 Finder no longer uses _DragGrayRgn but instead the Drag Manager to handle icon drags, which complicates everything because it doesn’t make it easy to continually get a callback triggered. So I had to find a way to continually get control during a drag when nobody is calling GetNextEvent … it turned out that patching XorRgn seems like a good choice. As of today this now works which I am delighted about. However a remaining challenge is getting the revealed windows (or desktop icons) in the front app to update instantly during a drag. This will probably require saving a screen buffer, which could take up a bunch of memory on older systems and may require another checkbox. (If unchecked, and you reveal windows by moving the mouse to a hot corner during a drag, you would get blank windows which will then be auto-refreshed by the Finder when you get the mouse over them [including while still dragging] … again if triggered not during a drag, everything except the desktop now instantly updates before the zoom rects are drawn).

I had a similar thought on the interface to set a key combo. I was actually planning on factoring that out as a separate library for use in other cdevs.
 

PB170

Well-known member
You’re brilliant! I was literally just thinking to myself, hmmm I’m not great at icon design I should ask @PB170 for any ideas :)
Haha, thanks! 😄 Without you this wouldn't have happened at all, so likewise! I hope you like it.

This will definitely save some time. I haven’t started working on the cdev at all yet, I will share a working prototype soon (was hoping tonight but the weekend is almost over already) which will have a locked-in keyboard shortcut (probably Cmd-Option-D, but feel free to name your preference) and a locked-in hot corner (probably top left).
I always have it set to the upper right corner, like in the mockup, so if I'd have to make only one choice, that'd be it 🙂 But anything is fine for now!

On the other topic above, yeah I did a ton of research into the circumstances where the Finder does and does not refresh desktop icons after windows are moved out of the way. (My initial working prototype sometimes saw them refresh only after about a second.). Basically the Finder instantly refreshes in the following circumstances: if (1) another app is in front, (2) that app does not have a pending update event that hasn’t been processed (this can be forced by calling ValidRect on all its windows) and (3) the Finder gets time with a WaitNextEvent that doesn’t get “eaten” by another app with a pending update event.
You probably know more about the Finder at this point than Apple themselves did at the time, haha 🙂 Incredible what amount of work and tricks it takes to get the Finder/System to behave as one wants. Amazing work! 🌟

Using this knowledge I am now able to force the Finder to update after a hide event is triggered but before the zoom rects animate (which was my goal), unless the Finder is in front. That case is much more complicated and will probably require again refreshing from an offscreen buffer, or the user will just have to wait for the zoom rects to finish before seeing those icons.
Hmm… are you sure that is a good idea? The first priority, I feel, should be to keep the 0,25 s duration for the animation. I see now that I check it that the Finder actually does the redrawing first (off the top of my head I would have guessed it was the other way around) so your way would be consistent with that, which makes sense. What makes it different though is that the Finder doesn't have anything else to do after the animation is done, while we still have to draw the windows at their new positions, so the redrawing could be perceived as a lag. The downside of course would be that this would give the user less time to locate the icons, but with the way the Mac was designed, most users probably already know where the item they're looking for is located. I'd have to see it to be certain, but I feel the redrawing should come after the zoom rects. If it would make it easier to code it that way too, maybe you could try that first?

All of the above applies with the keystroke trigger or when moving the mouse in a hot corner while not dragging. Today I just added the ability to trigger a hide event by moving the mouse to a hot corner during a drag. This is the hardest part of this by far because I have to somehow get control continually throughout the drag while in a spot where it’s OK to move windows around (e.g., not from a VBL task). The 7.5.5 Finder no longer uses _DragGrayRgn but instead the Drag Manager to handle icon drags, which complicates everything because it doesn’t make it easy to continually get a callback triggered. So I had to find a way to continually get control during a drag when nobody is calling GetNextEvent … it turned out that patching XorRgn seems like a good choice. As of today this now works which I am delighted about. However a remaining challenge is getting the revealed windows (or desktop icons) in the front app to update instantly during a drag. This will probably require saving a screen buffer, which could take up a bunch of memory on older systems and may require another checkbox. (If unchecked, and you reveal windows by moving the mouse to a hot corner during a drag, you would get blank windows which will then be auto-refreshed by the Finder when you get the mouse over them [including while still dragging] … again if triggered not during a drag, everything except the desktop now instantly updates before the zoom rects are drawn).
Hmm… I can definitely see how that could be a problem. Not updating new windows during a drag or while the mouse button is down seems like standard system behavior – let go of the mouse button and everything gets updated. Tricky indeed…

Just so that I understand you correctly; does this mean that if you trigger it while in another app than the Finder, the icons (and Finder windows, after re-triggered) redraw automatically during a drag, but not if the Finder is the active app?

If whole windows get updated as soon as the mouse gets moved over them, while not ideal, it could work. But tricky if the same applies to icons, considering their size…

This is probably far-fetched, but since you seem to enjoy looking at stuff under the hood, maybe the spring-loaded folders feature of Mac OS 8 could provide some insights? That's the only case I can think of where new windows update while the mouse button is down.

I had a similar thought on the interface to set a key combo. I was actually planning on factoring that out as a separate library for use in other cdevs.
(y)
 

PB170

Well-known member
I'll just leave this here!

This is not a mockup, but a running extension under 7.5.5 on Mini vMac:

Wow, this is amazing!!

After dreaming about this for some 15 years, I was so fascinated just by my own mockup that I've probably replayed it more than a hundred times by now 😄 and to now see it take shape as an actual extension is so incredibly cool! Amazing work!

Can't wait to try it out on my PowerBook!
 

PB170

Well-known member
The window update looks totally okay by the way. I actually didn't notice it at first. The question is how it would work with icons, but the way it works here is an acceptable compromise, I think.
 

Crutch

Well-known member
Thanks for the kind words all. This has been fun and I hope useful.

This video shows the features I added tonight, including the ability to restore all windows by clicking near the edge of the screen, and the ability to restore just one window by opening its Finder icon (the others stay hidden). This was done by patching _SelectWindow.

@PB170 you'll notice I'm now using your very nice icon. I'll share the beta version after slightly more cleanup in the next 1-2 days -- I might just bang out the cdev too and you'd have a full-featured beta.



View attachment Screen Recording 2021-11-14 at 10.11.21 PM.mov
 

PB170

Well-known member
Wonderful! Really impressive how quickly you’ve been able to put this together! Seems you already got all the functionality in place. Completely ignoring all the Toolbox and low level intricacies, it would probably have taken me a week just to code the zoom rect animation and window movement.

It will be so cool to try it out. Feels like I’m waiting for a brand new system release for my PowerBook 😃
 
Last edited:
Top