Sep 28 2008

Still kicking

Categories: Everburning, Life, Work
Tags: ,

Well, as usual, it’s been quite a while since I last posted. Lots going on the past month or two. As you know, I got a job a AideRSS where I’ve been having a lot of fun working with Rails, MySQL, Ruby and a few other bits. Lots of interesting stuff happening on both the front and back-end systems.

Along with the job, we’ve moved into our new house in Waterloo. Stacy and I have been busy unpacking and organizing stuff for the last couple of weeks. Most stuff is sorted out now I think, Canada Post finally found our mail from the two week hold they had on stuff and both of us are settling in pretty well. Just waiting on the last few pieces of furniture to arrive.

Since we’ve finally got a house, and a bit of a yard, we picked up a puppy on Saturday. We got ourselves a little Welsh Corgi which we’ve named Odin. He’s been a lot of fun so far. Lots of energy and lots of sleep seem to be the orders of the day.

There have been lots of other fun side trips, losing of Foosball tournaments, wine tasting and other goings on that I’m leaving out at the moment.

Hopefully I’ll get back into some more regularly schedule writing now that things are starting to settle down. Although, I appear to be getting volunteered to do a presentation at DevWaterloo tomorrow so we’ll see what happens.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

May 09 2008

Going Fullscreen With Medium

Categories: Everburning, Programming
Tags: ,

So, I’ve started to write another Mac media application. This one is called Medium. I figure I might as well write some of this stuff down as I do it as I’m learning most of it along the way. If you’re looking for Medium you can get the source from the repository hosted at GitHub. There is also a bug tracker setup thanks to LightHouse.

Given that I’ve started this on about 4 separate occasions I’ve got a bit of random code sitting around that I’ll probably recycle during this exercise. One of the bits is to switch between fullscreen and windowed mode for the application.

The first version of this I had was pretty complicated and convoluted. Thankfully, with the release of Leopard (or was it one of the Tiger point releases?) going fullscreen in a Cocoa application got a lot easier.

Here’s what we’re going to be doing. We’re going to create an application that can go fullscreen. We’ll use the defaults controller to store if it should start fullscreen or windowed. If the users resizes the window in windowed mode we’ll remember the window size for the next restart. When we switch to fullscreen or to windowed mode we’ll fade the screen to black and back again. We’ll also setup a default property for picking the screen the application should appear on (although, as I don’t have a second monitor pulled into my PowerBook at the moment this will remain untested).

With that, on with the show.

We’ll get started by creating a new XCode project. We’ll be using a Cocoa Application as the basis for the project. I’m going to call mine MediumFS, you’re free to use what you want.

We’re going to be using three objects for this application:

  1. MediumApplication — The NSApplication subclass
  2. MediumController — The controller
  3. MediumConfig — Our configuration class

Lets go ahead and ctrl-click on the classes item in the sidebar and select Add -> File …. We’ll then select the Objective-C class for each item.

With our files created we’re going to setup the application to use our MediumApplication as the Principle Class. The first thing we need to do is change the parent class of MediumApplication to be NSApplication instead of NSObject. That’s actually the only change we’ll need to make in MediumApplication.h. You should end up with something similar to:

#import 

@interface MediumApplication : NSApplication
{
}
@end

Now, in the Resourses section open the Info.plist file. Under the key NSPrincipleClass change the string value to be MediumApplication instead of NSApplication.

With that we’ve setup our MediumApplication class to be the main class in Principle Class for the application. We’ll now fill in some application code to handle processing key events so we can pull out the apple-f key press.

There are two methods we’ll implement in MediumApplication: - (void)sendEvent:(NSEvent *)ev and - (BOOL)handleKeyDownEvent:(NSEvent *)ev. The sendEvent will be overriding the parent class implemention. handleKeyDownEvent is a private helper method.

- (void)sendEvent:(NSEvent *)ev
{
    int handled = FALSE;
    if (([ev type] == NSKeyDown) && ([self handleKeyDownEvent:ev]))
        handled = TRUE;

    if (!handled)
        [super sendEvent:ev];
}

