Main

December 09, 2007

Always - on Print Preview

Some software is, in my experience, not quite WYSIWYG when it comes to the final printed output so I have gotten into the habit of viewing a PDF in preview before I actually send a document to my printer. This has saved many trees.

Apple makes it fairly easy to do this. Just command-P and click on the weird-looking PDF button to get the drop down menu to select "Open PDF In Preview."

preview.jpg

I would like to see most every print job automatically open in preview without needing to take my hands off of the keyboard. One can make this happen with a little bit of effort, some help from Folder Actions, and a little piece of free third-party software. Here's how I do it.

First, download and install Cups PDF per the instructions on its home page. This will create a printer and driver that dumps a pdf file into ~/Desktop/cups-pdf/. Set your default printer to CUPS-PDF.

Next, you will setup a folder action on the ~/Desktop/cups-pdf/ to open each file that gets dumped in there in preview. I keep my folder actions scripts in ~/Desktop/Folder\ Actions/, so I put the following applescript (derived from a similar script that ships with Leopard) there.

  
(*
add - new item alert

This Folder Action handler is triggered whenever items are added to the attached folder.
The script will display an alert containing the number of items added and offering the user
the option to reveal the added items in Finder.

Copyright © 2002–2007 Apple Inc.

You may incorporate this Apple sample code into your program(s) without
restriction. This Apple sample code has been provided "AS IS" and the
responsibility for its operation is yours. You are not permitted to
redistribute this Apple sample code as "Apple sample code" after having
made changes. If you're going to redistribute the code, we require
that you make it clear that the code was descended from Apple sample
code, but that you've made changes.
*)

property dialog_timeout : 5 -- set the amount of time before dialogs auto-answer.

on adding folder items to this_folder after receiving added_items
try
tell application "Finder"
--get the name of the folder
set the folder_name to the name of this_folder
end tell

-- find out how many new items have been placed in the folder
set the item_count to the number of items in the added_items
--create the alert string
set alert_message to ("Folder Actions Alert:" & return & return) as Unicode text
if the item_count is greater than 1 then
set alert_message to alert_message & (the item_count as text) & " new items have "
else
set alert_message to alert_message & "One new item has "
end if
set alert_message to alert_message & "been placed in folder " & «data utxt201C» & the folder_name & «data utxt201D» & "."
set the alert_message to (the alert_message & return & return & "Would you like to view the added items?")

display dialog the alert_message buttons {"Yes", "No"} default button 1 with icon 1 giving up after dialog_timeout
set the user_choice to the button returned of the result

if user_choice is "Yes" then
tell application "Preview"
--fire it up
activate
--open the items
open the added_items
end tell
end if
end try
end adding folder items to

Ctrl (or right) click the cups-pdf folder, Enable Folder Actions, then Configure Folder Actions via the contextual menues seen below.


EnableFolderActions.jpg

When you select "Configure Folder Actions" you'll set the script that runs every time a new file is added to that folder to the one above.

folderActions.jpg


Now every time you hit command - P to print in a application, you'll hit enter to print to your default CUPS-PDF printer, which will write a PDF to the cups-pdf folder on your desktop. The folder action will then pop-up this window (for five seconds).

PreviewMe.jpg


All you have to do is hit enter.

So! Every time I print I hit command - P, enter, wait a sec, enter. Instant preview.

November 11, 2007

More Leopard calDAV fun. Migration!

As you know from my last post, I have lots and lots of calendars. It used to be much much worse. Sharing calendars between my three Macs before Leopard server was a pain in the ass. Sure, you could put a calendar on a regular DAV server and subscribe to that calendar on your other machines, but you could not write to that calendar from any machine other than that which created it initially. All subscribed machines were read-only. What does this mean to me? You know about my 14 calendars, right? Triple it. That's right. I used to have 42 calendars on each of my 3 machines. 14 calendars to which I could write and 28 to which I was subscribed so I could read any appointments from my other two machines. What a mess.

