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));
122 // draw the paging curves on top of the memory usage graph
123 [memInfo startIterate];
124 for (lastpageins = lastpageouts = x = 0; [memInfo getNext:&vmdata]; x++) {
125 if (pageinAtopPageout) {
126 y = GRAPH_SIZE * (1.0 - vmdata.pageins * pgfactor);
127 yy = GRAPH_SIZE * (1.0 - lastpageins * pgfactor);
129 y = GRAPH_SIZE * vmdata.pageins * pgfactor;
130 yy = GRAPH_SIZE * lastpageins * pgfactor;
132 [[preferences objectForKey:PAGEIN_COLOR_KEY] set];
133 [NSBezierPath strokeLineFromPoint:NSMakePoint(x, yy) toPoint:NSMakePoint(x+1, y)];
134 if (pageinAtopPageout) {
135 y = GRAPH_SIZE * vmdata.pageouts * pgfactor;
136 yy = GRAPH_SIZE * lastpageouts * pgfactor;
138 y = GRAPH_SIZE * (1.0 - vmdata.pageouts * pgfactor);
139 yy = GRAPH_SIZE * (1.0 - lastpageouts * pgfactor);
141 [[preferences objectForKey:PAGEOUT_COLOR_KEY] set];
142 [NSBezierPath strokeLineFromPoint:NSMakePoint(x, yy) toPoint:NSMakePoint(x+1, y)];
143 lastpageins = vmdata.pageins;
144 lastpageouts = vmdata.pageouts;
147 // transfer graph image to icon image
148 [graphImage unlockFocus];
149 [displayImage lockFocus];
150 [graphImage compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];
152 // draw paging rate into the icon image
153 [self drawPageins:vmdata.pageins pageouts:vmdata.pageouts];
155 [displayImage unlockFocus];
160 // update graphImage (based on previous graphImage), put graph and pageing rate into displayImage
162 VMData vmdata, vmdata0;
165 BOOL pageinAtopPageout = [[preferences objectForKey:PAGEIN_ATOP_PAGEOUT_KEY] boolValue];
166 double pagingmax = [[preferences objectForKey:PAGING_SCALE_MAX_KEY] doubleValue];
167 double interval = 0.1 * [[preferences objectForKey:UPDATE_FREQUENCY_KEY] floatValue];
168 double pgfactor = 1.0 / (pagingmax * interval);
170 [graphImage lockFocus];
172 // offset the old graph image
173 [graphImage compositeToPoint:NSMakePoint(-1, 0) operation:NSCompositeCopy];
175 [memInfo getLast:&vmdata0];
176 [memInfo getCurrent:&vmdata];
178 // draw chronological graph into graph image
179 y = vmdata.wired * GRAPH_SIZE;
180 [[preferences objectForKey:WIRED_COLOR_KEY] set];
181 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, 0.0, GRAPH_SIZE - 1, y));
183 y += vmdata.active * GRAPH_SIZE;
184 [[preferences objectForKey:ACTIVE_COLOR_KEY] set];
185 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, yy, GRAPH_SIZE - 1, y));
187 y += vmdata.inactive * GRAPH_SIZE;
188 [[preferences objectForKey:INACTIVE_COLOR_KEY] set];
189 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, yy, GRAPH_SIZE - 1, y));
190 [[preferences objectForKey:FREE_COLOR_KEY] set];
191 NSRectFill (NSMakeRect(GRAPH_SIZE - 1, y, GRAPH_SIZE - 1, GRAPH_SIZE));
193 if (pageinAtopPageout) {
194 y = GRAPH_SIZE * (1.0 - vmdata.pageins * pgfactor);
195 yy = GRAPH_SIZE * (1.0 - vmdata0.pageins * pgfactor);
197 y = GRAPH_SIZE * vmdata.pageins * pgfactor;
198 yy = GRAPH_SIZE * vmdata0.pageins * pgfactor;
200 [[preferences objectForKey:PAGEIN_COLOR_KEY] set];
201 [NSBezierPath strokeLineFromPoint:NSMakePoint(GRAPH_SIZE - 1, yy) toPoint:NSMakePoint(GRAPH_SIZE, y)];
203 if (pageinAtopPageout) {
204 y = GRAPH_SIZE * vmdata.pageouts * pgfactor;
205 yy = GRAPH_SIZE * vmdata0.pageouts * pgfactor;
207 y = GRAPH_SIZE * (1.0 - vmdata.pageouts * pgfactor);
208 yy = GRAPH_SIZE * (1.0 - vmdata0.pageouts * pgfactor);
210 [[preferences objectForKey:PAGEOUT_COLOR_KEY] set];
211 [NSBezierPath strokeLineFromPoint:NSMakePoint(GRAPH_SIZE - 1, yy) toPoint:NSMakePoint(GRAPH_SIZE, y)];
213 // transfer graph image to icon image
214 [graphImage unlockFocus];
215 [displayImage lockFocus];
216 [graphImage compositeToPoint:NSMakePoint(0.0, 0.0) operation:NSCompositeCopy];
218 // draw paging rate into the icon image
219 [self drawPageins:vmdata.pageins pageouts:vmdata.pageouts];
221 [displayImage unlockFocus];
225 - (void)setApplicationIcon
226 // set the (scaled) application icon
228 float inc = GRAPH_SIZE * (1.0 - [[preferences objectForKey:DOCK_ICON_SIZE_KEY] floatValue]);
229 [iconImage lockFocus];
230 [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];
231 [iconImage unlockFocus];
232 [NSApp setApplicationIconImage:iconImage];
237 // get a new sample and refresh the graph
241 [self setApplicationIcon];
243 if ([[preferences objectForKey:SHOW_GRAPH_WINDOW_KEY] boolValue]) {
244 [window disableFlushWindow];
246 [window enableFlushWindow];
247 [window flushWindow];
253 // completely redraw the graph (to show new preferences settings)
256 [iconImage lockFocus];
257 [[NSColor clearColor] set];
258 NSRectFill (NSMakeRect(0, 0, GRAPH_SIZE, GRAPH_SIZE));
259 [iconImage unlockFocus];
260 [self setApplicationIcon];
262 if ([[preferences objectForKey:SHOW_GRAPH_WINDOW_KEY] boolValue]) {
263 [window disableFlushWindow];
265 [window enableFlushWindow];
266 [window flushWindow];
273 double newInterval = 0.1 * [[preferences objectForKey:UPDATE_FREQUENCY_KEY] floatValue];
276 if (fabs([timer timeInterval] - newInterval) < 0.001)
277 return; /* frequency not changed */
281 timer = [NSTimer scheduledTimerWithTimeInterval:newInterval
282 target:self selector:@selector(refreshGraph) userInfo:nil repeats:YES];
287 - (void)showPreferences:(id)sender
289 [NSApp activateIgnoringOtherApps:YES]; /* activate application when called from Dock menu */
290 [preferences showPreferences:self];
294 - (void)showAboutBox:(id)sender
296 [NSApp activateIgnoringOtherApps:YES]; /* activate application when called from Dock menu */
297 [NSApp orderFrontStandardAboutPanel:sender];
305 NSString *memoryMonitorPath = [[NSBundle mainBundle] bundlePath];
306 NSDictionary *loginItemDict = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithFormat:@"%@/Library/Preferences/loginwindow.plist", NSHomeDirectory()]];
307 NSEnumerator *loginItemEnumerator = [[loginItemDict objectForKey:@"AutoLaunchedApplicationDictionary"] objectEnumerator];
309 while ((obj = [loginItemEnumerator nextObject])) {
310 if ([[obj objectForKey:@"Path"] isEqualTo:memoryMonitorPath])
317 - (unsigned)systemVersion
318 // returns the system version normally retrieved with Gestalt(gestaltSystemVersion, &systemVersion)
322 unsigned version = 0;
324 for (p = [[[NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]
325 objectForKey:@"ProductVersion"] cString]; *p; p++) {
327 version = (version << 4) | (*p - '0');
329 if (version < 0x1000) // for 10.0, 10.1
335 - (BOOL)updateFrameName
336 // calculate the frameName used to save the window position; return TRUE iff the name changed,
337 // i. e. the display configuration changed since last call of this method
343 NSString *string = @"MMWL"; // MemoryMonitorWindowLocation
344 NSEnumerator *enumerator = [[NSScreen screens] objectEnumerator];
346 while ((screen = [enumerator nextObject])) {
347 rect = [screen frame];
349 stringByAppendingString:[NSString stringWithFormat:@"%.0f%.0f%.0f%.0f",
350 rect.origin.x, rect.origin.y, rect.size.width, rect.size.height]];
352 nameDidChange = ! [string isEqualToString:frameName];
356 return (nameDidChange);
360 - (void)applicationDidFinishLaunching:(NSNotification *)notification
362 preferences = [[Preferences alloc] init];
363 memInfo = [[MemInfo alloc] initWithCapacity:GRAPH_SIZE];
365 displayImage = [[NSImage allocWithZone:[self zone]] initWithSize:NSMakeSize(GRAPH_SIZE, GRAPH_SIZE)];
366 graphImage = [[NSImage allocWithZone:[self zone]] initWithSize:NSMakeSize(GRAPH_SIZE, GRAPH_SIZE)];
367 iconImage = [[NSImage allocWithZone:[self zone]] initWithSize:NSMakeSize(GRAPH_SIZE, GRAPH_SIZE)];
370 window = [[TranslucentWindow allocWithZone:[self zone]]
371 initWithContentRect:NSMakeRect(0.0, 0.0, GRAPH_SIZE, GRAPH_SIZE)
372 styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
373 [window setReleasedWhenClosed:NO];
374 [window setBackgroundColor:[NSColor clearColor]];
375 [self updateFrameName];
376 if (! [window setFrameUsingName:frameName]) {
377 // for compatibility with version 1.1 preferences file
378 [window setFrameUsingName:@"MemoryMonitorWindowLocation"];
379 [NSWindow removeFrameUsingName:@"MemoryMonitorWindowLocation"];
380 [window saveFrameUsingName:frameName];
382 [window setDelegate:self];
384 view = [[TranslucentView allocWithZone:[self zone]] initWithFrame:NSMakeRect(0.0, 0.0, GRAPH_SIZE, GRAPH_SIZE)];
385 [window setContentView:view];
386 [view setContentDrawer:self method:@selector(drawImageOnWindow)];
387 [view setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)];
388 [view setToolTip:@"Memory Monitor"];
391 [self showHideWindow];
393 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showHideWindow) name:PREFERENCES_CHANGED object:nil];
394 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateGraph) name:PREFERENCES_CHANGED object:nil];
395 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setTimer) name:PREFERENCES_CHANGED object:nil];
397 if ([self systemVersion] < 0x1010 && [self isLoginItem])
398 [NSTimer scheduledTimerWithTimeInterval:30 target:self selector:@selector(setTimer) userInfo:nil repeats:NO];
404 - (void)applicationWillTerminate:(NSNotification *)aNotification
411 [preferences savePreferences];
412 [NSApp setApplicationIconImage:[NSImage imageNamed:@"MemoryMonitor.icns"]];
416 - (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification
418 [self updateFrameName];
419 [window setFrameUsingName:frameName];
423 - (void)windowDidMove:(NSNotification *)aNotification
425 if (! [self updateFrameName])
426 [window saveFrameUsingName:frameName];