sendEvent is pretty simple. If we’re dealing with a NSKeyDown event we’ll pass the event to our handleKeyDownEvent method. If handleKeyDownEvent processes the event it will return TRUE. If this isn’t the case we’ll pass the event up to our parent, NSApplication in this case.

- (BOOL)handleKeyDownEvent:(NSEvent *)ev
{
    BOOL handled = FALSE;
    NSString *notice = nil;
    unichar key = [[ev charactersIgnoringModifiers] characterAtIndex:0];

    /* apple-f for fullscreen */
    if ((key == 0x66) && ([ev modifierFlags] & NSCommandKeyMask))
        notice = @"Medium/Notice/Fullscreen";

    if (notice)
    {
        [[NSNotificationCenter defaultCenter]
                            postNotificationName:notice
                            object:nil
                            userInfo:nil];
        handled = TRUE;
    }
    return handled;
}

When handleKeyDownEvent is called we’ll pull the key pressed out of the event with charactersIgnoringModifiers: and we’ll grab the first character that’s returned. If that character is 0×66 or f and the NSCommandKeyMask modifier is set in the event we’ve got our apple-f.

With that, if we’ve found something to send a notification about we’ll post it to the NSNotificationCenter for anyone that’s interesting to pick up on.

That’s it for our application class.

Moving on we’ll setup or configuration object, MediumConfig. MediumConfig is basically a wrapper around NSUserDefaults which allows me to get the defaults with function calls instead of having the keys everywhere in my application.

#import 

@interface MediumConfig : NSObject
{
    NSUserDefaults *defaults;
}

+(MediumConfig *)instance;

-(int)screen;
-(void)setScreen:(int)screen;

-(BOOL)fullscreen;
-(void)setFullscreen:(BOOL)fullscreen;

-(void)windowDimensionsWidth:(int *)width height:(int *)height;
-(void)setWindowDimensionsWidth:(int)width height:(int)height;
@end

The MediumConfig object is a singleton so we provide an instance: method to retrieve the object. The rest of the class are just the accessors for the different configuration variables we care about.

#import "MediumConfig.h"

#define MediumScreenKey @"Medium/Screen"
#define MediumFullscreenKey @"Medium/Fullscreen"
#define MediumWindowDimensionWidth @"Medium/Window/Dimension/Width"
#define MediumWindowDimensionHeight @"Medium/Window/Dimension/Height"

static MediumConfig *medium_config = nil;

@implementation MediumConfig

+(MediumConfig *)instance
{
    if (medium_config) return medium_config;
    return (medium_config = [[MediumConfig alloc] init]);
}

-(id) init
{
    if ((self = [super init]))
    {
        NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];

        [defaultValues setObject:[NSNumber numberWithBool:FALSE]
                          forKey:MediumFullscreenKey];
        [defaultValues setObject:[NSNumber numberWithInt:0]
                          forKey:MediumScreenKey];

        /* setting these to -1 will cause the code to calcuate a good window size */
        [defaultValues setObject:[NSNumber numberWithInt:-1]
                          forKey:MediumWindowDimensionWidth];
        [defaultValues setObject:[NSNumber numberWithInt:-1]
                          forKey:MediumWindowDimensionHeight];

        [[NSUserDefaults standardUserDefaults] registerDefaults:defaultValues];

        defaults = [NSUserDefaults standardUserDefaults];
    }
    return self;
}

-(int)screen
{
    return [defaults integerForKey:MediumScreenKey];
}

-(void)setScreen:(int)screen
{
    [defaults setInteger:screen forKey:MediumScreenKey];
}

-(BOOL)fullscreen
{
    return [defaults boolForKey:MediumFullscreenKey];
}

-(void)setFullscreen:(BOOL)fullscreen
{
    [defaults setBool:fullscreen forKey:MediumFullscreenKey];
}

-(void)windowDimensionsWidth:(int *)width height:(int *)height
{
    if (width) *width = [defaults integerForKey:MediumWindowDimensionWidth];
    if (height) *height = [defaults integerForKey:MediumWindowDimensionHeight];
}

