TweetFollow Us on Twitter

Measure Load
Volume Number:4
Issue Number:3
Column Tag:C Workshop

A Background Task for Measuring Load

By Peter Korn, Apple Computer

Peter Korn is a full time software quality assurance engineer for Apple Computer, and a full time undergraduate student in Mathematics at UC Berkely, which makes him a busy young man! In this article, he gives us a technique for measuring the activity under multifinder by timing null events in a background task.

True™ Multitasking

I don’t have to tell you that multitasking is good. By not having to wait on the computer to finish a task before you can go on to the next one you save time, frustration, and creative energies. There’s nothing as frustrating to me as having an idea and wanting to take it somewhere but not being able to do so because my computer is busy printing, or downloading, or compiling (admittedly with LightSpeed C my wait isn’t very long). But once we have the concept of multitasking, is multifinder True™ multitasking? Before I tackle that one, I want to give a brief history...

In the beginning there was the Macintosh (128K model). With it I could have a Desk Accessory or three pop up whenever I had an idea that I didn’t want to interrupt whatever else I was doing to work on. And, if I hadn’t foolishly started a print job or an {up,down} loading session on my modem, I was fine. Some of the usefulness of multitasking already!

When the 512K Mac came along there was more RAM to play with, and some programs took advantage of this RAM to use the Vertical Retrace Manager for more than just a cursor blinker and tick counter. With programs like SuperSpool, I could bring up a DA even while my program was printing. And the DAs were getting bigger too. There were spreadsheets, filers, outliners and word processors and terminal programs (even one called Backdown, that would download for me in the background). And while all of this took even more RAM, the Mac Plus was released shortly and life was pretty ok.

Except that for a program to allow itself to be run ‘in the background’, it had to go through a series of contortions, and deny itself use of the Memory Manager, and so very few programs were written this way. Certainly Backdown, while nice, wasn’t a full-blown terminal emulator. And still there was no way of checking spelling in the background, or compiling, or recalculating a spreadsheet, or what-have-you. The Vertical Retrace Manager just wasn’t cut out to be a scheduler.

Even with Switcher I still couldn’t do all these sorts of things at once; I could only switch between them when they were idle. And this I had to do by hand (with the exception of ThunderScan, but there’s no surprise there, as it shares authors with Switcher).

Fig. 1Our little Load Average Window Measures time between null events

A True™ multitasking system should allow me to run True™ tasks, and have them all going simultaneously (or close to simultaneously--you know what I mean). But an important question is, must this be preemptive? As a user, do I really care how it’s all done, so long as my download, spreadsheet recalculation, and mandelbrot plot all continue whilst I’m busy writing a letter to grandma? In fact, if I’m typing my letter, I don’t want the computer to be any slower responding to my keystrokes just because it’s doing other things--I’m being creative here, and I don’t want to have to deal with a sluggish interface that slows me down just because I have the computer doing other things as well.

In wider circles, True™ multitasking is considered preemptive multitasking. Each task (program, driver, what-have-you) is assigned some priority number (-10 to 10, say), and some scheduler program arbitrates which task gets how much CPU time when. First it looks at all the programs of the highest priority level, and for all of those that want time (some may not; perhaps their waiting for slow networking i/o, or are finished computing their mandelbrot and haven’t been given further instructions), it allocates that time evenly amongst them, interrupting each at some small, regular interval (100 milliseconds, say), and giving time over to the next needy task of equal priority level. When no tasks at that priority level want time, then the scheduler looks at tasks of a lower priority level and parcels out time as before to all of them, and so on. If the user doesn’t want his creativity disrupted, he merely sets the priority of the task he is working on to something higher than all the other tasks. And in between keystrokes (in the word processor, say), the other tasks will get time to do what they need to do (ie: when the user is idle [for some fraction of a second]).

Apple, in it’s infinite wisdom, didn’t do it this way. The main reason was for compatibility. Another was because of system architecture (while the Amiga can carry it off without a hardware Memory Management Unit that ensures that whatever memory address a program writes to isn’t taken by something else, certain developers [including Apple] have too long thought of the Macintosh as belonging entirely to the program currently running [allowing for those piddling desk accessories, maybe] to pull this off). But is Apple’s way also True™ multitasking? Well, if it gets the job done...

Multitasking the Macintosh way

