Programatically Controlling Spaces

March 22nd, 2008 by Joe Ranieri

There’s been a lot of questions on mailing lists about how to control Spaces programatically. So far, there’s been no results from these discussions. Well, fortunately accomplishing this is quite simple, if you’re comfortable using private APIs.

Functions to query the current settings:

CGError CGSGetWorkspace(CGSConnectionID cid, CGSWorkspaceID *outWorkspace);
extern bool CoreDockGetWorkspacesEnabled();
extern void CoreDockGetWorkspacesCount(int *rows, int *columns);

You’ll probably also want to switch spaces. You can accomplish this by using a distributed notification. This tells the Dock to switch spaces, giving you all of the normal animations:

- (void)switchToSpace:(CGSWorkspaceID)spaceNumber {
  // note that the notification is 1 based, but CGSWorkspaceID is zero based!
  [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"com.apple.switchSpaces"
                                                                 object:[NSString stringWithFormat:@"%i", spaceNumber + 1]];
}

And lastly, you’ll probably want to get notified when things change:

//! Gets called when the current space changes.
void SpaceChangedCallback(CGSNotificationType type, CGSWorkspaceID *workspace, unsigned int dataLength, Controller *self) {
  if(*workspace != kCGSTransitioningWorkspaceID) {
    // do something
  }
}
 
//! Gets called when the user enables or disables Spaces.
- (void)spacesEnabledChanged:(NSNotification *)notification {
  // do something
}
 