Leopard calendar server has brought me back to a debatably sane 14 calendars. But how does one migrate all of those calendars spread across multiple machines onto the shiny new calDAV server? Like so:

1. Subscribe to your shiny new calDAV server (under the Accounts tab) in iCal preferences.
2. Create a new calendar on your calDAV server through iCal (File Menu -> new Calendar -> your server) for each of your 14 calendars.
3. Create a *.ics file from your old calendar by:
a. Selecting the calendars you want to export by using the export functionality in iCal (File Menu -> Export) on each of your machines or...
b. Mounting your DAV server and downloading all of 'em at once (assuming you're keeping your calendars on a DAV server now, which I was, this method is a lot easier for obvious reasons)...
4. Select the calendar from the source list (one residing on your new calDAV server) and import the *.ics files you wish to store in the new calendar (File Menu -> import).

Your appointments are now on your calDAV server and will show up on all of your subscribed machines fully editable from all.

More Leopard calDAV fun. Moving appointments between calendars from different sources.

I'm scheduled to within an inch of my life, so there is no surprise that I have 14 calendars in iCal right now. I have calendars for billable hours vs. non-billable hours. I have calendars on my calDAV server that I share with clients, some under a different username than others, and some that I use to synchronize personal calendars across multiple machines, and yet another that I share with my assistant. (Hi Christina!) I have calendars coming out my ears from various sources.

Very often, I'll place an appointment in one calendar and later wish to move it to another. Perhaps I'll have a tentative appointment with a client, so I'll put that in my non-billable calendar. When that appointment happens, perhaps I'll put that into my billable calendar. Unfortunately, my billable calender is under a different username on my calDAV server than my billable calendar (one of them I share, the other I do not). Normally, when I want to move an appointment from one calendar to another I right/ctrl-click the appointment and select the calendar I want to move it to. Unfortunately, the only calendars listed in that contextual menu are those under the same user account as the appointment I'm trying to move. Bummer.

The workaround:

1. Select the appointment you would like to move.
2. Use your favorite method of getting it to the clipboard (I use command-X to cut)
3. Select the calendar from the source list to which you would like to move the appointment
4. Paste (command - v for me)

All appointment data when cut or copied is retained when pasted into the other calendar.

Wouldn't it be nice to have ALL of your calendars in the contextual menu? Radar: 5593635

November 06, 2007

Leopard server, calDAV, and Mozilla Sunbird

For those wishing to use Mozilla Sunbird with the calDAV server that comes with Leopard, it's not setup in Sunbird in (quite) the same way as it is in iCal.

Let's say you have the calDAV Account URL working in ical (which will subscribe you to all of your calendars) and its URL is

 https://somedomain.com:8443/principals/users/username/ 

In Sunbird, you'll have to subscribe to the your calendars individually. Every calDAV user I've setup so far in Leopard server has been given a default calendar called simply "calendar", which you can subscribed to from Sunbird at

https://somedomain.com:8443/calendars/users/username/calendar/
Easy enough. However, if you add a new calendar in iCal, it will create a calendar named with a UUID. In order to figure out what that UUID is, it's easiest to just mount the calDAV in the finder (Command - K) at
https://somedomain.com:8443/calendars/users/username/
You'll see several directories in there: calendar, dropbox, oubox, notifications, etc. The one that looks like a UUID (for example, 037D3206-9C1D-4C6C-8E54-B3E3CAF90ABF) is the directory you'll want to subscribe to in Sunbird. In the example above, you would subscribe to
https://somedomain.com:8443/calendars/users/username/037D3206-9C1D-4C6C-8E54-B3E3CAF90ABF/

August 22, 2007

Carburetor Tuning

Sent in an email recently.

Carburetor tuning:

Even when you're doing it right, anything you change in one system will often screw something up in another unexpectedly.