Under the new operating system (System Disk 5.0, which contains System 4.2, Finder 6.0, MultiFinder 1.0, Chooser and Control Panel 3.2, and [aha, so this is where they got the number] LaserWriter Driver 5.0), we can run a program called MultiFinder. MultiFinder, (like Switcher and Servant before them) allows us to load in several programs at once, and switch between them at will, in a very clean fashion (click on window beneath belonging to another program, select the program from the Apple menu, or cycle through the program list on the upper right corner of the MenuBar; this type of program switching is what tech note #180 calls “Major Switching”). Additionally, programs that define themselves as ‘backgroundable’ can get time when the ‘foreground’ program (the one who’s window is foremost and active) is idle.

This is done by taking control when the foreground program calls GetNextEvent(), EventAvail(), or WaitNextEvent(), a new call under multifinder, and seeing what the foreground program’s event is. If the event is not a nullEvent, the foreground program gets it. If the event is a nullEvent, then things get interesting and somewhat confusing.

Null Event Processing

If our foreground program is not MultiFinder friendly, it’s doubtless using GetNextEvent(). In this case, MultiFinder doesn’t do anything on the first nullEvent. Or the second nullEvent (this is for programs that do work on nullEvents but were written before the program’s authors knew about MultiFinder, like MS-Word). But the third nullEvent the foreground program never sees (in the current version of MultiFinder -- in later versions GNE() calling apps may only see 1 nullEvent, or perhaps none). Instead MultiFinder looks about and sees if there are any background tasks running (I’ll describe those in a moment), and turns control over to one of them (this type of switching is called “Minor Switching” by tech note #180). That background task then calls GNE() or WNE(), and MultiFinder takes over immediately, checks the event, and if it’s not a nullEvent, returns control immediately to the foreground program. If it is another nullEvent, MultiFinder checks to see if there are any other background tasks, allocating nullEvents in a round-robin fashion until a non-nullEvent comes along, at which time control is returned to the ‘foreground’ task, which is returned that non-nullEvent.

For everything to work right, MultiFinder has to make certain assumptions. First, it assumes that all programs call GNE(), WNE(), or EA() regularly (which a program is under no obligation to do, though it would be nice...). If a program doesn’t, then when it’s in the foreground, no background tasks will ever get time. Furthermore, all programs that want to run as background tasks have to call one of these three, and call them pretty often, in order to ensure that the foreground task can quickly respond to any user event (a non-nullEvent, in this case). There’s no more a way to stop a renegade background task from taking over the CPU the moment it gets it than there is a way to stop a renegade program from making the machine crash -- programmers and users and the media will be the judge, not any hardware MMU.

So is it True™ multitasking? You decide.

How to be a (good) background task

Formally all you have to do to be a background task is to have a Size = -1 resource with the can background bit set. As a background task, to do any work, you also have to check for, and use, nullEvents. Furthermore, the obedient background task does no more than 50-100 milliseconds of work before calling GNE(), WNE(), or EA() again. for a wonderful example of a disobedient background task, look at Apple’s PrintMonitor that’s shipping with the 5.0 release (clearly following in MacWrite’s lawless ways...). Caveat: in MultiFinder 1.0 there is a bug when using GNE() from the background: you will get no nullEvents when you are first launched--you have to be switched out and then back in again before you start seeing those precious nullEvents.

As a background task, you can do almost anything a foreground task can do. You can write to the screen, read from disk, use the memory manager, play sound, etc. You can even be a foreground task (like a clock that updates the time constantly, and in the foreground allows the user to change from 12 hour to 24 hour display--or like a graphic load average program [which happens to be the subject of this article]). However... you are warned to do all of your work inside of windows. Drawing to the screen is a no-no. And if you are like a certain game I won’t mention (but who’s initials are Crystal Quest), and you don’t draw in a window and expect the user to click the mouse button whilst using your game, you will swiftly find yourself being switched (twitched, as it’s sometimes called) out while whatever program’s window was underneath the mouse when the mouseDown happened suddenly coming into the foreground. Also you shouldn’t put up a Modal Dialog box, as it won’t come up in the foreground but will stay in your layer and the user might never see it. Nor should you do anything that might affect the foreground task, like fiddling with the menuBar or cursor. Lastly, if your program is so uncouth as to change some system parameter (like the mouse tracking ratio, as done by a certain program with the same initials), you should at least be good enough to set it back to what it was when you get twitched out.

Which brings us to the next level of MultiFinder compatibility: accepting suspend/resume events (as the bit in the Size = -1 resource is labeled). These programs recognize two new event types that came about from the Switcher days: suspend and resume. A program (any program, not just backgroundable ones) that accepts suspend events doesn’t ever get that first nullEvent (on a Major Switch), (unless it’s got the background bit set, in which case it will share nullEvents with the other programs that have a background bit set). Instead it will see a suspend event. A suspend event is an app4Evt (theEvent.what = 15) with bit 0 of theEvent.message clear. A resume event is an app4Evt with bit 0 set to 1. A suspend event means that the program is about to be juggled (for some reason the future tense is juggled, and otherwise it’s called twitching), and the program should set the state of anything it muddled with back to what it was before it muddled with it and so forth, and then call GNE(), WNE() or EA(). Furthermore, if bit 1 of theEvent.message of the suspend event is set, the application should convert it’s internal clipboard to the deskscrap (or vice versa if the event is a resume event) so that other applications can use it. If that bit is clear, then no scrap conversion is necessary.

Another bit in the Size = -1 resource is the Juggler Aware bit. If this bit is set, the program uses WNE() instead of GNE() if MultiFinder is running (see the example in my code or in tech note #158 [where I stole it from] for how to do this). WNE() has two additional parameters that it gets passed: sleep (an unsigned long), and mouseRgn (a RgnHandle). Sleep is the number of ticks that the application can go without getting a nullEvent (for things like updating a blinking cursor or clock getting a character from the modem or some such). mouseRgn is a RgnHandle within which the mouse can move and the program needn’t be consulted as to it changing shape. An example of this is a terminal emulator, who’s cursor is an I-beam when it’s over the content region of the window, and an arrow when it’s over anything else. NIL cast to the proper variable type can be passed for either of these parameters.

Sayeth Apple: you shouldn’t set the Juggler Aware bit unless you use WNE(), and recognize suspend/resume events, and are in all ways cognizant of MultiFinder and running with other foreground and background tasks. And what does this involve?

Background tasking programs (in fact, all programs) should now do a number of things in order to better function in the MultiFinder environment. Programs should not do any work on updateEvents (other than updating the window in question with calls to BeginUpdate() and EndUpdate(), if nothing else). If a foreground program’s window is moved about, it my reveal a portion of a window underneath it that wasn’t revealed before. That program may all of a sudden decide that, on getting it’s updateEvent, that it wants to get some more characters from the modem and display them on the screen and such (like another program that I won’t mention who’s initials [really are] UW). All programs should include a Size = -1 resource, in which, if nothing else, they give a preferred memory partition size that they want to run in. If none is given, 384K is assumed (though this can be edited from the Get Info box in the Finder). Programs that were designed for the 128K Mac (in the 128K Mac days...) wind up taking gobs of RAM for no good reason. All programs need to support Desk Accessories, as it’s through this mechanism that Major Switching takes place. Lastly, to better utilize the MultiFinder environment (that allows multiple programs to be auto-launched on startup), programs should save their window positions and states so that the next time that they are invoked the user doesn’t have to fuss with moving around all of their windows to where he wants them. The kosher way to do this is with GlobalToLocal() on the upper left and lower right positions of your window (or just the upper left if it’s not resizable), and saving that information into the resource or data fork of another file (not the one that the program is in). Then call MoveWindow(savedPoint) to position it before making it visible. Alternately you could save a copy of the window resource into a configuration file. Just keep in mind that reading and writing to the window record itself a no-no (says Apple), ‘cause it may change in the future. And by writing to a file other than the program file, you allow yourself to be shared on a network (such as AppleShare or TOPS). Of course, if you don’t want to be shared, than this isn’t a problem. Unless you are a commercial program with single-user licensing only, you may as well allow yourself to be shared.

Graphic Load Average

When I first saw MultiFinder in action, I immediately thought that some means of measuring the ‘load average’ of the CPU was needed (after all, most all True™ multitasking systems have this). Being as there was no real way to measure this in the traditional sense (the number of processes the CPU is actively switching through), I decided that instead it would measure the number of nullEvents it got over a period of time, and graph it’s results (which is a more meaningful measure under MultiFinder).

Unfortunately someone beat me to it (actually, a lot of people beat me to it, but one sticks out in particular). I have to give credit to Erik Kilk, who posted Waiting to the Usenet some 72 hours before I had my program running. Clearly the only way I could salvage my ego was to try to make my program better than his.

Graphic Load Average (GLA) has four load meters, each of which can be set to a different sensitivity and update duration (and width). These settings are set via a hierarchical menu. Additionally it uses WNE(), and will not run if MultiFinder isn’t present (a dialog box comes up to nicely inform you of this). Also there is an option to Compress Juggler RAM, which makes a call to the new juggler call MFMaxMem(). While I have yet to see this call actually do something, certainly having it there somehow improves GLA (it also shows how to make the call from LightSpeed C v. 2.13, which doesn’t yet support these new calls -- see the modified-multifinder.h include file that I made, with the generous help of Ed Tecot of Apple, to use with LightSpeed C). [Note: The current version of LS C is 2.15. Updates are available from Think, or if purchased from MacTutor, from us. -Ed]

Graphic Load Average Controls

Each set of triplets controls three things: the width item, which is the width of the displayed graphic bar in the window, the ceiling, which is the number of ticks which will equal a full bar, and the time over which the null events will be averaged. The ceiling parameter is the sensitivity setting, as to how many ticks give a full height bar. The time parameter determines the number of ticks to wait before updating the display. If we set this to five, then we wait five ticks before updating the display with the average number received for that bar. We take the average, and divide it by the ceiling to calculate the height of the bar to be displayed. The ceiling is converted into pixels, divided by the number of ticks that equals a full ceiling to find the number of pixels to display.

By using the four indicators, you can have four different sensitivity settings for how many ticks would result in a full bar display. The maximum setting is a single tick. A tick is a 60th of a second. On each null event, the time is compared, so the average time between null events is being measured. The more bars you display, the longer the time between null events, which means more load on the machine.

Other points of interest and note: putting up a window in a saved position is all very nice, but what if the program is first run on a MacII, and then later is run on a MacPlus. It is important to check that window position is within the bounds of the window (better still, within the dragBounds that you use for MoveWindow()). I cheated in GLA, and took the cheap way out when it came to AppleShare. I wrote the configuration information into the same file as I execute from, which means that I cannot be shared under AppleShare (note: even if I was good, and wrote to a separate configuration file, I’d still not automatically be sharable. There’s a bit [there’s always a bit] that I need to set that says “I’m sharable, and won’t do anything bad”). [As we have reported in the past, forcing developers to create hundreds of little configuration files is a poor excuse for Apple’s failure to build a resource manager that is sharable. We don’t think this is the way to go and don’t particulary want our system file filled up with these things. Configuration information should be stored in a document instead. Documents are normally not expected to be sharable at the same time. In this program, Peter does another thing we don’t recommend: he opens a specific resource file by file name, a definite no-no. You should obtain the application file name from the finder info and open the file using that name instead of hard-coding a file name into the source. -Ed]

Dear Phil and Erich

(MultiFinder wish list)

While I’m very happy with all that I can now do with my Macintosh and MultiFinder, there are still things I’d like to see (especially as a programmer).

• I want some way of getting mouse clicks into a window when that mouse click caused a Major Switch (try clicking on GLA’s content region in a switch, dragging, and releasing the mouse--GLA’s window won’t drag)

• I’d like Alert and Dialog boxes that I can bring up via some sort of sublaunch mechanism that puts the dialog/alert ‘program’ into the foreground and my program in the background so that it can do work while the user takes his/her time responding to the dialog (I’d like a clean mechanism, not have to write my own ‘dialog/alert displaying task’ especially to do this).

• I want to see more backgroundware from Apple. A MacTerminal that downloads in the background would be very nice. Initializing a floppy diskette in the background would be great (though that requires multi-threaded i/o, another nice feature for a future MultiFinder)

• I’d like some kosher way of notifying a user that I need attention when I’m in the background. What does PrintMonitor know that we lowly developers don’t?

• I want to see documentation on this mysterious Layer Manager that’s managing all the app layers for me behind my back (and not sending me mouseDowns on Major Switches). In return, I promise to restrain myself from playing with undocumented traps in search of it.

• I’d like language developers to be seeded as early as possible with things like MultiFinder so that I’m not forced to use MPW to program on the cutting edge (try running MPW and MultiFinder and the app your writing on a 2 Meg machine sometime, not to mention the 80% of us who have 1 Meg machines!). Please don’t make me write glue for this--that’s what the language developers are for. [A swift kick in the rear to third party develoepers to update their products when they get the new interfaces is also in order. -Ed]

• I want MultiFinder to somehow guarantee my background task a nullEvent at least every so often, so that my communications software doesn’t time out due to it not getting any time. Just a little pre-emption so that connections don’t die just because MicroSomeone’s software decides to keep the CPU longer than it should have.

• I want the playing of sounds to not kill all user i/o. I want to be able to type characters and switch between programs while sound is playing. How about some more background tasks guys?

/* Graphic Load Average
 © 1987 Peter Korn

 This is a little program designed 
 to run in the backround under MultiFinder.
 It’s purpose:  to graphically indicate 
 the ‘load’ by creating a column chart of 
 the load.
 
*/
/*  Defines */

#define NIL  0
#define nullStopMask  0
#define MYALERT  128
#define MYDIALOG 150
#define MYWINDOW 128
#define DESK_ID  128
#define FILE_ID  129
#define EDIT_ID  130
#define CONFIGURE_ID 131
#define BASE_ID  200 /* heirarchical menus */
#define WINDOW_POSh150
#define WINDOW_POSv151
#define BAROFFSET10
#define WNETrapNum 0x60
#define UnImplTrapNum0x9F
#define SUSPEND_RESUME_EVT15
#define SUSPEND_RESUME_BIT0
#define CLIPBOARD_BIT0
#define MessageDialog258

/*  Includes*/

#include <Quickdraw.h>
#include <MacTypes.h>
#include <FontMgr.h>
#include <WindowMgr.h>
#include <MenuMgr.h>
#include <TextEdit.h>
#include <DialogMgr.h>
#include <EventMgr.h>
#include <DeskMgr.h>
#include <FileMgr.h>
#include <ToolboxUtil.h>
#include <ControlMgr.h>
#include <OSUtil.h>
#include <ResourceMgr.h>
#include <modified_MultiFinder.h>

/*  Globals */
 
typedef struct ConfigurationItem {
 int    value;
 Handle valueHandle;
 MenuHandle menuHandle;
}ConfigurationItem;

typedef struct ConfigurationTriplet {
 ConfigurationItem ceiling;
 ConfigurationItem interval;
 ConfigurationItem width;
};

struct  ConfigurationTriplettheConfiguration[4];

long    ticksGoneBy, tickSum[4], tickSumDivider[4];
int**windowPoshHandle, **windowPosvHandle;
GrafPtr theCurrentPort;
MenuHandledeskMenu, fileMenu, editMenu, configureMenu;
WindowPtr loadWindow;
Str255  daName;
Rect    dragBoundsRect;

/* restartProc */
restartProc()
{
 quitTime();
}

/* the Switches  */

long  CeilingSwitch (itemNumber)
 int    itemNumber;
{
 long returnVal;
 
 switch(itemNumber){
 case 1:
 returnVal = 1;
 break;
 case 2:
 returnVal = 2;
 break;
 case 3:
 returnVal = 3;
 break;
 case 4:
 returnVal = 4;
 break;
 case 5:
 returnVal = 5;
 break;
 case 6:
 returnVal = 6;
 break;
 case 7:
 returnVal = 7;
 break;
 case 8:
 returnVal = 8;
 break;
 case 9:
 returnVal = 9;
 break;
 case 10:
 returnVal = 10;
 break;
 case 11:
 returnVal = 15;
 break;
 case 12:
 returnVal = 20;
 break;
 case 13:
 returnVal = 30;
 break;
 case 14:
 returnVal = 60;
 break;
 }
 
 return(returnVal);
}

long  IntervalSwitch(itemNumber)
 int  itemNumber;
{
 long returnVal;
 
 switch(itemNumber){
 case 1:
 returnVal = 1;
 break;
 case 2:
 returnVal = 2;
 break;
 case 3:
 returnVal = 5;
 break;
 case 4:
 returnVal = 10;
 break;
 case 5:
 returnVal = 20;
 break;
 case 6:
 returnVal = 60;
 break;
 case 7:
 returnVal = 120;
 break;
 case 8:
 returnVal = 180;
 break;
 case 9:
 returnVal = 600;
 break;
 case 10:
 returnVal = 1200;
 break;
 }

 return(returnVal);
}

long  WidthSwitch(itemNumber)
 int    itemNumber;
{
 long returnVal;

 switch(itemNumber){
 case 1:
 returnVal = 1;
 break;
 case 2:
 returnVal = 2;
 break;
 case 3:
 returnVal = 3;
 break;
 case 4:
 returnVal = 4;
 break;
 case 5:
 returnVal = 5;
 break;
 }
 return(returnVal);
}

/* doMessage */

/* Politely inform user of messages like no
 multifinder running. Note- this is not
 the kosher way to do things since it doesn’t
 involve string resources, which can be edited 
 or translated. But it is much faster to develop. */

doMessage (message0,message1,message2,message3)
Str255  message0;
Str255  message1;
Str255  message2;
Str255  message3;
{
DialogPtr dialogP;
intitem;

 ParamText(message0, message1, message2, message3);
 dialogP = GetNewDialog(MessageDialog, (Ptr)NIL, (Ptr)-1);
 if (dialogP == NIL) {
 SysBeep(5);
 ExitToShell();
 }
 else {
 InitCursor(); 
 ModalDialog((Ptr)NIL, item);
 DisposDialog(dialogP); 
 } 
}

/* setUpWorld */

setUpWorld()
{
 int    i;
 SysEnvRectheWorld;
 OSErr  err;
 
 InitGraf(&thePort);
 InitFonts();
 FlushEvents(everyEvent,nullStopMask);
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(restartProc);
 InitCursor();

 /*check if WNE is implemented, and exit if not
 ...stolen from Tech Note #158*/
 err = SysEnvirons(1, &theWorld);
 if ((theWorld.machineType < 0) ||
 (NGetTrapAddress(WNETrapNum,ToolTrap) == NGetTrapAddress(UnImplTrapNum,ToolTrap))) 
{
 doMessage(“\pMultifinder not active”,””,””,””);
 ExitToShell();
 }
 SetRect(&dragBoundsRect, screenBits.bounds.left +4,
 screenBits.bounds.top + MBarHeight, 
 screenBits.bounds.right -4, screenBits.bounds.bottom -4);

 /*get saved resources  & don’t let ‘em get purged */

 OpenResFile(“\pGLA appl”); 
 /* note: this is not nice. Never hard code a file name. */
 
 for (i = 0; i < 4; i++) {
 theConfiguration[i].ceiling.valueHandle = GetResource((ResType) ‘INFO’, 
(BASE_ID + i*3));
 HNoPurge(theConfiguration[i].ceiling.menuHandle);
 theConfiguration[i].ceiling.value = CeilingSwitch(**theConfiguration[i].ceiling.valueHandle);
 
 theConfiguration[i].interval.valueHandle = GetResource((ResType) ‘INFO’, 
(BASE_ID + 1 + i*3));
 HNoPurge(theConfiguration[i].interval.menuHandle);
 theConfiguration[i].interval.value = IntervalSwitch(**theConfiguration[i].interval.valueHandle);

 theConfiguration[i].width.valueHandle = GetResource((ResType) ‘INFO’, 
(BASE_ID + 2 + i*3));
 HNoPurge(theConfiguration[i].width.menuHandle);
 theConfiguration[i].width.value = WidthSwitch(**theConfiguration[i].width.valueHandle);
 }

 /*call ResError(), and set them manually if get error */
 windowPoshHandle = (int **) GetResource( (ResType) ‘INFO’, WINDOW_POSh);
 HNoPurge(windowPoshHandle);
 
 windowPosvHandle = (int **) GetResource( (ResType) ‘INFO’, WINDOW_POSv);
 HNoPurge(windowPosvHandle);
 
 /*if point not inside dragBoundsRect, set default */
 if ((**windowPoshHandle < dragBoundsRect.left) ||
 (**windowPoshHandle > dragBoundsRect.right) ||
 (**windowPosvHandle > dragBoundsRect.bottom) ||
 (**windowPosvHandle < dragBoundsRect.top)) {
 
 **windowPoshHandle = (int) ((dragBoundsRect.right - dragBoundsRect.left)/2);
 **windowPosvHandle = (int) ((dragBoundsRect.bottom - dragBoundsRect.top)/2);
 }
 
 loadWindow = GetNewWindow(MYWINDOW, (Ptr) NIL, (Ptr) -1);
 MoveWindow(loadWindow, **windowPoshHandle, **windowPosvHandle, FALSE);
 ShowWindow(loadWindow);

 SetPort(loadWindow);
 PenNormal();

}

/* setUpMenus */

setUpMenus()
{
 int    i;
 
 deskMenu = GetMenu(DESK_ID);
 AddResMenu(deskMenu, ‘DRVR’);
 InsertMenu(deskMenu, 0);
 
 fileMenu = GetMenu(FILE_ID);
 InsertMenu(fileMenu, 0);
 
 editMenu = GetMenu(EDIT_ID);
 InsertMenu(editMenu, 0);
 
 configureMenu = GetMenu(CONFIGURE_ID);
 InsertMenu(configureMenu, 0);
 
 for (i = 0; i < 4; i++) {

 theConfiguration[i].ceiling.menuHandle = GetMenu(BASE_ID + i*3);
 InsertMenu(theConfiguration[i].ceiling.menuHandle, -1);
 CheckItem(theConfiguration[i].ceiling.menuHandle, (int) **theConfiguration[i].ceiling.valueHandle, 
TRUE);
 
 theConfiguration[i].interval.menuHandle = GetMenu(BASE_ID + i*3 + 1);
 InsertMenu(theConfiguration[i].interval.menuHandle, -1);
 CheckItem(theConfiguration[i].interval.menuHandle, (int) **theConfiguration[i].interval.valueHandle, 
TRUE);
 
 theConfiguration[i].width.menuHandle = GetMenu(BASE_ID + i*3 + 2);
 InsertMenu(theConfiguration[i].width.menuHandle, -1);
 CheckItem(theConfiguration[i].width.menuHandle, (int) **theConfiguration[i].width.valueHandle, 
TRUE);
 }
 
 DrawMenuBar();
}


/* doMenu */

doMenu(menuResult)
 long menuResult;
{
 int    menuID, itemNumber, newHPos;
 int    newVPos, itemHit, i, configSetting;
 long   longPointer;
 ProcPtrmyFilterProc;

/* if (menuResult == (long) NIL)
 menuResult = MenuChoice(); */
 
 menuID = HiWord(menuResult);
 itemNumber = LoWord(menuResult);
 
 switch(menuID)
 {
 case DESK_ID:   /*Apple Menu */
 if (itemNumber == 1){
 Alert(MYALERT, NIL);
 break;
 }
 else if (itemNumber == 0)/* b/c of nested menus */
 break;
 else
 {
 GetItem(deskMenu, itemNumber, &daName);
 GetPort(&theCurrentPort);
 OpenDeskAcc(&daName);
 SetPort(theCurrentPort);
 
 break;
 }
 break;
 
 case FILE_ID:   /*  the File Menu */
 switch(itemNumber)
 {
 case 1:/*the print command */
 break;
 case 2:/*the quit command*/
 quitTime();
 break;
 }
 break;
 
 case EDIT_ID:   /*the Edit Menu */
 switch(itemNumber){
 case 1:/*the undo command*/
 break;
 case 2:/*just dashed lines */
 break;
 case 3:/*the cut command */
 break;
 case 4:/*the copy command*/
 break;
 case 5:/*the paste command */
 break;
 case 6:/*the clear command */
 break;
 }
 break;
 
 case CONFIGURE_ID:/*the Configure Menu*/
 switch(itemNumber){
 case 1:/*Set Time:Heirarchical  */
 case 2:/*Average Over: Heirarchical */
 case 3:/*Set Width: Heirarchical  */
 break;
 case 4:/*spacer */
 break;
 case 5:/*Set Time:Heirarchical  */
 case 6:/*Average Over: Heirarchical */
 case 7:/*Set Width: Heirarchical  */
 break;
 case 8:/*spacer */
 break;
 case 9:/*Set Time:Heirarchical  */
 case 10: /*Average Over: Heirarchical */
 case 11: /*Set Width:    Heirarchical */
 break;
 case 12: /*spacer */
 break;
 case 13: /*Set Time:Heirarchical  */
 case 14: /*Average Over: Heirarchical */
 case 15: /*Set Width:    Heirarchical */
 break;
 case 16: /*spacer */
 break;
 case 17: /*compress juggler RAM...*/
 MFMaxMem(&longPointer);
 break;
 } /* end switch */
 break;
 
 case BASE_ID:
 case (BASE_ID + 3):
 case (BASE_ID + 6): 
 case (BASE_ID + 9):
 
 configSetting = ((menuID - BASE_ID)/3);
 CheckItem(theConfiguration[configSetting].ceiling.menuHandle, (int) 
**theConfiguration[configSetting].ceiling.valueHandle, FALSE);
 **theConfiguration[configSetting].ceiling.valueHandle = (SignedByte) 
itemNumber;
 CheckItem(theConfiguration[configSetting].ceiling.menuHandle, (int) 
**theConfiguration[configSetting].ceiling.valueHandle, TRUE);
 theConfiguration[configSetting].ceiling.value = CeilingSwitch(itemNumber);
 break;

 case (BASE_ID + 1):
 case (BASE_ID + 4):
 case (BASE_ID + 7): 
 case (BASE_ID + 10):
 configSetting = (menuID - BASE_ID - 1)/3;
 CheckItem(theConfiguration[configSetting].interval.menuHandle, (int) 
**theConfiguration[configSetting].interval.valueHandle, FALSE);
 **theConfiguration[configSetting].interval.valueHandle = (SignedByte) 
(itemNumber);
 CheckItem(theConfiguration[configSetting].interval.menuHandle, (int) 
**theConfiguration[configSetting].interval.valueHandle, TRUE);
 theConfiguration[configSetting].interval.value = IntervalSwitch(itemNumber);
 break;
 
 case (BASE_ID + 2):
 case (BASE_ID + 5):
 case (BASE_ID + 8): 
 case (BASE_ID + 11):
 configSetting = (menuID - BASE_ID - 2)/3;
 CheckItem(theConfiguration[configSetting].width.menuHandle, (int) **theConfiguration[configSetting].width.valueHandle, 
FALSE);
 **theConfiguration[configSetting].width.valueHandle = (SignedByte) itemNumber;
 CheckItem(theConfiguration[configSetting].width.menuHandle, (int) **theConfiguration[configSetting].width.valueHandle, 
TRUE);
 theConfiguration[configSetting].width.value = WidthSwitch(itemNumber);
 break;

 }
 HiliteMenu(0);
}

/* doLoad */
 
doLoad()
{
 long   currentTicks, deltaTicks, barHeight, barWidth; 
 long   leftEdge, rightEdge, topEdge, bottomEdge;
 Rect   loadBar, eraseBar;
 int    i;
 

 /*sample the load */
 currentTicks = TickCount();
 deltaTicks = currentTicks - ticksGoneBy;
 ticksGoneBy = currentTicks;

 /*increment accumulators and draw load*/
 for (i = 0; i < 4; i++) {
 tickSum[i] += deltaTicks;
 tickSumDivider[i]++;
 if (tickSum[i] > theConfiguration[i].interval.value) {
 
 leftEdge = 5 + (i * BAROFFSET);
 bottomEdge = 35;/*will become resizable     */
 topEdge = 0;
 barWidth = theConfiguration[i].width.value;
 
 barHeight = (40 - ((tickSum[i] * 35)/(tickSumDivider[i] * theConfiguration[i].ceiling.value)));
 /*eliminate decimals */
 SetRect (&loadBar, leftEdge, barHeight, leftEdge + barWidth, bottomEdge);
 SetRect (&eraseBar, leftEdge, topEdge, leftEdge + barWidth, barHeight);
 EraseRect(&eraseBar);
 PaintRect(&loadBar);
 
 tickSum[i] = 0;
 tickSumDivider[i] = 0;
 } /* endif */
 } /* end for  */
}

/* quitTime */

quitTime()
{/*time to write resources, etc.   */
 int    i;
 Point  p;
 
 for (i = 0; i < 4; i++) {
 ChangedResource(theConfiguration[i].ceiling.valueHandle);
 ChangedResource(theConfiguration[i].interval.valueHandle);
 ChangedResource(theConfiguration[i].width.valueHandle);
 }
 
 SetPort(loadWindow);
 
 p.h = 0;
 p.v = 0;
 
 LocalToGlobal(&p);
 
 **windowPoshHandle = p.h;
 **windowPosvHandle = p.v;

 ChangedResource((Handle) windowPosvHandle);
 ChangedResource((Handle) windowPoshHandle);
 
 ExitToShell();
}


/* main */

main()
{

 /*variables*/
 
 EventRecordtheEvent;
 Point  eventPt;
 char   eventChar;
 WindowPtrwhichWindow, eventWindow;
 short  windowCode, stillInGoAway, controlCode, whichItem,
 scrapIndex;
 
 setUpWorld();
 setUpMenus();
 
 
 while (TRUE) {
 WaitNextEvent(everyEvent, &theEvent, (long) NIL, (RgnHandle) NIL);
 switch (theEvent.what){
 case SUSPEND_RESUME_EVT:
 if ( ! BitTst( (Ptr) theEvent.message, SUSPEND_RESUME_BIT))   
 /*suspend event */
 ;
 else (BitTst( (Ptr) theEvent.message, SUSPEND_RESUME_BIT))    
 /*resume event  */
 ;
 if ( ! BitTst( (Ptr) theEvent.message, CLIPBOARD_BIT))        /*
 don’t convert clipboard  */
 ;
 else (BitTst( (Ptr) theEvent.message, CLIPBOARD_BIT))         /*
 convert clipboard */
 ;
 break;
 case mouseDown:
 eventPt = theEvent.where;
 windowCode = FindWindow(eventPt, &eventWindow);
 switch (windowCode) {
 case inSysWindow:
 SystemClick(&theEvent, eventWindow);
 break;
 case inContent: 
 /*won’t work on minor switch 
 b/c of compatibility issues w/Excel */
 case inGrow:
 case inGoAway:
 if (eventWindow == loadWindow)
 DragWindow(eventWindow, eventPt, &dragBoundsRect);
 break;
 case inDrag:
 DragWindow(eventWindow, eventPt, &dragBoundsRect);
 break;
 case inDesk:
 break;
 case inMenuBar:
 doMenu(MenuSelect(theEvent.where));
 break;
 default: 
 break;
 } /* end switch(windowCode)*/
 break;
 
 case keyDown:
 case autoKey:
 eventChar = theEvent.message;
 if (theEvent.modifiers & cmdKey) {
 doMenu(MenuKey(eventChar));
 HiliteMenu(0);
 }
 break;
 case activateEvt:
 break;
 case updateEvt: /*absolutely necessary*/
 BeginUpdate(theEvent.message);
 EndUpdate(theEvent.message);
 break;
 case nullEvent: 
 doLoad();
 break;
 default: ;
 } /* end of case theEvent.what */
 } /* while */
}


/*
 MultiFinder.h -- MultiFinder Interfaces

 C Interface to the Macintosh Libraries
 Copyright Apple Computer,Inc.1987
 All rights reserved.
*/

#ifndef __MULTIFIN__
#define __MULTIFIN__
#ifndef __EVENTS__
#endif
#ifndef __QUICKDRAW__
#endif


/* MultiFinder dispatch trap */

#define _OSDISPATCH0xA88F
#define _WAITNEXTEVENT    0xA860



/* Routine selector values */

#define mfMaxMemSel21
#define mfFreeMemSel 24
#define mfTempNewHandleSel29
#define mfTempHLockSel    30
#define mfTempHUnLockSel  31
#define mfTempDisposHandleSel 32


#define MFMaxMem(grow) \
 cMFMaxMem(grow,mfMaxMemSel)
 
#define MFFreeMem() \
 cMFFreeMem(mfFreeMemSel)
 
#define MFTempNewHandle(logicalSize,resultCode) \
 cMFTempNewHandle(logicalSize,resultCode,mfTempNewHandleSel)
 
#define MFTempHLock(h,resultCode) \
 cMFTempHLock(h,resultCode,mfTempHLockSel)
 
#define MFTempHUnLock(h,resultCode) \
 cMFTempHUnLock(h,resultCode,mfTempHUnLockSel)
 
#define MFTempDisposHandle(h,resultCode) \
 cMFTempDisposHandle(h,resultCode,mfTempDisposHandleSel)


/* WaitNextEvent was released in MPW 2.0 interfaces. It is
 commented out below to avoid a duplicate declaration.
 Uncomment only if working with earlier interfaces. */

pascal Boolean WaitNextEvent(mask,event,sleep,mouseRgn)
 unsigned short mask;
 EventRecord *event;
 unsigned long sleep;
 RgnHandle mouseRgn;
 {
 asm {
 SUBQ.L #2, SP
 MOVE.W 20(A6), -(SP)/* short mask */
 MOVE.L 16(A6), -(SP)/* long *event*/
 MOVE.L 12(A6), -(SP)/* long sleep */
 MOVE.L 8(A6), -(SP) /* long handle*/
 DC.W _WAITNEXTEVENT
 MOVE.W (SP)+, 22(A6)/* return stack pointer state*/
 }
 }

pascal long cMFMaxMem(grow, SW)
 long   *grow;
 short  SW;
 {
 asm{
 SUBQ.L #4, SP
 MOVE.L 10(A6), -(SP)/* long *grow */
 MOVE.W 8(A6), -(SP) /* short SW */
 DC.W _OSDISPATCH
 MOVE.L (SP)+, 14(A6)/* return stack pointer state */
 }
 }

pascal Size cMFFreeMem(SW)
 short  SW;
 {
 asm{
 DC.W _OSDISPATCH
 }
 }
 
pascal Handle cMFTempNewHandle(logicalSize, resultCode, SW)
 unsigned long logicalSize;
 short  *resultCode;
 short  SW;
 {
 asm{
 DC.W _OSDISPATCH
 }
 }
 
pascal void cMFTempHLock(h, resultCode, SW)
 Handle h;
 short  *resultCode;
 short  SW;
 {
 asm{
 DC.W _OSDISPATCH
 }
 }
 
pascal void cMFTempHUnLock(h, resultCode, SW)
 Handle h;
 short  *resultCode;
 short  SW;
 {
 asm{
 DC.W _OSDISPATCH
 }
 }
 
pascal void cMFTempDisposHandle(h, resultCode, SW)
 Handle h;
 short  *resultCode;
 short  SW;
 {
 asm{
 DC.W _OSDISPATCH
 }
 }
 

#endif


*Graphic Load Appl.R
*
GLA.rsrc
RSRCWAKA

Type WAKA = STR 
 ,0
Graphic Load Average \0D© by Peter Korn \0Dver 28 JAN 1988
 
Type FREF
,128
APPL 0

Type BNDL
,128
WAKA 0
ICN#
0 128
FREF 
0 128

TYPE WIND
BarWindow,128
load
40 350 80 400
Invisible NoGoAway
3
0

TYPE SIZE = GNRL
JugglerSize, -1
5800 0000 8000 0000 8000  ;; gotten by looking at Size = -1 resource 
as general

TYPE MENU
AppleMenu, 128
\14
About Load Average
(-

FileMenu, 129
File
(Print/P
Quit/Q

EditMenu, 130
Edit
(Undo/Z
(-
(Cut/X
(Copy/C
(Paste/P
(Clear/B

ConfigureMenu, 131
Configure
Tick ceiling/\1B!\C8
Interval time/\1B!\C9
Measurement width/\1B!\CA
(-
Tick ceiling/\1B!\CB
Interval time/\1B!\CC
Measurement width/\1B!\CD
(-
Tick ceiling/\1B!\CE
Interval time/\1B!\CF
Measurement width/\1B!\D0
(-
Tick ceiling/\1B!\D1
Interval time/\1B!\D2
Measurement width/\1B!\D3
(-
Compress juggler RAM

Ceiling1Menu, 200
Ceiling1
1
2
3
4
5
6
7
8
9
10
15
20
30
60

Interval1Menu, 201
Interval1
1
2
5
10
20
60
120
180
600
1200

Width1Menu, 202
Width1
1
2
3
4
5

Ceiling2Menu, 203
Ceiling2
1
2
3
4
5
6
7
8
9
10
15
20
30
60

Interval2Menu, 204
Interval2
1
2
5
10
20
60
120
180
600
1200

Width2Menu, 205
Width2
1
2
3
4
5

Ceiling3Menu, 206
Ceiling3
1
2
3
4
5
6
7
8
9
10
15
20
30
60

Interval3Menu, 207
Interval3
1
2
5
10
20
60
120
180
600
1200

Width3Menu, 208
Width3
1
2
3
4
5

Ceiling4Menu, 209
Ceiling4
1
2
3
4
5
6
7
8
9
10
15
20
30
60

Interval4Menu, 210
Interval4
1
2
5
10
20
60
120
180
600
1200

Width4Menu, 211
Width4
1
2
3
4
5

* Program Messages Dialog box...
type DLOG
 ,258
Program Messages
100 100 200 400
Visible NoGoAway
1
0
258

type DITL
 ,258
3
BtnItem Enabled
65 230 95 285
OK

StatText Disabled
15 60 85 222 
^0\0D^1\0D^2\0D^3

IconItem Disabled
10 10 42 42
1

TYPE INFO = GNRL
Window Pos h, 150
0060    ;; initial hor window position

Window Pos v, 151
0060    ;; initial ver window position

Ceiling1, 200
01 ;; initial Ceiling1 value

Interval1, 201
01 ;; initial Interval1 value

Width1, 202
05 ;; initial Width1 value

Ceiling2, 203
02 ;; initial Ceiling2 value

Interval2, 204
02 ;; initial Interval2 value

Width2, 205
05 ;; initial Width2 value

Ceiling3, 206
03 ;; initial Ceiling3 value

Interval3, 207
03 ;; initial Interval3 value

Width3, 208
05 ;; initial Width3 value

Ceiling4, 209
04 ;; initial Ceiling4 value

Interval4, 210
05 ;; initial Interval4 value

Width4, 211 
05 ;; initial Width4 value


TYPE ALRT
MyAlert, 128 (2)
79 139 192 450
128
4444

TYPE DITL
MyDialogList, 128 (2)
4
 ButtonItem Enabled
 81 127 101 177
 OK
 
 StatText Enabled
 8 78 28 228
 Graphic Load Average
 
 StatText Enabled
 28 78 48 228
 written by Peter Korn
 
 StatText Enabled
 53 9 72 294
 Portions of this code © Think Technologies

Type ICN# = GNRL
  ,128 (0)
.H
0000 0000 0000 0000 00FF FF80 0300 0060
0400 0010 0800 0008 1000 0004 1000 0004
2000 0002 2000 0002 2000 0002 2000 F002
2000 F002 2000 F002 2000 F002 2000 F002
21E0 F002 21E0 F002 21E0 F002 21E0 F0E2
21E0 F0E2 21E0 F0E2 21E0 F0E2 21E0 F0E2
11E0 F0E4 11E0 F0E4 09E0 F0E8 05E0 F0F0
03E0 F0E0 00FF FF80 0000 0000 0000 0000
*
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Coda 2.5.11 - One-window Web development...
Coda is a powerful Web editor that puts everything in one place. An editor. Terminal. CSS. Files. With Coda 2, we went beyond expectations. With loads of new, much-requested features, a few surprises... Read more
Bookends 12.5.7 - Reference management a...
Bookends is a full-featured bibliography/reference and information-management system for students and professionals. Access the power of Bookends directly from Mellel, Nisus Writer Pro, or MS Word (... Read more
Maya 2016 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
RapidWeaver 6.2.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
MacFamilyTree 7.5.2 - Create and explore...
MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more
Paragraphs 1.0.1 - Writing tool just for...
Paragraphs is an app just for writers. It was built for one thing and one thing only: writing. It gives you everything you need to create brilliant prose and does away with the rest. Everything in... Read more
BlueStacks App Player 0.9.21 - Run Andro...
BlueStacks App Player lets you run your Android apps fast and fullscreen on your Mac. Version 0.9.21: Note: Now requires OS X 10.8 or later running on a 64-bit Intel processor. Initial stable... Read more
Tweetbot 2.0.2 - Popular Twitter client....
Tweetbot is a full-featured OS X Twitter client with a lot of personality. Whether it's the meticulously-crafted interface, sounds and animation, or features like multiple timelines and column views... Read more
Apple iBooks Author 2.3 - Create and pub...
Apple iBooks Author helps you create and publish amazing Multi-Touch books for iPad. Now anyone can create stunning iBooks textbooks, cookbooks, history books, picture books, and more for iPad. All... Read more
NeoOffice 2014.12 - Mac-tailored, OpenOf...
NeoOffice is a complete office suite for OS X. With NeoOffice, users can view, edit, and save OpenOffice documents, PDF files, and most Microsoft Word, Excel, and PowerPoint documents. NeoOffice 3.x... Read more

Rage of Bahamut is Giving Almost All of...
The App Store isn't what it used to be back in 2012, so it's not unexpected to see some games changing their structures with the times. Now we can add Rage of Bahamut to that list with the recent announcement that the game is severely cutting back... | Read more »
Adventures of Pip (Games)
Adventures of Pip 1.0 Device: iOS iPhone Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: ** ONE WEEK ONLY — 66% OFF! *** “Adventures of Pip is a delightful little platformer full of charm, challenge and impeccable... | Read more »
Divide By Sheep - Tips, Tricks, and Stre...
Who would have thought splitting up sheep could be so involved? Anyone who’s played Divide by Sheep, that’s who! While we’re not about to give you complete solutions to everything (because that’s just cheating), we will happily give you some... | Read more »
NaturalMotion and Zynga Have Started Tea...
An official sequel to 2012's CSR Racing is officially on the way, with Zynga and NaturalMotion releasing a short teaser trailer to get everyone excited. Well, as excited as one can get from a trailer with no gameplay footage, anyway. [Read more] | Read more »
Grab a Friend and Pick up Overkill 3, Be...
Overkill 3 is a pretty enjoyable third-person shooter that was sort of begging for some online multiplayer. Fortunately the begging can stop, because its newest update has added an online co-op mode. [Read more] | Read more »
Scanner Pro's Newest Update Adds Au...
Scanner Pro is one of the most popular document scanning apps on iOS, thanks in no small part to its near-constant updates, I'm sure. Now we're up to update number six, and it adds some pretty handy new features. [Read more] | Read more »
Heroki (Games)
Heroki 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: CLEAR THE SKIES FOR A NEW HERO!The peaceful sky village of Levantia is in danger! The dastardly Dr. N. Forchin and his accomplice,... | Read more »
Wars of the Roses (Games)
Wars of the Roses 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: | Read more »
TapMon Battle (Games)
TapMon Battle 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: It's time to battle!Tap! Tap! Tap! Try tap a egg to hatch a Tapmon!Do a battle with another tapmons using your hatched tapmons! *... | Read more »
Alchemic Dungeons (Games)
Alchemic Dungeons 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: ### Release Event! ### 2.99$->0.99$ for limited time! ### Roguelike Role Playing Game! ### Alchemic Dungeons is roguelike... | Read more »

Price Scanner via MacPrices.net

Canon PIXMA MG3620 Wireless Inkjet All-in-One...
Canon U.S.A., Inc. has announced the PIXMA MG3620 Wireless (1) Inkjet All-in-One (AIO) printer for high-quality photo and document printing. Built with convenience in mind for the everyday home user... Read more
July 4th Holiday Weekend 13-inch MacBook Pro...
Save up to $150 on the purchase of a new 2015 13″ Retina MacBook Pro at the following resellers this weekend. Shipping is free with each model: 2.7GHz/128GB MSRP $1299 2.7GHz/... Read more
27-inch 3.5GHz 5K iMac on sale for $2149, sav...
Best Buy has the 27″ 3.5GHz 5K iMac on sale for $2149.99. Choose free shipping or free local store pickup (if available). Sale price for online orders only, in-store prices may vary. Their price is $... Read more
Apple now offering refurbished 2015 11-inch...
The Apple Store is now offering Apple Certified Refurbished 2015 11″ MacBook Airs as well as 13″ MacBook Airs (the latest models), available for up to $180 off the cost of new models. An Apple one-... Read more
15-inch 2.5GHz Retina MacBook Pro on sale for...
Amazon.com has the 15″ 2.5GHz Retina MacBook Pro on sale for $2274 including free shipping. Their price is $225 off MSRP, and it’s the lowest price available for this model. Read more
Finally Safe To Upgrade To Yosemite’?
The reason I’ve held back from upgrading my MacBook Air from OS X 10.9 Mavericks to 10.10 Yosemite for nearly a year isn’t just procrastination. Among other bugs reported, there have been persistent... Read more
Logo Pop Free Vector Logo Design App For OS X...
128bit Technologies has released of Logo Pop Free 1.2 for Mac OS X, a vector based, full-fledged, logo design app available exclusively on the Mac App Store for the agreeable price of absolutely free... Read more
21-inch 1.4GHz iMac on sale for $999, save $1...
B&H Photo has new 21″ 1.4GHz iMac on sale for $999 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Best Buy has the 21″ 1.4GHz iMac on sale for $999.99 on their... Read more
16GB iPad mini 3 on sale for $339, save $60
B&H Photo has the 16GB iPad mini 3 WiFi on sale for $339 including free shipping plus NY tax only. Their price is $60 off MSRP. Read more
Save up to $40 on iPad Air 2, NY tax only, fr...
B&H Photo has iPad Air 2s on sale for up to $40 off MSRP including free shipping plus NY sales tax only: - 16GB iPad Air 2 WiFi: $489 $10 off - 64GB iPad Air 2 WiFi: $559 $40 off - 128GB iPad Air... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Fulfillment Operations Execution Ana...
**Job Summary** The AMR Apple Fulfillment Operations Team is seeking a talented team player to drive the Apple Online Store (AOS) fulfillment performance to ensure a Read more
Localization Producer - *Apple* HR and Reta...
…project manager to support the Retail Globalization team. You will participate in Apple exponential inte ational growth and drive global project initiatives for the Read more
*Apple* Online Store UAT Lead - Apple (Unite...
**Job Summary** The Apple Online Store is a fast paced and ever evolving business environment. A UAT lead in this organization is able to have a direct impact on one of Read more
Senior Payments Security Manager - *Apple*...
**Job Summary** Apple , Inc. is looking for a highly motivated, innovative and hands-on senior payments security manager to join the Apple Pay security team. You will Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.