adding CPU files from CPU Usage app.
[cpuHistory.git] / CPUUsageMonitor.m
blobda6afabbc85199dfc9a725c379c3cb1fda4e9c91
1 //
2 //  CPUUsageMonitor.m
3 //  CPU Usage
4 //
5 //  Created by Peter Hosey on 2006-06-21.
6 //  Copyright 2006 Peter Hosey. All rights reserved.
7 //
9 #import "CPUUsageMonitor.h"
11 #import "CPUUsageView.h"
12 #import "NSString+Percentage.h"
14 #import "BZGridEnumerator.h"
16 #include <sys/types.h>
17 //sqrtf, ceilf
18 #include <math.h>
19 //sysctl and its parameters
20 #include <sys/sysctl.h>
21 //errno, strerror
22 #include <sys/errno.h>
23 #include <string.h>
25 #define DOCK_ICON_SIZE 128.0f
27 @interface CPUUsageMonitor (PRIVATE)
29 - (void)layOutCellsInWindow:(NSWindow *)thisWindow;
31 @end
33 @implementation CPUUsageMonitor
35 + (void)initialize {
36         [self exposeBinding:@"backgroundColor"];
37         [self exposeBinding:@"cellWidth"];
38         [self exposeBinding:@"cellHeight"];
39         [self exposeBinding:@"shouldDrawToDockIcon"];
40         [self exposeBinding:@"shouldDrawToWindow"];
43 - init {
44         if((self = [super init])) {
45                 NSNumber *thirtyTwo = [NSNumber numberWithFloat:32.0f];
46                 NSDictionary *defaultPrefs = [NSDictionary dictionaryWithObjectsAndKeys:
47                         [NSArchiver archivedDataWithRootObject:[NSColor whiteColor]], @"Floater text color",
48                         [NSNumber numberWithFloat:0.0f], @"Floater text opacity",
49                         [NSArchiver archivedDataWithRootObject:[NSColor whiteColor]], @"Dock icon text color",
50                         [NSNumber numberWithFloat:1.0f], @"Dock icon text opacity",
51                         [NSArchiver archivedDataWithRootObject:[NSColor redColor]], @"Background color",
52                         thirtyTwo, @"Cell width",
53                         thirtyTwo, @"Cell height",
54                         [NSNumber numberWithBool:YES], @"Draw CPU usage to window",
55                         [NSNumber numberWithInt:CPUUsageOrientationVertical], @"Window orientation",
56                         [NSNumber numberWithBool:NO], @"Draw CPU usage to Dock icon",
57                         nil];
59                 [[NSUserDefaults standardUserDefaults] registerDefaults:defaultPrefs];
60         }
61         return self;
64 #pragma mark NSApplication delegate conformance
66 - (void)applicationDidFinishLaunching:(NSNotification *)notification {
67         //Set up us the properties of new cells.
68         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
69         NSData *data = [defaults dataForKey:@"Background color"];
70         NSColor *color = data ? [NSUnarchiver unarchiveObjectWithData:data] : [NSColor redColor];
71         [self setBackgroundColor:color];
73         [self setCellWidth:[defaults floatForKey:@"Cell width"]];
74         [self setCellHeight:[defaults floatForKey:@"Cell height"]];
76         //Sign up for notifications of pref changes.
77         NSUserDefaultsController *udc = [NSUserDefaultsController sharedUserDefaultsController];
78         NSDictionary *colorBindingOptions = [NSDictionary dictionaryWithObject:@"NSUnarchiveFromData" forKey:NSValueTransformerNameBindingOption];
79         [self bind:@"backgroundColor"      toObject:udc withKeyPath:@"values.Background color"            options:colorBindingOptions];
80         [self bind:@"cellWidth"            toObject:udc withKeyPath:@"values.Cell width"                  options:nil];
81         [self bind:@"cellHeight"           toObject:udc withKeyPath:@"values.Cell height"                 options:nil];
82         [self bind:@"shouldDrawToDockIcon" toObject:udc withKeyPath:@"values.Draw CPU usage to Dock icon" options:nil];
83         [self bind:@"shouldDrawToWindow"   toObject:udc withKeyPath:@"values.Draw CPU usage to window"    options:nil];
85         //Fetch the orientation from prefs.
86         orientation = [defaults integerForKey:@"Window orientation"];
88         //Set up the orientation buttons.
89         int buttonStates[2] = { NSOffState, NSOnState };
90         [horizontalButton  setState:buttonStates[(orientation == CPUUsageOrientationHorizontal )]];
91         [verticalButton    setState:buttonStates[(orientation == CPUUsageOrientationVertical   )]];
92         [rectangularButton setState:buttonStates[(orientation == CPUUsageOrientationRectangular)]];
94         //We could get the number of processors the same way that we get the CPU usage info, but that allocates memory.
95         enum { miblen = 2U };
96         int mib[miblen] = { CTL_HW, HW_NCPU };
97         size_t sizeOfNumCPUs = sizeof(numCPUs);
98         int status = sysctl(mib, miblen,
99                    &numCPUs, &sizeOfNumCPUs,
100                    /*newp*/ NULL, /*newlen*/ 0U);
101         if(status != 0)
102                 numCPUs = 1; //XXX Should probably error out instead…
104         //Set up our display destinations.
105         //First, the Dock icon window. We'll copy the content view into an image, which we set as the application icon.
106         NSRect dockIconRect = { NSZeroPoint, { DOCK_ICON_SIZE, DOCK_ICON_SIZE } };
107         dockIconWindow = [[NSPanel alloc] initWithContentRect:dockIconRect
108                                                                                                 styleMask:NSBorderlessWindowMask
109                                                                                                   backing:NSBackingStoreBuffered
110                                                                                                         defer:YES];
111         [dockIconWindow setTitle:@"CPU Usage hidden Dock-icon window"];
112         [dockIconWindow setOpaque:NO];
113         [dockIconWindow setAcceptsMouseMovedEvents:NO];
114         [dockIconWindow setMovableByWindowBackground:YES];
115         [dockIconWindow setHasShadow:NO];
116         [dockIconWindow setIgnoresMouseEvents:YES];
117         [dockIconWindow setReleasedWhenClosed:YES];
118         [dockIconWindow setOneShot:NO];
119         [dockIconWindow setBackgroundColor:[NSColor clearColor]];
120         //We need to order the window in so that the views can draw. But we only want the user to see the Dock icon, not the window. So we set the alpha to 0.
121         [dockIconWindow setAlphaValue:0.0f];
122         [dockIconWindow orderBack:nil];
124         dockIconUsageViewsContainingView = [dockIconWindow contentView];
126         dockIconUsageViews = [[NSMutableArray alloc] initWithCapacity:numCPUs];
127         [self layOutCellsInWindow:dockIconWindow];
129         //Second, create the floater.
130         float numCPUsFloat = numCPUs;
131         float numViewsX, numViewsY;
132         if(orientation == CPUUsageOrientationHorizontal) {
133                 numViewsX = numCPUsFloat;
134                 numViewsY = 1.0f;
135         } else if(orientation == CPUUsageOrientationVertical) {
136                 numViewsX = 1.0f;
137                 numViewsY = numCPUsFloat;
138         } else {
139                 numViewsX = ceilf(sqrtf(numCPUsFloat));
140                 numViewsY = ceilf(numCPUsFloat / numViewsX);
141         }
142         float numCellsMissing = ((numViewsX * numViewsY) - numCPUsFloat);
144         //Here, these enumerators are used only to compute the frame of the window. No actual enumeration is done.
145         BZGridEnumerator *floaterOddCellsEnum = nil;
146         BZGridEnumerator *floaterCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:(NSSize){ cellWidth, cellHeight }
147                                                                                                                                         numberOfColumns:numViewsX
148                                                                                                                                            numberOfRows:numViewsY];
149         NSRect frame = [floaterCellsEnum overallRect];
150         if(numCellsMissing) {
151                 float numOddCells = numCellsMissing ? numViewsX - numCellsMissing : 0.0f;
152                 /*oddCellsWidth is the fraction of the total frame width used by odd cells.
153                  *total = X*Y
154                  *numEmptySpaces = total - numCPUs
155                  *numOddCPUs = X - numEmptySpaces
156                  *oddCellsWidth = numOddCPUs / X
157                  */
158                 float oddCellsWidth = numOddCells / numViewsX;
159                 float oddCellWidth = oddCellsWidth / numOddCells;
160                         
161                 floaterOddCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:(NSSize){ frame.size.width * oddCellWidth, cellHeight }
162                                                                                                                                   offset:(NSPoint){ 0.0f, cellHeight * numViewsY }
163                                                                                                                  numberOfColumns:numOddCells
164                                                                                                                         numberOfRows:1U];
165                 frame.size.height += [floaterOddCellsEnum overallRect].size.height;
166         }
168         window = [[NSWindow alloc] initWithContentRect:frame
169                                                                                  styleMask:NSBorderlessWindowMask
170                                                                                    backing:NSBackingStoreBuffered
171                                                                                          defer:YES];
172         [window setTitle:@"CPU Usage"];
173         [window setOpaque:NO];
174         [window setAcceptsMouseMovedEvents:NO];
175         [window setMovableByWindowBackground:YES];
176         [window setLevel:kCGDesktopWindowLevel + 2];
177         [window setHasShadow:NO];
178         [window setIgnoresMouseEvents:YES];
179         [window setReleasedWhenClosed:YES];
180         if(orientation != CPUUsageOrientationRectangular) {
181                 BOOL isHorizontal = (orientation == CPUUsageOrientationHorizontal);
182                 [window setResizeIncrements:(NSSize){ numCPUs * isHorizontal, numCPUs * !isHorizontal }];
183         }
184         [window setBackgroundColor:[NSColor clearColor]];
186         usageViews = [[NSMutableArray alloc] initWithCapacity:numCPUs];
187         [self layOutCellsInWindow:window];
189         [window setFrameAutosaveName:@"CPU usage window"];
190         if(![window setFrameUsingName:@"CPU usage window"])
191                 [window center];
192         else {
193                 frame.origin = [window frame].origin;
194                 [window setFrame:frame display:NO];
195         }
196         [preferencesPanel center];
198         CPUUsageLock = [[NSLock alloc] init];
199         CPUUsage = NSZoneMalloc([self zone], numCPUs * sizeof(float));
201         updateTimer = [[NSTimer scheduledTimerWithTimeInterval:0.5
202                                                                                                         target:self
203                                                                                                   selector:@selector(updateCPUUsageButNotViews:)
204                                                                                                   userInfo:nil
205                                                                                                    repeats:YES] retain];
206         [updateTimer fire];
208         //Launch the threaded timers that sweep our CPU usage array looking for views.
209         for(unsigned i = 0U; i < numCPUs; ++i) {
210                 [NSThread detachNewThreadSelector:@selector(threadedLaunchTimer:)
211                                                                  toTarget:self
212                                                            withObject:[NSNumber numberWithUnsignedInt:i]];
213         }
215         if(shouldDrawToWindow)
216                 [window orderFront:nil];
219 - (void)applicationWillTerminate:(NSNotification *)notification {
220         //First, fade out the window.
221         NSDictionary *animDict = [NSDictionary dictionaryWithObjectsAndKeys:
222                 window, NSViewAnimationTargetKey,
223                 NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
224                 nil];
225         NSViewAnimation *anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animDict]];
226         [anim setDuration:0.5];
227         [anim setAnimationBlockingMode:NSAnimationNonblockingThreaded];
229         //Tell the threads to stop, then wait for them to comply.
230 //      threadsShouldExit = YES;
231         threadsRemainingToDie = [usageViews count];
232         [anim startAnimation];
234         unsigned sleepCount = 100U; //Wait no more than this many seconds.
235         while(threadsRemainingToDie && sleepCount--)
236                 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
238         //Wait for the animation to run out.
239         while([anim isAnimating])
240                 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
242         [updateTimer invalidate];
243         [updateTimer release];
245         [usageViews release];
247         [backgroundColor release];
249         [window close];
251         NSZoneFree([self zone], CPUUsage);
253         [NSApp setApplicationIconImage:[NSImage imageNamed:@"CPUUsageIcon"]];
256 - (void)applicationDidBecomeActive:(NSNotification *)notification {
257         NSEnumerator *usageViewsEnum = [usageViews objectEnumerator];
258         CPUUsageView *usageView;
259         while((usageView = [usageViewsEnum nextObject])) {
260                 [usageView setDrawsFrame:YES];
261                 [usageView setNeedsDisplay:YES];
262         }
264         [window setIgnoresMouseEvents:NO];
266 - (void)applicationWillResignActive:(NSNotification *)notification {
267         NSEnumerator *usageViewsEnum = [usageViews objectEnumerator];
268         CPUUsageView *usageView;
269         while((usageView = [usageViewsEnum nextObject])) {
270                 [usageView setDrawsFrame:NO];
271                 [usageView setNeedsDisplay:YES];
272         }
273         
274         [window setIgnoresMouseEvents:YES];
277 #pragma mark Stuff
279 - (void)drawToDockIcon {
280         NSRect dockIconRect = { NSZeroPoint, { DOCK_ICON_SIZE, DOCK_ICON_SIZE } };
282         [dockIconUsageViewsContainingView lockFocus];
283         NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:dockIconRect];
284         [dockIconUsageViewsContainingView unlockFocus];
286         NSImage *image = [[NSImage alloc] initWithSize:dockIconRect.size];
287         [image addRepresentation:rep];
288         [rep release];
290         //Add a border.
291         [image lockFocus];
292         [backgroundColor set];
293         NSFrameRectWithWidth([dockIconUsageViewsContainingView frame], 2.0f);
294         [image unlockFocus];
296         [NSApp setApplicationIconImage:image];
297         [image release];
300 #pragma mark Timer
302 - (void)threadedUpdateCPUUsageView:(NSTimer *)timer {
303         unsigned i = [[timer userInfo] unsignedIntValue];
305         CPUUsageView *view = [usageViews objectAtIndex:i];
306         CPUUsageView *viewInDockIcon = [dockIconUsageViews objectAtIndex:i];
308         [CPUUsageLock lock];
309         [view setCPUUsage:CPUUsage[i]];
310         [viewInDockIcon setCPUUsage:CPUUsage[i]];
311         [CPUUsageLock unlock];
313         if(shouldDrawToWindow)
314                 [view display];
315         if(shouldDrawToDockIcon)
316                 [viewInDockIcon display];
318         if(threadsRemainingToDie) {
319                 [timer invalidate];
321                 [deathLock lock];
322                 --threadsRemainingToDie;
323                 [deathLock unlock];
324         }
326 - (void)threadedLaunchTimer:(NSNumber *)CPUIndexNum {
327         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
329         NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
331         NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
332                                                                                          target:self
333                                                                                    selector:@selector(threadedUpdateCPUUsageView:)
334                                                                                    userInfo:CPUIndexNum //userInfo
335                                                                                         repeats:YES];
336         [timer setFireDate:[NSDate dateWithTimeIntervalSinceNow:0.25]];
338         [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
339         [runLoop run];
341         [pool release];
344 //Main thread.
345 - (void)updateCPUUsageButNotViews:(NSTimer *)timer {
346         if(shouldDrawToDockIcon)
347                 [self drawToDockIcon];
349         natural_t numProcessors_nobodyCares = 0U;
350         processor_info_array_t processorInfo;
351         mach_msg_type_number_t numProcessorInfo;
353         kern_return_t err = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numProcessors_nobodyCares, &processorInfo, &numProcessorInfo);
354         if(err == KERN_SUCCESS) {
355                 vm_map_t target_task = mach_task_self();
357                 [CPUUsageLock lock];
359                 for(unsigned i = 0U; i < numCPUs; ++i) {
360                         //We only want the last $REFRESH_TIME seconds' worth of data, not all time.
361                         float inUse, total;
362                         if(lastProcessorInfo) {
363                                 inUse = (
364                                   (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER]   - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER])
365                                 + (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM])
366                                 + (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE]   - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE])
367                                 );
368                                 total = inUse + (processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE] - lastProcessorInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE]);
369                         } else {
370                                 inUse = processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_USER] + processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_SYSTEM] + processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_NICE];
371                                 total = inUse + processorInfo[(CPU_STATE_MAX * i) + CPU_STATE_IDLE];
372                         }
374                         CPUUsage[i] = inUse / total;
375                 }
377                 [CPUUsageLock unlock];
379                 if(lastProcessorInfo) {
380                         size_t lastProcessorInfoSize = sizeof(integer_t) * numLastProcessorInfo;
381                         vm_deallocate(target_task, (vm_address_t)lastProcessorInfo, lastProcessorInfoSize);
382                 }
384                 lastProcessorInfo = processorInfo;
385                 numLastProcessorInfo = numProcessorInfo;
386         } else {
387                 NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
388                         NSLocalizedString(@"Could not get CPU load information", /*comment*/ nil), NSLocalizedDescriptionKey,
389                         NSLocalizedString(@"Do you have a CPU in your computer? (Just kidding. I have no idea how to get a useful string from a kernel error code. --The Author)", /*comment*/ nil), NSLocalizedRecoverySuggestionErrorKey,
390                         nil];
391                 [NSApp presentError:[NSError errorWithDomain:NSMachErrorDomain code:err userInfo:dict]];
392                 [NSApp terminate:nil];
393         }
396 #pragma mark Accessors
398 - (NSColor *)backgroundColor {
399         return backgroundColor;
401 - (void)setBackgroundColor:(NSColor *)newBackgroundColor {
402         if(backgroundColor != newBackgroundColor) {
403                 [backgroundColor release];
404                 backgroundColor = [newBackgroundColor retain];
405         }
408 - (float)cellWidth {
409         return cellWidth;
411 - (void)setCellWidth:(float)newCellWidth {
412         cellWidth = newCellWidth;
414         if(window) {
415                 register float numCPUsFloat = numCPUs;
416                 float numViewsX, numViewsY;
417                 if(orientation == CPUUsageOrientationHorizontal) {
418                         numViewsX = numCPUs;
419                         numViewsY = 1.0f;
420                 } else if(orientation == CPUUsageOrientationVertical) {
421                         numViewsX = 1.0f;
422                         numViewsY = numCPUs;
423                 } else {
424                         numViewsX = ceilf(sqrtf(numCPUsFloat));
425                         numViewsY = ceilf(numCPUsFloat / numViewsX);
426                 }
428                 NSRect oldFrame = [window frame];
429                 NSRect newFrame = oldFrame;
430                 newFrame.size.width  = cellWidth  * numViewsX;
431                 newFrame.size.height = cellHeight * numViewsY;
432 //              [window setFrame:newFrame display:YES animate:YES]; //For some reason, this blocks dragging the window around. Lucky me â€” the NSViewAnimation is prettier.
433                 NSDictionary *animDict = [NSDictionary dictionaryWithObjectsAndKeys:
434                         window, NSViewAnimationTargetKey,
435                         [NSValue valueWithRect:oldFrame], NSViewAnimationStartFrameKey,
436                         [NSValue valueWithRect:newFrame], NSViewAnimationEndFrameKey,
437                         nil];
438                 NSViewAnimation *anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animDict]];
439                 [anim startAnimation];
440                 [anim release];
441         }
444 - (float)cellHeight {
445         return cellHeight;
447 - (void)setCellHeight:(float)newCellHeight {
448         cellHeight = newCellHeight;
450         if(window) {
451                 register float numCPUsFloat = numCPUs;
452                 float numViewsX, numViewsY;
453                 if(orientation == CPUUsageOrientationHorizontal) {
454                         numViewsX = numCPUs;
455                         numViewsY = 1.0f;
456                 } else if(orientation == CPUUsageOrientationVertical) {
457                         numViewsX = 1.0f;
458                         numViewsY = numCPUs;
459                 } else {
460                         numViewsX = ceilf(sqrtf(numCPUsFloat));
461                         numViewsY = ceilf(numCPUsFloat / numViewsX);
462                 }
464                 NSRect oldFrame = [window frame];
465                 NSRect newFrame = oldFrame;
466                 newFrame.size.width  = cellWidth  * numViewsX;
467                 newFrame.size.height = cellHeight * numViewsY;
468 //              [window setFrame:frame display:YES animate:YES]; //For some reason, this blocks dragging the window around. Lucky me â€” the NSViewAnimation is prettier.
469                 NSDictionary *animDict = [NSDictionary dictionaryWithObjectsAndKeys:
470                         window, NSViewAnimationTargetKey,
471                         [NSValue valueWithRect:oldFrame], NSViewAnimationStartFrameKey,
472                         [NSValue valueWithRect:newFrame], NSViewAnimationEndFrameKey,
473                         nil];
474                 NSViewAnimation *anim = [[NSViewAnimation alloc] initWithViewAnimations:[NSArray arrayWithObject:animDict]];
475                 [anim startAnimation];
476                 [anim release];
477         }
480 - (BOOL)shouldDrawToDockIcon {
481         return shouldDrawToDockIcon;
483 - (void)setShouldDrawToDockIcon:(BOOL)flag {
484         shouldDrawToDockIcon = flag;
486         if(!shouldDrawToDockIcon)
487                 [NSApp setApplicationIconImage:[NSImage imageNamed:@"CPUUsageIcon"]];
490 - (BOOL)shouldDrawToWindow {
491         return shouldDrawToWindow;
493 - (void)setShouldDrawToWindow:(BOOL)flag {
494         shouldDrawToWindow = flag;
496         if(shouldDrawToWindow)
497                 [window orderFront:nil];
498         else
499                 [window orderOut:nil];
502 #pragma mark Actions
504 - (IBAction)setOrientationToVertical:sender {
505         orientation = CPUUsageOrientationVertical;
506         [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:[NSNumber numberWithInt:orientation] forKey:@"Window orientation"];
508         [self layOutCellsInWindow:window];
510         [verticalButton    setState:NSOnState];
511         [horizontalButton  setState:NSOffState];
512         [rectangularButton setState:NSOffState];
514 - (IBAction)setOrientationToHorizontal:sender {
515         orientation = CPUUsageOrientationHorizontal;
516         [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:[NSNumber numberWithInt:orientation] forKey:@"Window orientation"];
518         [self layOutCellsInWindow:window];
520         [verticalButton    setState:NSOffState];
521         [horizontalButton  setState:NSOnState];
522         [rectangularButton setState:NSOffState];
524 - (IBAction)setOrientationToRectangular:sender {
525         orientation = CPUUsageOrientationRectangular;
526         [[[NSUserDefaultsController sharedUserDefaultsController] values] setValue:[NSNumber numberWithInt:orientation] forKey:@"Window orientation"];
528         [self layOutCellsInWindow:window];
530         [verticalButton    setState:NSOffState];
531         [horizontalButton  setState:NSOffState];
532         [rectangularButton setState:NSOnState];
535 @end
537 @implementation CPUUsageMonitor (PRIVATE)
539 - (void)layOutCellsInWindow:(NSWindow *)thisWindow {
540         [thisWindow disableScreenUpdatesUntilFlush];
542         //Each cell in the Dock icon is square, so this rectangle may not be square. Consider a dual-proc machine.
543         //And of course, the floater is non-square in most configurations.
544         /*numCPUs = 8
545          *+--+--+
546          *|1 | 2|
547          *+-+-+-+
548          *|3|4|5|
549          *+-+-+-+
550          *|6|7|8|
551          *+-+-+-+
552          */
553         register float numCPUsFloat = numCPUs;
554         float numViewsX, numViewsY;
555         if((thisWindow == window) && (orientation == CPUUsageOrientationHorizontal)) {
556                 numViewsX = numCPUsFloat;
557                 numViewsY = 1.0f;
558         } else if((thisWindow == window) && (orientation == CPUUsageOrientationVertical)) {
559                 numViewsX = 1.0f;
560                 numViewsY = numCPUsFloat;
561         } else {
562                 numViewsX = ceilf(sqrtf(numCPUsFloat));
563                 numViewsY = ceilf(numCPUsFloat / numViewsX);
564         }
565         BOOL drawingToDock = (thisWindow == dockIconWindow);
567         NSSize cellSize;
568         if(drawingToDock) {
569                 cellSize.width  = DOCK_ICON_SIZE / numViewsX;
570                 cellSize.height = DOCK_ICON_SIZE / numViewsY;
571         } else {
572                 cellSize.width  = cellWidth;
573                 cellSize.height = cellHeight;
574         }
576         //Handle odd cells (1 and 2 above).
577         //The number of cells missing from the row with the odd cells (in the example above, this = 1).
578         float numCellsMissing = ((numViewsX * numViewsY) - numCPUsFloat);
579         float numOddCellsFloat = numCellsMissing ? numViewsX - numCellsMissing : 0.0f;
580         unsigned numOddCells = numOddCellsFloat;
581         unsigned numNotOddCells = numCPUs - numOddCells;
582         
583         BZGridEnumerator *floaterOddCellsEnum = nil;
584         BZGridEnumerator *floaterCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:cellSize
585                                                                                                                                         numberOfColumns:numViewsX
586                                                                                                                                            numberOfRows:numViewsY];
587         NSRect frame = [floaterCellsEnum overallRect];
588         if(numCellsMissing) {
589                 /*oddCellsWidth is the fraction of the total frame width used by odd cells.
590                  *total = X*Y
591                  *numEmptySpaces = total - numCPUs
592                  *numOddCPUs = X - numEmptySpaces
593                  *oddCellsWidth = numOddCPUs / X
594                  */
595                 float oddCellsWidth = numOddCellsFloat / numViewsX;
596                 float oddCellWidth = oddCellsWidth / numOddCellsFloat;
597                 floaterOddCellsEnum = [[BZGridEnumerator alloc] initWithCellSize:(NSSize){ frame.size.width * oddCellWidth, cellSize.height }
598                                                                                                                                   offset:(NSPoint){ 0.0f, cellSize.height * numViewsY }
599                                                                                                                  numberOfColumns:numOddCells
600                                                                                                                         numberOfRows:1U];
601                 frame.size.height += [floaterOddCellsEnum overallRect].size.height;
602         }
603         frame.origin = [thisWindow frame].origin;
604         [thisWindow setFrame:frame display:NO];
605         float offsetY = drawingToDock ? ((DOCK_ICON_SIZE - frame.size.height) * 0.5f) : 0.0f;
607         //Sign up for notifications of pref changes.
608         NSUserDefaultsController *udc = [NSUserDefaultsController sharedUserDefaultsController];
609         NSDictionary *colorBindingOptions = [NSDictionary dictionaryWithObject:@"NSUnarchiveFromData" forKey:NSValueTransformerNameBindingOption];
611         NSMutableArray *usageViewsArray = (thisWindow == window) ? usageViews : dockIconUsageViews;
612         unsigned usageViewsArrayCount = [usageViewsArray count];
614         //We need this information to set drawsFrame of every view to the correct value.
615         //We don't need to retrieve it every time, so we get it once here and cache it in a variable.
616         //The Dock icon window never has per-cell frames.
617         BOOL shouldDrawFrame = [NSApp isActive] && (thisWindow == window);
619         //Add CPU usage views.
620         unsigned numCols = numViewsX, numRows = numViewsY;
621         NSView *contentView = [thisWindow contentView];
622         unsigned CPUNum = 0U;
623         if(numCellsMissing) {
624                 while(numOddCells--) {
625                         NSRect oddCellRect = [floaterOddCellsEnum nextRect];
626                         oddCellRect.origin.y += offsetY;
628                         CPUUsageView *usageView;
629                         if(CPUNum >= usageViewsArrayCount) {
630                                 usageView = [[[CPUUsageView alloc] initWithFrame:oddCellRect] autorelease];
631                                 [contentView addSubview:usageView];
632                                 [usageViewsArray addObject:usageView];
633                                 ++usageViewsArrayCount;
634                         } else {
635                                 usageView = [usageViewsArray objectAtIndex:CPUNum];
636                                 [usageView setFrame:oddCellRect];
637                         }
639                         [usageView setDrawsFrame:shouldDrawFrame];
640                         [usageView setCPUUsage:8.88f]; //XXX TEMP - Should just not update the Dock icon until after all samples anyway
641                         if(numCPUs > 1)
642                                 [usageView setCPUNumber:++CPUNum];
644                         [usageView bind:@"textColor"
645                                    toObject:udc
646                                 withKeyPath:@"values.Dock icon text color"
647                                         options:colorBindingOptions];
648                         [usageView bind:@"textOpacity"
649                                    toObject:udc
650                                 withKeyPath:@"values.Dock icon text opacity"
651                                         options:nil];
652                         [usageView bind:@"backgroundColor"
653                                    toObject:self
654                                 withKeyPath:@"backgroundColor"
655                                         options:nil];
657                         oddCellRect.origin.x += oddCellRect.size.width;
658                 }
659         }
660         while(numNotOddCells--) {
661                 frame = [floaterCellsEnum nextRect];
662                 frame.origin.y += offsetY;
663                 unsigned col = CPUNum % numCols, row = CPUNum / numRows;
665                 CPUUsageView *usageView;
666                 if(CPUNum >= usageViewsArrayCount) {
667                         usageView = [[[CPUUsageView alloc] initWithFrame:frame] autorelease];
668                         [contentView addSubview:usageView];
669                         [usageViewsArray addObject:usageView];
670                         ++usageViewsArrayCount;
671                 } else {
672                         usageView = [usageViewsArray objectAtIndex:CPUNum];
673                         [usageView setFrame:frame];
674                 }
675                 unsigned mask = NSViewWidthSizable | NSViewHeightSizable
676                         | (NSViewMinXMargin * (col  > 0U)           )
677                         | (NSViewMaxXMargin * (col < (numCols - 1U)))
678                         | (NSViewMaxYMargin * (row  > 0U)           )
679                         | (NSViewMinYMargin * (row < (numRows - 1U)))
680                         ;
681                 [usageView setAutoresizingMask:mask];
682                 [usageView setMenu:contextualMenu];
684                 [usageView setDrawsFrame:shouldDrawFrame];
685                 if(numCPUs > 1)
686                         [usageView setCPUNumber:++CPUNum];
688                 [usageView bind:@"textColor"
689                            toObject:udc
690                         withKeyPath:@"values.Floater text color"
691                                 options:colorBindingOptions];
692                 [usageView bind:@"textOpacity"
693                            toObject:udc
694                         withKeyPath:@"values.Floater text opacity"
695                                 options:nil];
696                 [usageView bind:@"backgroundColor"
697                            toObject:self
698                         withKeyPath:@"backgroundColor"
699                                 options:nil];
700         }
702         [floaterOddCellsEnum release];
703         [floaterCellsEnum release];
706 @end