Sometimes starting over from a reasonable baseline is better than hammering away at a not-quite-right but oh-so-close-to-working plan.

Keeping track of what you've done makes it easier to get where you want to be, even if what you've done has sucked grapes.

...funny. Writing music works the same way, too... and so does writing code.

August 14, 2007

iPhone Native Pong Application: the real "sweet" SDK

The native iPhone hack I put together for the C4[1] conference is in the process of a "wow, I hope somebody can read this code someday" cleanup. I will release the source in the coming days. Stay tuned for a download link.

In the meantime, here are a couple of teasers.

Screenshots:

scrnsht1.jpg


scrnsht2.jpg


And here is some code for playing a sound file from your native iPhone Application's bundle (with the class-dump'd headers you'll need, too). The iPhone, as was mentioned by everyone with a native hack at C4, has some really intelligent API. Hats off, ladies and gentlemen, to the iPhone team. They must be having a grand ol' time internally playing with this thing.



//
// PongAudio.h
//
// Created by Jonathan Saggau on 2007-08-12.
// Copyright (c) Jonathan Saggau All rights reserved.
//

#import <Foundation/Foundation.h>
@class AVItem;
@class AVController;
@class AVQueue;

@interface PongAudio : NSObject
{
AVItem *bounce;
AVItem *loser;
AVQueue *q;
AVController *controller;
}

-(void)playBounce;
-(void)playLoser;

-(void)stop;
@end

//
// PongAudio.m
//
// Created by Jonathan Saggau on 2007-08-12.
// Copyright (c) 2007 __MyCompanyName__. All rights reserved.
//

#import "PongAudio.h"
#import "AVItem.h"
#import "AVController.h"
#import "AVQueue.h"

//From Aaron hillegass
#define LogMethod() NSLog(@"-[%@ %s]", self, _cmd)

@interface PongAudio (PrivateAPI)
-(void)play:(AVItem *)item;
@end

@implementation PongAudio


- (id)init
{
self = [super init];
if (nil!= self)
{
NSError *err;
NSString *path = [[NSBundle mainBundle] pathForResource:@"PongBounce" ofType:@"wav" inDirectory:@"/"];
bounce = [[AVItem alloc] initWithPath:path error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n item = [[AVItem alloc] initWithPath:path error:&err];", err);
exit(1);
}

path = [[NSBundle mainBundle] pathForResource:@"PongLose" ofType:@"wav" inDirectory:@"/"];
loser = [[AVItem alloc] initWithPath:path error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n item = [[AVItem alloc] initWithPath:path error:&err];", err);
exit(1);
}

controller = [[AVController alloc] init];
[controller setDelegate:self];
q = [[AVQueue alloc] init];
[q appendItem:bounce error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n [q appendItem:item error:&err];", err);
exit(1);
}

[q appendItem:loser error:&err];
if (nil != err)
{
NSLog(@"err! = %@ \n [q appendItem:item error:&err];", err);
exit(1);
}
}
return self;
}

- (void)dealloc
{
[bounce release]; bounce = nil;
[loser release]; loser = nil;
[q release]; q = nil;
[controller release]; controller = nil;
[super dealloc];
}

-(void)playBounce
{
[self play:bounce];
}

-(void)playLoser
{
[self play:loser];
}

-(void)play:(AVItem *)item;
{
[controller setCurrentItem:item];

//play NOW
[controller setCurrentTime:(double)0.0];
//should probably check this eventually, too.
//- (BOOL)isCurrentItemReady;
NSError *err;
[controller play:&err];
if(nil != err)
{
NSLog(@"err! = %@ [controller play:&err];", err);
exit(1);
}
}

-(void)stop;
{
[controller pause];
}

- (void)queueItemWasAdded:(id)fp8
{
LogMethod();
}
@end


//AVController.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/


#import <Foundation/Foundation.h>
#import "CDStructures.h"