-(void)setWindowDimensionsWidth:(int)width height:(int)height
{
    [defaults setInteger:width forKey:MediumWindowDimensionWidth];
    [defaults setInteger:height forKey:MediumWindowDimensionHeight];
}

@end

The implementation is also pretty simple. We set a few default values into our user defaults hash and the accessors are just wrappers around the calls into the NSUserDefaults object. Nothing special to see here. Oh, as a side note, I’m allowing the user to pass a nil into the windowDimensionsWidth:height: method. I wasn’t sure if you’d always need both values and this just seemed easier in the long run.

Finally, onto the interesting bit, MediumController. We’re going to store references to the main window, a CGDisplayFadeReservationToken which I’ll discuss later and to our configuration.

#import 
#import "MediumConfig.h"

@interface MediumController : NSObject
{
    NSWindow *win;
    CGDisplayFadeReservationToken tok;  /** Fade out/in token */

    MediumConfig *cfg;
}

@end

With the header file created we’re going to do some work in Interface Builder. Double click on the MainMenu.nib file in the Resources section.

When interface launches you’ll see a few windows popup. One of which will be the control window (at least, that’s what I call it).

We don’t actually need the Window (window) item so we can click on that and hit delete. We’re going to be creating the window manually in the controller.

The next step is to create an instance of our MediumController in Interface Builder. This is done by dragging an Object controller from the library to our control window.

If the inspector isn’t open already click on the new object and press shift-apple-i. In the inspector press apple-6 to make sure you’re in the identity section. Set the Class field to MediumController.

In the control window ctrl-click on the File’s Owner object and drag the mouse over the Medium Controller object. You should have a popup appear. Click on the delegate entry. We’ll now receive notifications in our controller as the application changes state.

That’s it for our work in Interface Builder. Save your changes and close.

Given that MediumController is the large part of our code I’m going to go through it piece meal instead of giving a full listing.

#import "MediumController.h"

@interface MediumController (ControllerDelegates)
- (void)applicationDidFinishLaunching:(NSNotification *)notice;
- (void)applicationWillTerminate:(NSNotification *)notice;
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app;
@end

@interface MediumController (WindowDelegates)
- (void)windowDidResize:(NSNotification *)notice;
@end

@interface MediumController (WindowManipulation)
- (void)fullscreenNotice:(NSNotification *)notice;
- (void)changeFullscreenState;
- (NSScreen *)screen;
- (void)fadeOut;
- (void)fadeIn;
@end

We start by creating a bunch of method declarations. I like to group my methods into categories as I think it keeps things cleaner. As you can see we’re going to implement some Controller Delegates, Window Delegates and Window Manipulation functions.

@implementation MediumController

- (id)init
{
    if ((self = [super init]))
    {
        cfg = [[MediumConfig instance] retain];

        /* catch the full screen events */
        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc addObserver:self
               selector:@selector(fullscreenNotice:)
                   name:@"Medium/Notice/Fullscreen"
                 object:nil];

    }
    return self;
}

@end

Our init: method is pretty simple. We grab, and retain an instance of the MediumConfig singleton. We’ll be using this throughout our code later. We then add an observer for the Medium/Notice/Fullscreen event that we setup in our MediumApplication code. Whenever the given event is fired the fullscreenNotice: method will be executed.

#pragma mark Controller Delegates
@implementation MediumController (ControllerDelegates)

