4 * Copyright © 2001-2003 Bernhard Baehr
6 * MainController.m - Main Application Controller Class
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #import "MainController.h"
27 #define GRAPH_SIZE 128
30 @implementation MainController
33 - (void)drawImageOnWindow
35 [displayImage drawInRect:NSMakeRect(0, 0, NSWidth([window frame]), NSHeight([window frame]))
36 fromRect:NSMakeRect(0, 0, GRAPH_SIZE, GRAPH_SIZE) operation:NSCompositeCopy
41 - (void)showHideWindow
45 if ([[preferences objectForKey:SHOW_GRAPH_WINDOW_KEY] boolValue]) {
46 size = [[preferences objectForKey:GRAPH_WINDOW_SIZE_KEY] floatValue];
47 [window setContentSize:NSMakeSize(size, size)];
48 [window orderWindow:NSWindowBelow relativeTo:[preferences windowNumber]];
49 [window setLevel:([[preferences objectForKey:GRAPH_WINDOW_ON_TOP_KEY] boolValue] ?
50 NSFloatingWindowLevel : NSNormalWindowLevel)];
52 [window orderOut:self];
56 - (void)drawPageins:(int)pageins pageouts:(int)pageouts
60 NSMutableDictionary *fontAttrs;
62 // draw paging rate into the icon image
63 if ([[preferences objectForKey:SHOW_PAGING_RATE_KEY] boolValue]) {
64 paging = (pageins + pageouts) / (0.1 * [[preferences objectForKey:UPDATE_FREQUENCY_KEY] floatValue]);
67 string = NSLocalizedString(@"out", @"");
68 else if (pageouts == 0)
69 string = NSLocalizedString(@"in", @"");
71 string = NSLocalizedString(@"i/o", @"");
72 fontAttrs = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
73 [NSFont boldSystemFontOfSize:48.0], NSFontAttributeName,
74 [NSColor blackColor], NSForegroundColorAttributeName,
76 [string drawAtPoint:NSMakePoint(4.0, 60.0) withAttributes:fontAttrs];
77 [fontAttrs setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
78 [string drawAtPoint:NSMakePoint(2.0, 62.0) withAttributes:fontAttrs];
79 string = [NSString stringWithFormat:@"%d", paging];
80 [fontAttrs setObject:[NSColor blackColor] forKey:NSForegroundColorAttributeName];
81 [string drawAtPoint:NSMakePoint(4.0, 12.0) withAttributes:fontAttrs];
82 [fontAttrs setObject:[NSColor whiteColor] forKey:NSForegroundColorAttributeName];
83 [string drawAtPoint:NSMakePoint(2.0, 14.0) withAttributes:fontAttrs];
91 // completely redraw graphImage, put graph and pageing rate into displayImage
94 int lastpageins, lastpageouts, x;
97 BOOL pageinAtopPageout = [[preferences objectForKey:PAGEIN_ATOP_PAGEOUT_KEY] boolValue];
98 double pagingmax = [[preferences objectForKey:PAGING_SCALE_MAX_KEY] doubleValue];
99 double interval = 0.1 * [[preferences objectForKey:UPDATE_FREQUENCY_KEY] floatValue];
100 double pgfactor = 1.0 / (pagingmax * interval);
102 [graphImage lockFocus];
104 // draw the memory usage graph
105 [memInfo startIterate];
106 for (x = 0; [memInfo getNext:&vmdata]; x++) {
107 y = vmdata.wired * GRAPH_SIZE;
108 [[preferences objectForKey:WIRED_COLOR_KEY] set];
109 NSRectFill (NSMakeRect(x - 1, 0.0, x, y));
111 y += vmdata.active * GRAPH_SIZE;
112 [[preferences objectForKey:ACTIVE_COLOR_KEY] set];
113 NSRectFill (NSMakeRect(x - 1, yy, x, y));
115 y += vmdata.inactive * GRAPH_SIZE;
116 [[preferences objectForKey:INACTIVE_COLOR_KEY] set];
117 NSRectFill (NSMakeRect(x - 1, yy, x, y));
118 [[preferences objectForKey:FREE_COLOR_KEY] set];
119 NSRectFill (NSMakeRect(x - 1, y, x, GRAPH_SIZE));
123 TODO paging curves: we don't need these here anymore.
125 // draw the paging curves on top of the memory usage graph
126 [memInfo startIterate];
127 for (lastpageins = lastpageouts = x = 0; [memInfo getNext:&vmdata]; x++) {
128 if (pageinAtopPageout) {
129 y = GRAPH_SIZE * (1.0 - vmdata.pageins * pgfactor);
130 yy = GRAPH_SIZE * (1.0 - lastpageins * pgfactor);
132 y = GRAPH_SIZE * vmdata.pageins * pgfactor;
133 yy = GRAPH_SIZE * lastpageins * pgfactor;
135 [[preferences objectForKey:PAGEIN_COLOR_KEY] set];
136 [NSBezierPath strokeLineFromPoint:NSMakePoint(x, yy) toPoint:NSMakePoint(x+1, y)];
137 if (pageinAtopPageout) {
138 y = GRAPH_SIZE * vmdata.pageouts * pgfactor;
139 yy = GRAPH_SIZE * lastpageouts * pgfactor;
141 y = GRAPH_SIZE * (1.0 - vmdata.pageouts * pgfactor);
142 yy = GRAPH_SIZE * (1.0 - lastpageouts * pgfactor);
144 [[preferences objectForKey:PAGEOUT_COLOR_KEY] set];
145 [NSBezierPath strokeLineFromPoint:NSMakePoint(x, yy) toPoint:NSMakePoint(x+1, y)];
146 lastpageins = vmdata.pageins;
147 lastpageouts = vmdata.pageouts;
150 // transfer graph image to icon image
151 [graphImage unlockFocus];
152 [displayImage lockFocus];
153 [graphImage compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];
155 // draw paging rate into the icon image
156 [self drawPageins:vmdata.pageins pageouts:vmdata.pageouts];
158 [displayImage unlockFocus];
163 // update graphImage (based on previous graphImage), put graph and pageing rate into displayImage
165 VMData vmdata, vmdata0;
168 BOOL pageinAtopPageout = [[preferences objectForKey:PAGEIN_ATOP_PAGEOUT_KEY] boolValue];
169 double pagingmax = [[preferences objectForKey:PAGING_SCALE_MAX_KEY] doubleValue];
170 double interval = 0.1 * [[preferences objectForKey:UPDATE_FREQUENCY_KEY] floatValue];
171 double pgfactor = 1.0 / (pagingmax * interval);
173 [graphImage lockFocus];
175 // offset the old graph image
176 [graphImage compositeToPoint:NSMakePoint(-1, 0) operation:NSCompositeCopy];
178 [memInfo getLast:&vmdata0];
179 [memInfo getCurrent:&vmdata];
181 // draw chronological graph into graph image
182 y = vmdata.wired * GRAPH_SIZE;
183 [[preferences objectForKey:WIRED_COLOR_KEY] set];
184 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, 0.0, GRAPH_SIZE - 1, y));
186 y += vmdata.active * GRAPH_SIZE;
187 [[preferences objectForKey:ACTIVE_COLOR_KEY] set];
188 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, yy, GRAPH_SIZE - 1, y));
190 y += vmdata.inactive * GRAPH_SIZE;
191 [[preferences objectForKey:INACTIVE_COLOR_KEY] set];
192 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, yy, GRAPH_SIZE - 1, y));
193 [[preferences objectForKey:FREE_COLOR_KEY] set];
194 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, y, GRAPH_SIZE - 1, GRAPH_SIZE));
196 if (pageinAtopPageout) {
197 y = GRAPH_SIZE * (1.0 - vmdata.pageins * pgfactor);
198 yy = GRAPH_SIZE * (1.0 - vmdata0.pageins * pgfactor);
200 y = GRAPH_SIZE * vmdata.pageins * pgfactor;
201 yy = GRAPH_SIZE * vmdata0.pageins * pgfactor;
203 [[preferences objectForKey:PAGEIN_COLOR_KEY] set];
204 [NSBezierPath strokeLineFromPoint:NSMakePoint(GRAPH_SIZE - 1, yy) toPoint:NSMakePoint(GRAPH_SIZE, y)];
206 if (pageinAtopPageout) {
207 y = GRAPH_SIZE * vmdata.pageouts * pgfactor;
208 yy = GRAPH_SIZE * vmdata0.pageouts * pgfactor;
210 y = GRAPH_SIZE * (1.0 - vmdata.pageouts * pgfactor);
211 yy = GRAPH_SIZE * (1.0 - vmdata0.pageouts * pgfactor);
213 [[preferences objectForKey:PAGEOUT_COLOR_KEY] set];
214 [NSBezierPath strokeLineFromPoint:NSMakePoint(GRAPH_SIZE - 1, yy) toPoint:NSMakePoint(GRAPH_SIZE, y)];
216 // transfer graph image to icon image
217 [graphImage unlockFocus];
218 [displayImage lockFocus];
219 [graphImage compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];
221 // draw paging rate into the icon image
222 [self drawPageins:vmdata.pageins pageouts:vmdata.pageouts];
224 [displayImage unlockFocus];
228 - (void)setApplicationIcon
229 // set the (scaled) application icon
231 float inc = GRAPH_SIZE * (1.0 - [[preferences objectForKey:DOCK_ICON_SIZE_KEY] floatValue]);
232 [iconImage lockFocus];
233 [displayImage drawInRect:NSMakeRect(inc, inc, GRAPH_SIZE - 2 * inc, GRAPH_SIZE - 2 * inc) fromRect:NSMakeRect(0, 0, GRAPH_SIZE, GRAPH_SIZE) operation:NSCompositeCopy fraction:1.0];
234 [iconImage unlockFocus];
235 [NSApp setApplicationIconImage:iconImage];
240 // get a new sample and refresh the graph
244 [self setApplicationIcon];
246 if ([[preferences objectForKey:SHOW_GRAPH_WINDOW_KEY] boolValue]) {
247 [window disableFlushWindow];
249 [window enableFlushWindow];
250 [window flushWindow];
256 // completely redraw the graph (to show new preferences settings)
259 [iconImage lockFocus];
260 [[NSColor clearColor] set];
261 NSRectFill (NSMakeRect(0, 0, GRAPH_SIZE, GRAPH_SIZE));
262 [iconImage unlockFocus];
263 [self setApplicationIcon];
265 if ([[preferences objectForKey:SHOW_GRAPH_WINDOW_KEY] boolValue]) {
266 [window disableFlushWindow];
268 [window enableFlushWindow];
269 [window flushWindow];
276 double newInterval = 0.1 * [[preferences objectForKey:UPDATE_FREQUENCY_KEY] floatValue];
279 if (fabs([timer timeInterval] - newInterval) < 0.001)
280 return; /* frequency not changed */
284 timer = [NSTimer scheduledTimerWithTimeInterval:newInterval
285 target:self selector:@selector(refreshGraph) userInfo:nil repeats:YES];
290 - (void)showPreferences:(id)sender
292 [NSApp activateIgnoringOtherApps:YES]; /* activate application when called from Dock menu */
293 [preferences showPreferences:self];
297 - (void)showAboutBox:(id)sender
299 [NSApp activateIgnoringOtherApps:YES]; /* activate application when called from Dock menu */
300 [NSApp orderFrontStandardAboutPanel:sender];
308 NSString *memoryMonitorPath = [[NSBundle mainBundle] bundlePath];
309 NSDictionary *loginItemDict = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Library/Preferences/loginwindow.plist", NSHomeDirectory()]];
310 NSEnumerator *loginItemEnumerator = [[loginItemDict objectForKey:@"AutoLaunchedApplicationDictionary"] objectEnumerator];
312 while ((obj = [loginItemEnumerator nextObject])) {
313 if ([[obj objectForKey:@"Path"] isEqualTo:memoryMonitorPath])
320 - (unsigned)systemVersion
321 // returns the system version normally retrieved with Gestalt(gestaltSystemVersion, &systemVersion)
325 unsigned version = 0;
327 for (p = [[[NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]
328 objectForKey:@"ProductVersion"] cString]; *p; p++) {
330 version = (version << 4) | (*p - '0');
332 if (version < 0x1000) // for 10.0, 10.1
338 - (BOOL)updateFrameName
339 // calculate the frameName used to save the window position; return TRUE iff the name changed,
340 // i. e. the display configuration changed since last call of this method
346 NSString *string = @"MMWL"; // MemoryMonitorWindowLocation
347 NSEnumerator *enumerator = [[NSScreen screens] objectEnumerator];
349 while ((screen = [enumerator nextObject])) {
350 rect = [screen frame];
352 stringByAppendingString:[NSString stringWithFormat:@"%.0f%.0f%.0f%.0f",
353 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height]];
355 nameDidChange = ! [string isEqualToString:frameName];
359 return (nameDidChange);
363 - (void)applicationDidFinishLaunching:(NSNotification *)notification
365 preferences = [[Preferences alloc] init];
366 memInfo = [[MemInfo alloc] initWithCapacity:GRAPH_SIZE];
367 cpuInfo = [[CPUInfo alloc] init];
369 displayImage = [[NSImage allocWithZone:[self zone]] initWithSize:NSMakeSize(GRAPH_SIZE, GRAPH_SIZE)];
370 graphImage = [[NSImage allocWithZone:[self zone]] initWithSize:NSMakeSize(GRAPH_SIZE, GRAPH_SIZE)];
371 iconImage = [[NSImage allocWithZone:[self zone]] initWithSize:NSMakeSize(GRAPH_SIZE, GRAPH_SIZE)];
374 window = [[TranslucentWindow allocWithZone:[self zone]]
375 initWithContentRect:NSMakeRect(0.0, 0.0, GRAPH_SIZE, GRAPH_SIZE)
376 styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
377 [window setReleasedWhenClosed:NO];
378 [window setBackgroundColor:[NSColor clearColor]];
379 [self updateFrameName];
380 if (! [window setFrameUsingName:frameName]) {
381 // for compatibility with version 1.1 preferences file
382 [window setFrameUsingName:@"MemoryMonitorWindowLocation"];
383 [NSWindow removeFrameUsingName:@"MemoryMonitorWindowLocation"];
384 [window saveFrameUsingName:frameName];
386 [window setDelegate:self];
388 view = [[TranslucentView allocWithZone:[self zone]] initWithFrame:NSMakeRect(0.0, 0.0, GRAPH_SIZE, GRAPH_SIZE)];
389 [window setContentView:view];
390 [view setContentDrawer:self method:@selector(drawImageOnWindow)];
391 [view setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
392 [view setToolTip:@"Memory Monitor"];
395 [self showHideWindow];
397 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showHideWindow) name:PREFERENCES_CHANGED object:nil];
398 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateGraph) name:PREFERENCES_CHANGED object:nil];
399 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setTimer) name:PREFERENCES_CHANGED object:nil];
401 if ([self systemVersion] < 0x1010 && [self isLoginItem])
402 [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(setTimer) userInfo:nil repeats:NO];
408 - (void)applicationWillTerminate:(NSNotification *)aNotification
415 [preferences savePreferences];
416 [NSApp setApplicationIconImage:[NSImage imageNamed:@"MemoryMonitor.icns"]];
420 - (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification
422 [self updateFrameName];
423 [window setFrameUsingName:frameName];
427 - (void)windowDidMove:(NSNotification *)aNotification
429 if (! [self updateFrameName])
430 [window saveFrameUsingName:frameName];