@interface AVController : NSObject
{
struct AVControllerPrivate *_priv;
}

+ (void)setEnableNetworkMode:(BOOL)fp8;
+ (id)avController;
+ (id)avControllerWithQueue:(id)fp8 error:(id *)fp12;
- (id)initWithError:(id *)fp8;
- (void)setAVItemClass:(Class)fp8;
- (id)init;
- (void)dealloc;
- (struct AVControllerPrivate *)privateStorage;
- (BOOL)isNewImageAvailableForTime:(const CDAnonymousStruct1 *)fp8 willNeverBeAvailable:(char *)fp12;
- (long)copyImageForTime:(struct __CVBuffer **)fp8 time:(const CDAnonymousStruct1 *)fp12;
- (void)itemPropertyDidChangeNotification:(id)fp8;
- (void)scheduleQueueSpaceCheck;
- (void)checkQueueSpace;
- (void)queueItemWasAdded:(id)fp8;
- (void)queueItemWasAddedNotification:(id)fp8;
- (void)queueItemWillBeRemovedNotification:(id)fp8;
- (void)cancelPrepareForPlayback;
- (void)setQueue:(id)fp8;
- (id)queue;
- (void)feederRangeWasInserted:(id)fp8;
- (void)feederRangeWasRemoved:(id)fp8;
- (void)feederInvalidatedWithCurrentItemMoved:(id)fp8;
- (void)setQueueFeeder:(id)fp8 withIndex:(int)fp12;
- (void)setQueueFeeder:(id)fp8;
- (id)queueFeeder;
- (BOOL)setRepeatMode:(int)fp8;
- (int)repeatMode;
- (BOOL)havePlayedCurrentItem;
- (id)currentItem;
- (id)currentItemPriv;
- (void)setCurrentItem:(id)fp8 preservingRate:(BOOL)fp12;
- (void)setCurrentItem:(id)fp8;
- (void)makeCurrentItemReady;
- (BOOL)isCurrentItemReady;
- (void)continueAfterRepeatGap;
- (void)cancelContinueAfterRepeatGap;
- (void)doCancelContinueAfterRepeatGap;
- (void)doScheduleContinueAfterRepeatGap:(id)fp8;
- (BOOL)beginRepeatGap;
- (void)itemFailedPlaying;
- (void)itemFinishedPlaying:(id)fp8;
- (void)itemCompletedDecode;
- (BOOL)play:(id *)fp8;
- (void)pause;
- (void)dequeueFirstItem;
- (unsigned int)indexOfCurrentQueueFeederItem;
- (BOOL)setIndexOfCurrentQueueFeederItem:(unsigned int)fp8 error:(id *)fp12;
- (id)addNextFeederItemToQueue;
- (BOOL)playNextItem:(id *)fp8;
- (float)rate;
- (BOOL)shouldBeginPlayingItem:(id)fp8 error:(id *)fp12;
- (BOOL)setRate:(float)fp8 error:(id *)fp12;
- (BOOL)resumePlayback:(double)fp8 error:(id *)fp16;
- (id)errorWithDescription:(id)fp8 code:(long)fp12;
- (void)makeError:(id *)fp8 withDescription:(id)fp12 code:(long)fp16;
- (BOOL)beginInterruption:(id *)fp8;
- (BOOL)activate:(id *)fp8;
- (void)endInterruptionWithStatus:(id)fp8;
- (float)volume;
- (void)setVolume:(float)fp8;
- (double)currentTime;
- (void)setCurrentTime:(double)fp8;
- (BOOL)muted;
- (void)setMuted:(BOOL)fp8;
- (void)setEQPreset:(int)fp8;
- (int)eqPreset;
- (struct OpaqueFigVisualContext *)visualContext;
- (void)setVisualContext:(struct OpaqueFigVisualContext *)fp8;
- (id)outputQTESFilePath;
- (void)setOutputQTESFilePath:(id)fp8;
- (id)lkLayer;
- (void)setLayer:(id)fp8;
- (id)attributeForKey:(id)fp8;
- (BOOL)setAttribute:(id)fp8 forKey:(id)fp12 error:(id *)fp16;
- (struct _LKImageQueue *)lkImageQueue;
- (struct _LKImageQueue *)lkEnsureQueueForWidth:(unsigned int)fp8 Height:(unsigned int)fp12;
- (double)lkServerTime;
- (BOOL)okToNotifyFromThisThread;
- (void)fmpTimeJumped;
- (void)fmpRateDidChange;
- (void)rateDidChangeToRate:(float)fp8;
- (void)repeatModeHasChanged:(int)fp8;
- (void)currentItemWillChangeToItem:(id)fp8 oldItemCurrentTime:(double)fp12;
- (void)currentItemHasChanged:(id)fp8;
- (void)itemHasFinishedPlayingNotification:(id)fp8;
- (void)resynchronizeTiming;
- (id)delegate;
- (void)setDelegate:(id)fp8;
- (BOOL)setItemAttribute:(id)fp8 value:(id)fp12 forKey:(id)fp16 error:(id *)fp20;
- (id)itemAttribute:(id)fp8 forKey:(id)fp12;
- (id)initWithQueue:(id)fp8 error:(id *)fp12;
- (BOOL)isValid;
- (id)initWithQueue:(id)fp8 fmpType:(unsigned long)fp12 error:(id *)fp16;
- (void)applyAttributesFromItem:(id)fp8;
- (void)fmpRelease:(id)fp8;
- (void)failPlayback:(id)fp8 reason:(long)fp12 notifyClient:(unsigned char)fp16;
- (void)prepareForPlaybackReply:(long)fp8;
- (int)instantiateFMPRef:(struct opaqueFigMoviePlaybackRef **)fp8 forItem:(id)fp12;
- (void)maybeDumpPerformanceDictionary:(struct opaqueFigMoviePlaybackRef *)fp8;
- (void)removeFMPRefListeners:(struct opaqueFigMoviePlaybackRef *)fp8;
- (void)shutdownFMPRef:(struct opaqueFigMoviePlaybackRef *)fp8;
- (void)updateTimeMarkerObservations;
- (void)scheduleUpdateTimeMarkerObservations;
- (void)registerTimeMarkerObserver:(id)fp8 forItem:(id)fp12 times:(id)fp16 context:(id)fp20;
- (void)removeObserver:(id)fp8 fromTMOArray:(id)fp12;
- (void)unregisterTimeMarkerObserver:(id)fp8 forItem:(id)fp12;