- (void)applicationDidFinishLaunching:(NSNotification *)notice
{
    NSRect screenFrame = [[self screen] frame];
    int width = -1, height = -1;

    [cfg windowDimensionsWidth:&width height:&height];
    if ((width < 0) || (height < 0))
    {
        width = screenFrame.size.width * 0.75;
        height = screenFrame.size.height * 0.75;
    }

    NSRect frame = NSMakeRect(((screenFrame.size.width - width) / 2),
                              ((screenFrame.size.height - height) / 2),
                              width, height);

    win = [[NSWindow alloc] initWithContentRect:frame
                styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
                  backing:NSBackingStoreBuffered
                    defer:NO
                   screen:[self screen]];
    [win setDelegate:self];
    [win setTitle:@"MediumFS"];

    /* XXX make this our real view */
    NSView *view = [[NSView alloc] initWithFrame:[win frame]];
    [win setContentView:view];

    if ([cfg fullscreen]) [self changeFullscreenState];

    /* do this after we've gone fullscreen */
    [win makeKeyAndOrderFront:self];
}

Once the application has launched we'll, as the delegate, get a applicationDidFinishLaunching: notification. We'll use this to create our main window by hand.

Our window will be set to 75% of the screen width and height if the user hasn't already site a width and height. To do this we'll first need to get the screen frame. We'll see the screen method later, just rest assured it returns an NSScreen object we can work with. We'll then check in our configuration if the user has set a width and height previously. If not, we use the screen frame to calculate the width and height.

With that done we'll create the frame for our window with NSMakeRect. We do some simple calculations to place the window in the middle of the screen and use the width and height we calculated earlier.

We can now create our window. We use the frame we just calculated and apply a style mask of NSTitledWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask. This way we can have a title, can minimize the window and allow the user to resize the window. We set the backing to NSBackingStoredBuffered and the screen to the screen returned from the screen method.

With the window created we set the controller to be the delegate of the window. This way we can catch certain window events which we'll see in a bit. setTitle: just sets the window title to @"MediumFS".

Since I don't actually have anything to display in this window I just create an NSView and set it as the content view of my window. You can add your actual views as needed.

With the window created and our content in place we check if we should be fullscreen by querying the MediumConfig object. If we should be fullscreen we transition by calling changeFullscreenState. With all that done we give the window the focus with makeKeyAndOrderFront:.

Phew, OK, that was a lot of work right off the bat. Don't worry, things will get shorter from here on out.

- (void)applicationWillTerminate:(NSNotification *)notice
{
    if ([cfg fullscreen]) [self fadeOut];

    /* hide the window so it doesn't flicker on fade in */
    [win orderOut:nil];
    [win release];
    if ([cfg fullscreen]) [self fadeIn];

    [cfg release];
}

When the user hits apple-q or otherwise terminates the application our applicationWillTerminate notification will be triggered. We first check if we’re in fullscreen mode. If so, we fade the screen out with fadeOut. We then use orderOut: to hide the window. We don’t want it to flicker back into existence when we fade the screen back in. With the window hidden we can release it as we no longer need it. Then, as before, if we’re fullscreen we fade back in with fadeIn. We then release the config object as we’re done with it.

- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
{
    return YES;
}

@end

The final method in our controller delegates section is applicationShouldTerminateAfterLastWindowClosed we’re just returning a simple YES here so when our window gets closed the application will terminate. Nothing special, move along.

We’ll now move into the window delegate methods, of which there is only one.

#pragma mark Window Delegates
@implementation MediumController (WindowDelegates)

- (void)windowDidResize:(NSNotification *)notice
{
    NSRect frame = [win frame];
    [cfg setWindowDimensionsWidth:frame.size.width height:frame.size.height];
}

@end

After the window has been resized the windowDidResize: notification will be sent. We use this to update the window width and height in MediumConfig so we can open the window to the same size on the next launch. Simple enough.

Our last set of functions I’m put under the name Window Manipulation.

#pragma mark Window Manipulation
@implementation MediumController (WindowManipulation)

- (void)fullscreenNotice:(NSNotification *)notice
{
    [self changeFullscreenState];
    [cfg setFullscreen:(![cfg fullscreen])];
}

As we saw above when the Medium/Notice/Fullscreen is sent it will trigger the fullscreenNotice: method. We simply call changeFullscreenState and invert the current fullscreen flag in MediumConfig. Short and sweet.

Finally we get to what everyone actually wants to see, the fullscreen method.