//! Start listening for workspace related changes.
- (void)registerForNotifications {
  // listen for when Spaces is enabled or disabled
  [[NSDistributedNotificationCenter defaultCenter] addObserver:self
                                                      selector:@selector(spacesEnabledChanged:)
                                                          name:@"SpacesEnableChange"
                                                        object:nil
                                            suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
 
  // listen for when the current space changes
  CGSRegisterNotifyProc((CGSNotifyProcPtr)SpaceChangedCallback, kCGSNotificationWorkspaceChanged, self);
}

You can find a full code example in the CGSInternal svn repository under Examples/SpacesSwitchingMenu, which mimics the Spaces menu extra.

Happy hacking!


Moral of the Story (Part 2ish)

March 5th, 2008 by Joe Ranieri

On a somewhat related topic from last time, I thought I’d mention some other nasty hacks I’ve seen passed off as safe.

Pre-emptive Threading

There are several libraries that claim to offer this in REALbasic. The problem is that it can’t be done. The RB runtime simply isn’t thread safe, not to mention the framework itself!

Something as trivial as referencing a string has the potential to bring down your whole application in bizarre and difficult-to-detect ways. Even if your code doesn’t directly touch the runtime, the code the compiler generates might. The rules on when it does so (to yield to another thread, for example) are undocumented and extremely likely to change from version to version.

To make a long story short: it’s a bad idea.

Weak References

Another library implemented weak references in a horrible manner. They simply unlocked the object and returned it, decrementing its reference count.

At first glance this would appear to work - your object goes away even if another object holds a weak reference to it. However, the problem occurs if you attempt to access the “weak reference” after its original object goes away. This will almost certainly crash your program.

The problem is that your “weak reference” still points to the place in memory where the object lived. By the time you use your “weak reference,” REALbasic has most likely already tossed something else in that location.

To make a long story short: it’s a bad idea.


Moral of the Story

March 3rd, 2008 by Joe Ranieri

Back January 2006, I posted a neat trick to get the address of an object. This was on a mailing list full of people who knew what we were doing. We understood that these were hacks and that they shouldn’t be used in actual software.

However, some people were still concerned:

I’m also worried that you guys will write libraries based on this technique, understanding the risk, but will then release them for the use of other RB users, who might not know what they’re getting into.

Mars Saxman

Sadly, this is exactly what’s happened. Someone has taken this trick and used it a library they released. Now everyone who uses that library is relying on this hack never to break.

It’s even worse because the developer of that library didn’t even need this hack. There are better, legal ways to do this. The only reason I can see that he uses the hack is to cut corners.

Now, if upgrading to a new version of REALbasic breaks everyone’s software that uses this hack… who’s going to get blamed? Probably REAL Software and not the library author.

Anyways, the moral of the story is to use hacks responsibly.


Where’s this class from?

February 14th, 2008 by Joe Ranieri

Occasionally I want to know where a specific class is implemented. Today I wanted to know where iCal’s main calendar view was implemented, in hopes of stealing it for my own uses.

I don’t feel much like typing the description, so here’s a video “tutorial.”


Hijacking JavaScript Functions

February 11th, 2008 by Ryan Govostes

I spent several hours last week tracking down an obnoxious bug in WebKit JavaScriptCore’s Function.toString() implementation where multiple var declarations on a single line would get grouped with parentheses (Radar, Trac, Bugzilla). The incredible WebKit team made a working patch within 23 hours of my report, kudos to them.

Joe and I very much enjoy hijacking code. While this may become a recurring theme on this blog, right now I’m just going to share how to do it in JavaScript. Let’s say one of the .js files included from your page declares the function foo() as

function foo() {
    var x = "spinach";
    alert(x);
}

If you run foo(), it will obviously display a dialog reading “spinach”. But for some reason or other, you need it to say “eggplant” instead, but you can’t alter the original code. Furthermore, pretend we’re in a situation where it’s inconvenient to reimplement the function. No problem — we can just hijack it and modify the code:

// First, get the code to the function
// Here we strip off the "function foo() { ... }"
var c_foo = foo.toString().replace(/^[^{]*{/|>, '').replace(/}$/|>, '');
 
// Now perform whatever modifications we need
c_foo = c_foo.replace('spinach', 'eggplant');
 
// Now overwrite the function with a new Function object using our code
foo = new Function(c_foo);

Now whenever foo() is called, it will use our modified code. Of course, in this simple example we could have just redeclared foo(), but this becomes far more feasible with more complex code.

As an interesting aside, when you call toString() on a function, it’s not actually printing out the function as it was declared (hence, the bug I mentioned at the top of the post). In the case of JavaScriptCore (used by Safari), a function is broken into a graph of various nodes representing keywords, loops, strings constants, and so on. When you convert the function to a string, it traverses the graph and constructs a more familiar representation of it. As such, it’s kind of a handy way to reformat obfuscated (or just hard to read) code, but you’ll lose commenting.


Dock Extras

November 2nd, 2007 by Joe Ranieri

In 10.5, applications can update their dock icon even if they aren’t running, though the only known use of this is iCal. The way this works is that the Dock loads a piece of code (the Dock Extra) to draw the application’s icon for it.

Ok, so that’s not entirely accurate. The Dock tells SystemUIServer to load the Dock Extra and communicates via Mach IPC to get the icon. So, Dock Extras are NOT a way to load code into the Dock.

The Dock Extra bundle is found by the DockExtra key in the application’s Info.plist. As needed, SystemUIServer loads the Dock Extra bundle and creates an instance of its principal class.

As with manipulating the Dock icon in an actual application, you need to request it be redrawn yourself. To do this, you call getContext: to get the graphics context and the rect to draw in. Once you are done drawing, simply flush the context and call setDockImageFromContext:.

It should be noted that the SystemUIServer does NOT unload the bundle when you drag the icon off the Dock (it simply calls invalidate). This causes problems if you try to delete your app, because the Dock Extra is still in use.

It should also be noted that Dock Extras are only invoked when the application is visible on the Dock. It isn’t invoked when showing stacks or if there’s an alias to the application on the Dock.

Download DockExtras Example


Scripting REALbasic through AppleEvents

October 31st, 2007 by Jonathan Johnson

Here is a Halloween treat for our REALbasic-using readers:

At some point you may need to write a tool to script builds in REALbasic. For us, we have something coming down the pipeline in a few weeks that needs to do just this (let the speculations begin!), and I recalled an undocumented feature of the REALbasic IDE that makes things easier for us:

There exists an AppleEvent command of class ‘RBae’ and type ’scpt’. The direct object parameter is a string, which is the IDE Script source code to execute.

Of course, as an undocumented feature, I must stress that this could be removed at any time. The included IDE scripting command line utility is often enough to get the job done. The method described here requires less overhead, but has the downside of being harder to communicate with. (If you must communicate, you can always write a file out by using DoShellCommand. However, if you’re needing to anything beyond something simple, documented ways of doing this are much better.)

I’ll leave you with some sample code. This is all there is to it, enjoy!

Sub RunIDEScript(script as String)
  dim ae as AppleEvent
  ae = NewAppleEvent( "RBae", "scpt", "RBv2" )
  ae.StringParam("----") = "call BuildApp(8)"
  if not ae.Send then
    MsgBox "Error"
  end if
End Sub

Hello from the Collar City

September 20th, 2007 by Ryan Govostes

While I’m sorry to say I don’t have any teasers for you, I thought I’d introduce myself anyway: I’m Ryan Govostes, one of the other developers that Jon alluded to. My interests include cryptography, data mining, and artificial intelligence — I think you’ll find these topics pop up in the software I write.

When I’m not in the Labs, I am an undergraduate at the Rensselaer Polytechnic Institute in Troy, NY. I major in Computer Science and am involved in research in the Department of Cognitive Science. Occasionally I help with the student newspaper, The Poly.

I’m not very consistent in writing for my own blog, but I’ll try to keep you up to date on whatever is cooking in my corner of the Labs. I look forward to seeing you at my next posting, whenever that should be.


Tap tap tap?

September 19th, 2007 by Joe Ranieri

Ah, it’s been a while since I’ve posted. We relaunched the website, became a corporation (Alacatia Labs, Inc.), and have been doing some pretty cool stuff. Even though things have changed, we haven’t forgotten what brought you here.

I’d really love to tell you what we have up our sleeves, but not until we’re almost ready to ship. I will, however, tease you REALbasic devs with a neat hack that’ll never see the light of day.

CocoaDemo.mov