@end

//AVExternalAudio.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/

#import <Foundation/Foundation.h>
#import "CDStructures.h"

@interface AVExternalAudio : NSObject
{
struct AVExternalAudioPrivate *_priv;
}

+ (id)avExternalAudio:(id)fp8;
- (id)initWithDelegate:(id)fp8;
- (void)dealloc;
- (id)attributeForKey:(id)fp8;
- (BOOL)setAttribute:(id)fp8 forKey:(id)fp12 error:(id *)fp16;
- (void)makeError:(id *)fp8 withDescription:(id)fp12 code:(long)fp16;
- (void)postServerConnectionDiedNotification:(id)fp8;
- (void)fmpServerConnectionDied;
- (BOOL)okToNotifyFromThisThread;
- (BOOL)activate:(id *)fp8;
- (float)volume;
- (BOOL)isActive;
- (void)postUserVolumeChangedNotification:(id)fp8;
- (void)fmpUserVolumeDidChange;
- (void)fmpChangeConnectionActive:(BOOL)fp8;

@end

//AVItem.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/

#import <Foundation/Foundation.h>
#import "CDStructures.h"

@interface AVItem : NSObject
{
struct AVItemPrivate *_priv;
}

+ (id)avItem;
+ (id)avItemWithPath:(id)fp8 error:(id *)fp12;
- (id)initWithError:(id *)fp8;
- (id)init;
- (id)initWithPath:(id)fp8 error:(id *)fp12;
- (void)dealloc;
- (BOOL)setPath:(id)fp8 error:(id *)fp12;
- (int)_instantiateItem;
- (id)path;
- (double)duration;
- (struct CGSize)naturalSize;
- (float)volume;
- (void)setVolume:(float)fp8;
- (void)setEQPreset:(int)fp8;
- (int)eqPreset;
- (id)attributeForKey:(id)fp8;
- (BOOL)setAttribute:(id)fp8 forKey:(id)fp12 error:(id *)fp16;