- (void)changeFullscreenState
{
    NSView *view = [win contentView];
    NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:
                            [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens, nil];

    [self fadeOut];

    if ([view isInFullScreenMode])
        [view exitFullScreenModeWithOptions:opts];
    else
        [view enterFullScreenMode:[self screen] withOptions:opts];

    [self fadeIn];
}

As I mentioned earlier fullscreening got a lot simpler at some point. First thing we do is grab our content view that we set into the main window. It’s on this view that we’ll be changing the fullscreen state.

We’re going to be passing a dictionary of options into the fullscreen methods. In this case I’m just setting one key into the dictionary, NSFullScreenModeAllScreens which I’m setting to NO as I only want to go fullscreen on one screen.

With that created we’re calling fadeOut to fade the screen to make a nice transition.

The screen is blanked and we’re posed to go fullscreen. Since this function is just switching back and forth the first thing we do is query the current fullscreen state of the view with isInFullScreenMode if this is TRUE we’ll be going back to windowed mode. Switching to windowed mode is as simple as calling exitFullScreenModeWithOptions: on our view. Similarly, going to fullscreen mode is as simple as calling enterFullScreenModel:withOptions: and passing in the screen we want to be fullscreen on. That’s it. Pretty simple eh?

With the fullscreen mode changed we’ll use fadeIn: to fade the screen back from black.

Three more helper methods and we’re done.

/**
 * Return the screen that we should be displaying upon
 */
- (NSScreen *)screen
{
    NSArray *screens = [NSScreen screens];

    /* if we've only got one screen then return it */
    if ([screens count] < = 1) return [NSScreen mainScreen];

    /* try to use the screen set by the user */
    int screen = [cfg screen];
    if (screen > [screens count]) screen = [screens count] - 1;
    else if (screen < 0) screen = 0;

    return [screens objectAtIndex:screen];
}

The screen method will return a screen to display on. This will get all the screens by calling [NSScreen screens]. Then, if we only have one screen, it'll return that. Otherwise we check if the user has configured a screen to display on or return the first screen in the list.

The fade methods are both pretty similar.

- (void)fadeOut
{
    CGAcquireDisplayFadeReservation(25, &tok);
    CGDisplayFade(tok, 1.5, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, TRUE);
}

- (void)fadeIn
{
    CGDisplayFade(tok, 1.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, TRUE);
    CGReleaseDisplayFadeReservation(tok);
}

@end

On fadeOut we acquire a reservation token and tell the screen to fade. We fade from kGCDisplayBlendNormal to kGCDisplayBlendSolidColor and provide the 0, 0, 0 black colour. fadeIn works in basically the opposite order. We fade from kCGDisplayBlendSolidColor to kCGDisplayBlendNormal and start from 0, 0, 0, black. Once faded we release our reservation token.

That, as they say, is that. If you build and run you should be able to hit apple-f to switch to and from fullscreen mode.

Cool, eh?

MediumFS contains the full source for the application.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Jun 26 2007

I suck

Categories: Everburning, Life

Yes, that’s right. I suck at updating this thing. It’s been what a month? No real excuse. Been busy, but that’s beside the point. I just haven’t had much that struck me as blogging material for the last little while.

Lots of things are happening, Adam and Mels wedding, family gatherings, Stacy’s trip, etc. Just, don’t feel like recording it here at the moment.

Oh well.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Nov 23 2006

Delinquent

As you’ve probably noticed I’ve been delinquent with the whole blog thing again. I’ve been doing stuff to the blog, just not on the blog.

As you can see there is a new layout. I’ve converted from my custom Rails thingy to a Wordpress blog. The main reason, I can’t be bothered to update my code. Wordpress has other people working on it who can add cool things that I can use. I wrote some scripts to import all of my old posts with comments and to move my quotes over as well. So, everything should be as it was before. Managed to maintain my link names which is nice.

I’ve also started to setup a photo gallery again. I’m using Photostack and it seems to be working pretty well so far. They make it really simple to add new albums and things. I’ve only got some Amsterdam pictures up there at the moment. I’ll end up doing some theming on that too to make it match the rest of the site which is why it doesn’t show up on the sidebar yet.

