OsX Spaces

From Lundman Wiki

OsX Spaces

For those of you with older OsX, you might have run VirtuaDesktop or similar, where you can change the 'switch from one desktop space to another'-animation in the setup. With SnowLeopard, it has 'Spaces' built in. Unfortunately, you can not change the animation, or speed of the animation for the Workspace-switch.

For me, the animation is rather annoying, and irritates my eyes. So I have attempted to see if I can remove it, or change the speed to be so fast it is virtually instant.

The Spaces/Dock appear to send the com.apple.switchSpaces notification when you attempt to change spaces.

This appears to be handled in CoreGraphics, or more precisely /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics.

There appears to be two top-level functions for it:

│__text:0000000000212F43                 public _CGSSetWorkspaceWithTransition 
│__text:0000000000212F43                                                      
│__text:0000000000212F43                 push    rbp             ; extern OSStatus CGSSetWorkspaceWithTransition(const CGSConnection cid,
│__text:0000000000212F43                                         ;         int workspaceNumber, CGSTransitionType transition, CGSTransitionOption subtype,
│__text:0000000000212F43                                         ;         float time);


│__text:0000000000213014                 public _CGSSetWorkspace
│__text:0000000000213014                 push    rbp             ; extern OSStatus CGSSetWorkspace(const CGSConnection cid, int workspace);


The comments are my additions to random API/Header files I have come across. What is peculiar is that CGSSetWorkspace actually appears to take 3 arguments. The logic for the 3rd argument goes as follows:

switch($arg3) {
  case 2:
     ecx  = 0x10000001;
     xmm0 = 0x40800000;  (float for Decimal=4)
     break;
  case 3:
     ecx  = 0;
     xmm0 = 0;
     break;
  case 1:
     ecx  = 0;
     xmm0 = 0x3e4ccccd;  ( float for Decimal=0.2)
     break;
  default:
     ecx  = 0x10000001;
     xmm0 = 0x3e4ccccd;  ( float for Decimal=0.2)
  }

__CGSSetWorkspace();

As you can guess, xmm0 is the float value of time as also specified in CGSSetWorkspaceWithTransition. The Dock appears to be the program to call CGSSetWorkspace(), interestingly not the CGSSetWorkspaceWithTransition(). So I was assuming that with ecx set to 'use the default', CoreGraphics kicks in and sets the time to 0.2 for normal, and 4 for the 'slow version' (hold shift).

However, binary edit of CoreGraphics and reboot did not have any direct/noticeable difference that I can see. Presumably ecx=3 should be 'Set Directly'.

For some peculiar reason, I can not load Dock into IDAPro, it just does not like it. So I am unsure what values the Dock sends.

However, gdb confirms the values sent to CGSSetWorkspace are as follows:

rax            0x0      0
rbx            0x0      0
rcx            0x7fff84405e3a   140735412198970
rdx            0x3      3
rsi            0x2      2
rdi            0x6d0b   27915
rbp            0x10bed7ce0      0x10bed7ce0
rsp            0x10bed7ca0      0x10bed7ca0
r8             0x87b3   34739
r9             0x0      0
r10            0x2c     44
r11            0x1200   4608
r12            0x0      0
r13            0x4      4
r14            0x10     16
r15            0x6      6
xmm0           0

Which means Dock is using type = 3, and ecx = 0 and xmm0 = 0. Disappointing. Amusingly enough, if I break at CGSSetWorkspace, and wait a second, then issue cont the switch is instant. So the timing for the transition has already been recorded it would seem. Strange.

Day 2

So no amount of breaking at CGSSetWorkspace, or _CGSSetWorkspace, then changing the registers, appear to have any effect on the slide-transition. So it appears the Dock will do the animation, then just call the CGSSetWorkspace. Even though that function can do transitions.

Worse, it has become apparent that the Dock is encryption. How incredibly cumbersome.

Anyway, stepping through the "invisible" code, I get the following call-list for switching workspace:

0x00000001001160c0 in dyld_stub_CGSMoveWorkspaceWindowList ()
0x0000000100116210 in dyld_stub_CGSSetNextWorkspace ()
0x0000000100116858 in dyld_stub__LSCopyFrontApplication ()
0x000000010011682e in dyld_stub__LSASNExtractHighAndLowParts ()
0x0000000100115a72 in dyld_stub_CFRelease ()
0x0000000100116858 in dyld_stub__LSCopyFrontApplication ()
0x000000010011682e in dyld_stub__LSASNExtractHighAndLowParts ()
0x0000000100115a72 in dyld_stub_CFRelease ()
0x0000000100116b34 in dyld_stub_malloc ()
0x0000000100116870 in dyld_stub__LSFindApplicationsItem ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x000000010011603c in dyld_stub_CGSGetWindowTags ()
0x000000010011635a in dyld_stub_CPSGetWindowOwner ()
0x0000000100116870 in dyld_stub__LSFindApplicationsItem ()
0x00000001001158b6 in dyld_stub_CFArrayGetCount ()
0x00000001001158c8 in dyld_stub_CFArrayGetValueAtIndex ()
0x000000010011682e in dyld_stub__LSASNExtractHighAndLowParts ()
0x0000000100115a72 in dyld_stub_CFRelease ()
0x0000000100116b34 in dyld_stub_malloc ()
0x0000000100116030 in dyld_stub_CGSGetWindowListWithTags ()
0x0000000100116036 in dyld_stub_CGSGetWindowOwner ()
0x000000010011602a in dyld_stub_CGSGetWindowLevel ()
0x0000000100116036 in dyld_stub_CGSGetWindowOwner ()
0x000000010011602a in dyld_stub_CGSGetWindowLevel ()
0x0000000100116348 in dyld_stub_CPSEqualProcess ()
0x0000000100116348 in dyld_stub_CPSEqualProcess ()
0x0000000100115ff4 in dyld_stub_CGSGetProcessValidity ()
0x000000010011639c in dyld_stub_CPSSetFrontProcess ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100115f46 in dyld_stub_CGSCopyWindowProperty ()
0x0000000100116066 in dyld_stub_CGSGetWorkspaceWindowCountWithOptionsAndTags ()
0x000000010011695a in dyld_stub_calloc ()
0x000000010011606c in dyld_stub_CGSGetWorkspaceWindowGroup ()
0x0000000100116114 in dyld_stub_CGSOrderWindowListWithOperation ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100115f46 in dyld_stub_CGSCopyWindowProperty ()
0x000000010011594c in dyld_stub_CFDataCreateWithBytesNoCopy ()
0x00000001001159ac in dyld_stub_CFDictionaryGetValue ()
0x0000000100115a72 in dyld_stub_CFRelease ()
0x00000001001159e8 in dyld_stub_CFMachPortGetPort ()
0x00000001001169a8 in dyld_stub_dispatch_get_concurrent_queue ()
0x0000000100116996 in dyld_stub_dispatch_async ()
0x0000000100116348 in dyld_stub_CPSEqualProcess ()
0x00000001001166c6 in dyld_stub_Microseconds ()
0x0000000100116996 in dyld_stub_dispatch_async ()
0x000000010011698a in dyld_stub_cos ()
0x0000000100115f70 in dyld_stub_CGSDisableUpdate ()
0x0000000100116b34 in dyld_stub_malloc ()
0x00000001001162fa in dyld_stub_CGSSetWorkspace ()
0x00000001001162e8 in dyld_stub_CGSSetWindowTransformsAtPlacement ()
the screen is pushed to the left, I see part of new space on right
0x00000001001166c6 in dyld_stub_Microseconds ()
0x0000000100116138 in dyld_stub_CGSReenableUpdate ()
0x000000010011698a in dyld_stub_cos ()
0x0000000100115f70 in dyld_stub_CGSDisableUpdate ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
the helper-gfx just popped up
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
the helper-moved to window 2
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x00000001001162e8 in dyld_stub_CGSSetWindowTransformsAtPlacement ()
screen is moved back to the right
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow ()
the helper-gfx removed.
0x000000010011609c in dyld_stub_CGSInvalidateWindowShadow () 
0x00000001001162e8 in dyld_stub_CGSSetWindowTransformsAtPlacement ()
we just switched to new desk top (no animation)
0x0000000100115fb2 in dyld_stub_CGSGetConnectionIDForPSN ()
0x0000000100116066 in dyld_stub_CGSGetWorkspaceWindowCountWithOptionsAndTags ()
0x0000000100116210 in dyld_stub_CGSSetNextWorkspace ()
0x00000001001162fa in dyld_stub_CGSSetWorkspace ()
We just lost the window belonging to old space
0x0000000100115fb2 in dyld_stub_CGSGetConnectionIDForPSN ()
0x0000000100116066 in dyld_stub_CGSGetWorkspaceWindowCountWithOptionsAndTags ()
0x0000000100116b34 in dyld_stub_malloc ()
0x0000000100116078 in dyld_stub_CGSGetWorkspaceWindowListWithOptionsAndTags ()
0x000000010011695a in dyld_stub_calloc ()
0x0000000100115f46 in dyld_stub_CGSCopyWindowProperty ()
0x0000000100116066 in dyld_stub_CGSGetWorkspaceWindowCountWithOptionsAndTags ()
0x000000010011695a in dyld_stub_calloc ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116138 in dyld_stub_CGSReenableUpdate ()
0x0000000100115f70 in dyld_stub_CGSDisableUpdate ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116b70 in dyld_stub_memset ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116138 in dyld_stub_CGSReenableUpdate ()
0x0000000100116b70 in dyld_stub_memset ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116c72 in dyld_stub_pthread_mutex_unlock ()
0x0000000100115fa0 in dyld_stub_CGSGetClientWindowCount ()
0x0000000100116b34 in dyld_stub_malloc ()
0x0000000100115fa6 in dyld_stub_CGSGetClientWindowList ()
0x000000010011618c in dyld_stub_CGSReleaseWindowList ()
0x0000000100116a5c in dyld_stub_free ()
0x0000000100116bbe in dyld_stub_objc_msgSend ()
0x0000000100116996 in dyld_stub_dispatch_async ()
0x00007fff8443ef8e in _pthread_start ()


Day 3

Aha, so the old Apple protected Binaries were AES of the TEXT segment in the binary, it turns out that 10.6 brings in a TYPE_2, which uses blowfish instead.

I managed to modify Singh's abp_encrypt to include the new type 2 and I can finally look at the Dock to figure out what it does to change Workspaces. The pthread start appears to be at: 0x000b2c6c.

The basic outline for Switching spaces appears to be:

Freeze display 
SetWorkspace
Animation
Resume display

The display freezing can be done a few ways, Tony Arnold (of Hyperspaces fame) uses CGSNewTransition(). But the Dock appears to simply use CGSDisableUpdate(). At this point I would change the Dock to call CGSDisableUpdate(), CGSSetWorkspace() then force a jump to the code starting at CGSReenableUpdate(). However, with the whole encrypted binaries etc hassle, this is starting to be a huge hassle.