@end

//AVQueue.h
/*
* Generated by class-dump 3.1.1.
*
* class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
*/

#import <Foundation/Foundation.h>

@class NSMutableArray, NSRecursiveLock;

@interface AVQueue : NSObject
{
NSRecursiveLock *_mutex;
NSMutableArray *_items;
void *_reserved;
unsigned int _flags;
}

+ (id)avQueue;
+ (id)queueWithArray:(id)fp8 error:(id *)fp12;
- (id)initWithError:(id *)fp8;
- (id)init;
- (id)initWithArray:(id)fp8 error:(id *)fp12;
- (void)dealloc;
- (int)_instantiateItem;
- (unsigned int)itemCount;
- (id)itemAtIndex:(unsigned int)fp8;
- (unsigned int)indexOfItem:(id)fp8;
- (id)itemAfterItem:(id)fp8;
- (BOOL)appendItemsFromArray:(id)fp8 error:(id *)fp12;
- (BOOL)appendItem:(id)fp8 error:(id *)fp12;
- (void)itemWasAdded:(id)fp8;
- (BOOL)insertItem:(id)fp8 atIndex:(unsigned int)fp12 error:(id *)fp16;
- (BOOL)insertItem:(id)fp8 afterItem:(id)fp12 error:(id *)fp16;
- (void)itemWillBeRemoved:(id)fp8;
- (BOOL)removeItem:(id)fp8;
- (BOOL)removeItemAtIndex:(unsigned int)fp8;
- (void)removeItemsInRange:(struct _NSRange)fp8;
- (void)removeAllItems;

@end

January 02, 2007

Google Analytics follow-up

I found an easy way to add Google Analytics to a movabletype blog as well. There is a small plugin that puts the javascript on your page available here. I love it when others write code so I don't have to.

December 31, 2006

Google Analytics + apache configuration mini - howto

I’ve been thinking of setting up google analytics on my site for a while now and have finally taken a few moments to get started. I'm curious to see where folks are coming from and am kind of tired of writing one-off scripts to grep my apache logs.

If, like me, you have a site made of many many html pages, this one’s for you. (DO please make a backup of your site and configuration files before you start).

In order for google to analyze a given web page, you need to add a short javascript to each. It looks something like this:


<script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "XX-XXXXXXX-X";
urchinTracker();
</script>

In order to easily add this text the each of my pages, I chose to use Apache's 'Server Side Includes . This is an easy way to get a chunk of text to be included in many files. Put the javascript that google gives you into a file called (say) includes.html and drop it in your apache document root. (I chose to put mine in my /data/ subdirectory as it works best for the structure of my site).

Each html document you want google to track will need to include that file’s text immediately before the </body> tag through the placement of a small SSI tag.

It will look something like this:


<!--#include virtual="/data/included.html" -->

To do such a massive replace, we’ll need a little Perl (yes, Juan PERL!) and bash.

perl -p -i -e 's/<body<!--#include virtual=\"\/data\/included.html\" -->\n <body/g' `find . |grep \.html`

This finds all html files, then performs a mass insertion before the </body> tag with the SSI include. Apache will take the include and replace it with the contents of the file.