The condo stuff is coming along nicely. I should be getting a call at some point today to go pick up the key (or I’ll just go up at 5pm as it has to be there then.) Everything has gone through that I know of. Now we just need to paint and move in. Almost there.

I’ve written a few EWL tutorials over on wiki.edevelop.org. They’re introductions to EWL programming, although they do touch on some of the harder things like drag and drop support. You’ll basically end up with an image viewing application (showing a grid of images and then full images when clicked on) that allows you to drag images from another program onto it to display those images. The three tutorials are: EWL Introduction, EWL Introduction II and EWL Introduction III

The last big EWL thing is a nice Ewl_Text speed improvement. The Ewl_Text widget I wrote had never been optimized, we hadn’t had a lot of text in it before. This changed when pfritz wrote the syntax highlighting for C source files. Suddenly we had a lot of formatting nodes to deal with. My original tree approach fell apart under the load and I re-wrote it to use a simple linked list about a month ago. This managed to bring the time from about 6 minutes to 6 seconds, which is good. (The actual original time was 12 minutes which another small change dropped to 6 minutes). 6 seconds ain’t bad, but Nathan did some more profiling and noticed two hot spots in the code. One was when we validate the text we’re making an extra copy and another was how I was looking up some formatting nodes when converting indexes. Well, once I got the copying removed it dropped to about 5 seconds. Once I got the lookup fixed, we managed to hit 1 second, pfritz is seeming about 0.63 seconds. So, I’m quite happy with that. It now takes longer to draw then it does to create the formatting tree.

We’ll see if I manage to get back onto a regular blogging schedule again. Still a bit hectic with the moving and all going on at the moment.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Aug 22 2006

Self Inflicted Hell

Categories: Everburning

If you’ve been having issues with the site the last couple of days, things like crashes or messed up formatting, I apologize.

My last post Finding the Tree in the Forest seems to have caused BlueCloth to go nuts.

For those not in the know, BlueCloth is an implementation of Markdown for Ruby.

It seems that whenever I tried to edit the post, or after it had been loaded a certain number of times the ruby process running the site would crash with an out of memory error. Specifically it was crashing in the markdown call that I was using. I took a quick look at BlueCloth and noticed that its to_html method was duplicating the string about 10 times and letting the garbage collection clean it up. Oops. This seemed to be just enough to trigger the memory limits on TextDrive.

So, I decided to switch to RedCloth which is an implementation of Textile in ruby. I spent a bit of time testing that out and it seemed to be working so I converted the problem post over and everything seemed to be going well. I got about 90% of the post converted before I ran into an issue. It seems that the parser was screwing up and marking up some stuff it wasn’t suppost to. My code was starting to lose important symbols and do strange things like add bolding and strikethrough where it wasn’t suppost too. Not good, not good at all.

Eventually, I gave up on that too and dropped both. I’m now using straight HTML code. Doing hand markup. Serves me right for being lazy I guess.

The only problem with this? Every bloody post I’ve done had to be reformatted. Have its BlueCloth formatting removed and changed into the appropriate HTML code. This only took, 2, 2.5, hours?

Oh, but it gets better. That’s just the fun from tonight.

I spent about 2 hours writting that post last night. I then spent about 2.5 hrs getting it marked up and formatted so it looked correct in BlueCloth. Ok, that’s a good chunk of time, but it looked pretty good.

I get to work this morning and get a message from Stacy. Your site is buggered in Internet Explorer. Taking a look, sure enough everything goes screwy half way down the page. Ok, more digging, more fiddling. Spend about an hour undoing formatting from the night before and everythings looking good in IE. At which point I see the random crashes starting to occur. Editing, loading, viewing RSS feed. Crashing all over the place.

So, that’s my great adventure. My journey into my self inflicted hell. So much of hoping to use the easy route of a nice markup language.

What’s the moral? Fucked if I know. Let me know if you figure it out.

I need some sleep.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Next Page »