If SSI isn't yet configured on your apache box, there is a simple set of tricks that will make life MUCH easier. Assuming that you have compiled mod_include (most apache installations have this), you simply need to add

 Options +Includes 
to your site configuration. I placed it in my virtual hosts file along with my other Options directives. The real juice is here: place
 XBitHack on 
in your apache configuration as well. This will make apache parse any file (looking for SSI includes) with the execute (remember your unix permissions?) bit enabled. You will then set the execute bit on all of your html pages like so:

cd your/apache/htdocs
chmod +x `find . |grep \.html`

That should do it. We have written a small includes.html file to contain google's javascript, done a massive search and insert on all of your html files, setup apache to use SSI with XBitHack, and changed the execute bit of the html files so apache will parse them. Now, when you look at the source of your page as served from apache, it should include the javascript from the includes.html file within each page.

Note: XBitHack will not work on windows for obvious reasons. There is no executable bit on windows. See the apache tutorial referenced above for other methods to enable SSI if you're on windows.

November 04, 2006

Portfile as howto

I had an interesting Apache gdb session recently wherein I discovered that the Apache Portable Runtime (APR) package on one of my clients' shiny new Debian Linux boxes was.... well ... completely borked, causing a segfault for every apache child process. It's my fault for using the testing distribution, but (hey!) the box isn't all that important and it's an excuse to see what's new in Debian-land. However, as I actually do want to serve a web page from this machine, I figured it best not to further trust the package manager to find me non-broken software. I decided to download APR and friends + apache and compile it myself. The last time I did anything like this, I used MacPorts (then darwinports) to compile and install apache on my mac at home, a very "batteries included" affair as MacPorts selects a good set of default options for me. After some attempts to grok the various configuration options for Apache, I decided that I really just wanted the same options I have installed on the MacPorts+OSX box at home. Enter MacPorts happily human readable portfiles.

In the MacPorts portfile for apache we find the configure.args line.

configure.args --with-apr=${prefix}/bin/apr-1-config --with-apr-util=${prefix}/bin/apu-1-config --libdir=${prefix}/lib --with-expat=${prefix} --with-pcre=${prefix} --mandir=${prefix}/share/man --enable-mods-shared=all --enable-ssl --with-ssl=${prefix} --enable-deflate --enable-proxy --enable-proxy-connect --enable-proxy-http --enable-proxy-ftp


All that was necessary was to make the prefix variable point to where I wanted to install apache, drop in the args above for the ./configure script, and everything compiled without a hitch. I did already have the various other dependencies (ssl, etc.) installed on my system from the aborted attempts at installing the Apache2 package, so there were no surprises from missing libraries. Editing the (happily very clear and atomic) default apache configuration files a little brought me to the necessary service configuration very quickly. Now I have virtual hosting, mod_rewrite (to send all requests on port 80 to the https port), and some other nice features running on the very latest stable Apache2.

September 23, 2006

Reality

"Reality isn't virtual enough."

Seriously. I just heard this at a game design panel.

September 06, 2006

Nike data is smooooooooooth like budda

Looks like the Nike+ data gets... um ... just a little smoothing / averaging. Then again, I still need to test the data importer... You're looking at distance reported per 10 second interval. Raw data babeeee.


run.jpg

July 04, 2006

Parallels' problem isn't case-sensitivity.

With the help of Drew Thaler's instructions on how to grab disk access by running an app in gdb, it looks like the bug in Parallels Workstation was even weirder than the case-sensitivity problem. Parallels uses QT, a cross-platform GUI toolkit to handle user interface presentation, rather than the much-more-common-on-OSX-but-not-cross-platform toolkit called cocoa. Unfortunately, it follows my library search path to get to QT. My installation of QT is broken. (I've known this for a while and haven't had the time to rebuild it from source). Why doesn't Parallels force inclusion of QT from the app bundle? I don't know. Bug report filed.

See the extended entry for Drew's instructions (thanks Drew!)

Continue reading "Parallels' problem isn't case-sensitivity." »

June 07, 2006

Bob

I don't know how he finds the time, but the python / mac community owes Bob several rounds of applause. Let me be the first to stand (soon as I get this here foot outta mah mouth). I'm also wowed at how he managed to find my blog and comment before I got round to sending a question to the mailing lists. And ... well you know... he kicked my butt and I deserved it.

April 30, 2006

NSZombie update

I wonder if I'm using cocoa bindings incorrectly... or maybe there's a bug. Usually when I start to think "maybe there's a bug in somebody else's code," I look for ways in which I'm using that code in weird (wrong) ways. I figure that the pyobjc people are about 7 orders of magnitude better at this than I am.

Continue reading "NSZombie update" »

April 10, 2006

NSZombie eating my brain!

I've been learning cocoa bindings (I like this voodoo) and have hit a brick wall. I've been asking pythonmac-sig if they've any ideas and they pointed me in the direction of NSZombieEnabled, but i haven't been able to get very far.

I have an NSArray controller with a python object subclassing NSObject. I've put a + button on the gui to add the python object like this:::

def addTriggerModel_(self, sender): # BUTTON
self.setCanAddTriggerModel_(False)
newTriggerModel = scNSDrumTriggerModel.alloc().init()
newTriggerModel.resetWithNodeBusAndInput(node = self.nextNode, bus = self.nextBus, \
input = self.nextInput, sendInfo = self.sendingTriggersToClient)
self.nextNode += 100
self.nextBus += 10
self.nextInput += 1
NSLog("Adding newTriggerModel")
self.triggerArrayController.addObject_(newTriggerModel)
NSLog("Added newTriggerModel")
self.setCanAddTriggerModel_(True)


This works just peachy the first time I do it, but fails on a second addition of the new scNSDrumTriggerModel object. At first I was getting Signal 5/6 errors, then I did:

from PyObjCTools import Signals
Signals.dumpStackOnFatalSignal()
from PyObjCTools import Debugging
Debugging.installVerboseExceptionHandler()

This told me:

NSGenericException - *** Selector 'retain' sent to dealloced instance 0x1147cb0 of class OC_PythonArray

So I turned on NSZombieEnabled and tried running in gdb with a breakpoint on -[_NSZombie retain], but I don't really know what I'm looking for. I'm looking at a lot of hex memory addresses with very little human-readable stuff in there.

I tried setting a breakpoint at (+[OC_PythonArray newWithPythonObject:]) to see perhaps what proxy objects are being instantiated, but I'm clearly in over my depth. The variable corresponding to the pythonObject in OC_PythonArray newWithPythonObject: up there ^ has address 0x0 (Is that a null pointer?) in gdb each time I hit that breakpoint.

When I run malloc_history on the memory address that the NSZombie has, I get this:

Continue reading "NSZombie eating my brain!" »

January 27, 2006

Comments

Anyone who has tried to comment on my 'blog, please try again.  My server was blocking outgoing smtp and I wasn't getting the notifications to approve comments.

oops.

January 21, 2006

Sometimes Being a Nerd is NOT_FUN_(aka.the_suck())

So I've been going 'round and 'round (including sending a DVD-R via UPS RED which - I'm told - was corrupted upon arrival) to get a binary database save file to the company that handles the database maintenance at one of my clients so that they can perform an upgrade to the system.

I finally bit the bullet... and did everything I could think of to make sure that the file got there intact:

Continue reading "Sometimes Being a Nerd is NOT_FUN_(aka.the_suck())" »

January 18, 2006

python performance suck

Stupid nerd trick:  (File under... DUH!)

When working on real-time audio control, it's probably not a bad idea to try to get the best possible time-slicing quantum possible.  What with latency to think about and generally wanting everything to just-happen-right-the-heck-now(TM)...

Continue reading "python performance suck" »