TweetFollow Us on Twitter

MultiFinder Icon Fix
Volume Number:6
Issue Number:3
Column Tag:Programmer's Workshop

Related Info: Menu Manager OS Utilities Quickdraw Resource Manager

Æeshete--a fix for MultiFinder

By David Dunham, Bellevue, WA

Introduction

I’ve enjoyed multitasking on my Macintosh since I discovered in 1984 that the Key Caps DA kept running in a background window. So I should be happy with MultiFinder, right? Well, almost. Besides multitasking, I like my Macintosh because it’s, well, pretty. From the cute startup icon to the zooming Open command in the Finder, there’s an attention to artistic detail. Except in MultiFinder. I had to keep staring at that ugly shrunken representation of an icon at the right of the menu bar.

It was pretty obvious why it was ugly -- QuickDraw was just scaling down a 32*32 icon to 16*16 pixels. Why couldn’t MultiFinder plot small icons (SICN resources) instead of shrinking icons? (I don’t know -- I did suggest this in my bug reports. Apparently, this will happen in System 7.0.)

Checking for Traps

Obviously I had to do something to make my Macintosh aesthetic again. But what? Presumably MultiFinder was drawing the ugly menu bar icon, or at least patching the Menu Manager to do its dirty work. But patching MultiFinder didn’t seem like a good approach. First of all, it would be a lot of work -- I’d probably have to disassemble it, and I neither own a disassembler nor enjoy reading undocumented code. Second, there are several versions of MultiFinder. Each time a new one came out, I’d have to figure out how to patch it. Perhaps the Menu Manager (as embodied in the MBDF and MDEF defprocs) was responsible for the ugly icons, but these resources also get revised with each System release, and weren’t good candidates for patching.

My next thought was to patch the _PlotIcon trap, which must be responsible for drawing the shrunken icons. I used a low-level debugger to determine that _PlotIcon was never in fact getting called. Oops. But that was OK, because _CopyBits was called. This wasn’t a great surprise, since _PlotIcon would have called _CopyBits in any case. Unfortunately, _CopyBits wasn’t a good trap to patch. It’s used all over the place to blit (bit block transfer) to the screen, so any overhead I added to it would slow down frequent operations such as drawing scroll bars and radio buttons. Further, its argument is a bitmap, rather than a handle to an icon, which would make it difficult to determine whether or not the bitmap was an application icon. Worse, there might be legitimate cases for plotting ugly shrunken icons -- I wanted my patch to fix MultiFinder, not break other programs.

Finally, it dawned on me. I could plot aesthetic icons by patching _DetachResource. _DetachResource? Yes, because the application’s resource file may not be accessible when the menu’s drawn. If you need resource data, but can’t guarantee that the resource will be available, the simplest thing would be to do

/* 1 */

 handle = GetResource(type, id);
 DetachResource(handle);

and then use the handle some time later (of course, you’d have to make sure your resource wasn’t purgeable, or call _HNoPurge on the handle). I hauled out the debugger again, and sure enough, MultiFinder was calling _DetachResource on ICN# resources.

This would be just the right trap to patch -- it’s not called too frequently, and probably never in time-critical loops. And its argument is a handle, which I could easily call _GetResInfo on to determine if it was an ICN#.

The Patch

So now that I knew how to identify where MultiFinder got the icons for the menu, I had to replace them with SICNs. I could have replaced them with different ICN#s, but ResEdit has a graphical SICN editor which I like using. And SICNs are just bitmap data, the 16*16 equivalent of the 32*32 ICN#s.

The patch is simple (see listing 1): whenever someone calls _DetachResource on an ICN#, replace the data with a doubled SICN; when it’s plotted, it’ll be shrunk down to 50% and look like a SICN again. Just which SICN I determine by getting the current application’s signature (I find its name with _PBGetFCBInfo, then the signature with _PBHGetFInfo), then looking it up in a list (the CRE# resource, discussed below). If I find the signature, and the ICN# is for the application, I replace the ICN# data. Note that I replace only the icon data. I don’t touch the mask, because it’s not used in drawing menu icons.

In an attempt to prevent replacing ICN#s which are being drawn by programs other than their owner, the test in the beginning makes sure the ICN# belongs to the current application. The ICN# used by Finder comes from the System file, so it doesn’t belong to any application. It is, however, identifiable as ICN# 3.

In the original version of the patch, I got the SICN and doubled it by _CopyBitsing it right into the ICN#.

/* 2 */

typedef struct {
 OSType creator;
 word   rsrcID;
} SIGNATURE;

/*
AESTHETIZE -- Change an ICN# resource (so it’s aesthetic when shrunken)
*/
void aesthetize(handle, sicn, creators)
Handle handle, sicn; SIGNATURE **creators;
{
 word   sicnNum = -1;
 word   id;
 OSType type;
 Str255 name;
 FCBPBRec fcb;
 HParamBlockRec  pb;
 OSErr  error;
 register word i;

 GetResInfo(handle, &id, &type, &name);
 if (type == ‘ICN#’) {
 /* Be sure ICN# is owned by this application (or may be System) */
 if (HomeResFile(handle) != CurApRefNum && id != 3) return;
 
 /* Get application’s name and directory */
 fcb.ioNamePtr = name;
 fcb.ioRefNum = CurApRefNum;
 fcb.ioFCBIndx = 0;/* Look up by ioRefNum */
 error = PBGetFCBInfo(&fcb, FALSE);
 
 /* Get application’s signature */
 pb.fileParam.ioNamePtr = fcb.ioNamePtr;
 pb.fileParam.ioVRefNum = fcb.ioFCBVRefNum;
 pb.fileParam.ioDirID = fcb.ioFCBParID;
 pb.fileParam.ioFVersNum = 0;
 pb.fileParam.ioFDirIndex = 0;
 error = PBHGetFInfo(&pb, FALSE);
 
 /* Scan our creator list */
 if (error == noErr) {
 for (i = 0; i < (GetHandleSize(creators)/6); i++) {
 if (pb.fileParam.ioFlFndrInfo.fdCreator ==
 (*creators)[i].creator) {
 if (id == (*creators)[i].rsrcID) sicnNum = i;
 break;
 }
 }
 }
 if (sicnNum == -1) return;
 /* Replace the original ICN# */
 BlockMove(*sicn + (sicnNum << 7), *handle, 128L);
 }
}

«listing 1»

Gotchas

There’s a very important appendix in Inside Macintosh, which lists the traps that may move memory. Traps not listed here are guaranteed not to move memory -- you can pass them dereferenced handles, call them at interrupt time, or whatever. Unfortunately, I neglected to notice that _DetachResource was not on the list, but that _CopyBits was (or more truthfully, I forgot to check the list). Actually, _CopyBits doesn’t usually move memory -- except when the source and destination bitmaps are a different size.

Only one program broke because I’d inadvertently made _DetachResource move memory, but even one was too many -- I rely too much on certain traps not moving memory to give anyone else grief for doing the same. So I moved the doubling to startup time.

There was one small problem with calling a QuickDraw trap like _CopyBits at startup time -- QuickDraw isn’t fully initialized. So my doubleSICN (see listing 2) routine opens a new GrafPort and does the _CopyBitsing in it.

/* 3 */

/*
Get and double the size of the SICNs
*/
Handle double_SICN() {
 register Handle sicn, bigSicn;
 register word sicnNum;
 Size   size, bigSize;
 BitMap sicnBits, icnBits;
 GrafPtrsavePort;
 GrafPort port;

 sicn = GetResource(‘SICN’, 0);
 /* Get enough space */
 size = GetHandleSize(sicn);
 bigSize = size * 4;
asm {
 move.l bigSize,D0
 _NewHandle SYS  ; put it in system heap
 move.l A0,bigSicn
}
 GetPort(&savePort);
 OpenPort(&port);/* Give a valid QuickDraw environment */
 SetPort(&port);
 HLock(sicn);
 HLock(bigSicn);
 for (sicnNum = (size >> 5) - 1; sicnNum >= 0; sicnNum--) {
 /* Set up the bit map */
 sicnBits.rowBytes = 2;
 SetRect(&sicnBits.bounds, 0, 0, 16, 16);
 sicnBits.baseAddr = *sicn + (sicnNum << 5);
 icnBits.rowBytes = 4;
 SetRect(&icnBits.bounds,0,0,32,32);
 icnBits.baseAddr = *bigSicn + (sicnNum << 7);
 CopyBits(&sicnBits, &icnBits, &sicnBits.bounds,
 &icnBits.bounds, srcCopy, NIL);
 }
 HUnlock(sicn);
 HUnlock(bigSicn);
 ClosePort(&port);
 SetPort(savePort);
 return(bigSicn);
}

«listing 2»

Testing

The problem with writing INITs is that you have to keep rebooting to test them. However, most of this INIT is a patch to _DetachResource, and patches can be tested within an application. TestAesthete is such an application; it installs the patch when it starts, and restores the original trap address when it quits. Its About dialog plots an ICN# before and after _DetachResourceing it. It’s a straightforward Mac application (see listing 3), so I won’t discuss it.

The only problem with testing patches is that you really don’t want them to be around when you leave your test program, even if you had to _ExitToShell with a debugger. Since the trap table still points to the patch code in your heap, you’ll crash sooner or later. There is however a low memory global, IAZGlobal, which contains the address of a routine to call before initializing a heap zone; _ExitToShell calls it. With a cleanup routine stuffed in the global, the patch never lasts longer than the program.

Is using a low memory global dangerous for future compatibility? Probably; IAZNotify was stricken from Tech Note 64. It really doesn’t matter here, since it’s only in the test bed, not the final INIT.

/* 4 */

/*
Test the DetachResource patch
*/
#define global
#include “aesthete.h”

#define IAZGlobal((long *)0x33C)

#define appleMenu1
#define fileMenu 2
#define editMenu 3
#define LAST_MENU3

CursHandlewatch;
MenuHandlemenus[LAST_MENU];
word    myRef;   /* refNum of my resource file */
long    oldTrap;
long    oldIAZ;  /* Save old IAZNotify */
/*
MAIN
*/
void main() {
 WindowPtrwhichWindow;
 word   code;
 char   c;
 Point  pt;
 EventRecordevent;

 oldIAZ = *IAZGlobal;
 *IAZGlobal = (long)notify; /* Install our routine */
 initialize();
 InitCursor();   /* Back to arrow */ 
 while (TRUE) {  /* Main event loop */
 SystemTask();
 if (GetNextEvent(everyEvent,&event))
 switch(event.what) {
   case mouseDown:
 code = FindWindow(event.where, &whichWindow);
 switch (code) {
   case inMenuBar:
 do_command(MenuSelect(event.where));
 break;
   case inSysWindow: /* Cursor in system window */
 SystemClick(&event,whichWindow);
 break;
 } /* end switch code */
 break;
   case keyDown:
   case autoKey:
 c = event.message & 255;
 if (event.modifiers & 256)
 do_command(MenuKey(c));
   break;
 } /* end switch */
 } /* end while */
}

/*
ABOUT - Show info about this program
*/
void about() {
 register WindowPtrwindow;
 WindowRecord    wbuf;
 EventRecordevent;
 Rect   bounds;
 RGBColor colour;
 Handle icon;
 
 window = GetNewWindow(128, &wbuf, -1L);
 SetPort(window);
 colour.red = 39321;
 colour.green = 26214;
 colour.blue = 0;
 RGBForeColor(&colour);
 TextFace(0);
 TextFont(newYork);
 TextSize(12);
 show_string(140,85,3);
 icon = GetResource(‘ICN#’, 128);
 SetRect(&bounds, 10, 10, 42, 42);
 PlotIcon(&bounds, icon);
 DetachResource(icon);
 SetRect(&bounds, 110, 10, 142, 42);
 PlotIcon(&bounds, icon);
 SetRect(&bounds, 210, 10, 226, 26);
 PlotIcon(&bounds, icon);
 while (TRUE) {
 SystemTask();   /* Keep clock ticking */
 if (GetNextEvent(mDownMask | keyDownMask, &event)) break;
 }
 CloseWindow(window);
}

/*
CLOSE_ALL - Close all desk accessories
*/
void close_all() {
 register WindowPeek w, v;

 w = (WindowPeek)FrontWindow();
 while (w != NIL) {/* Look at each window */
 v = w->nextWindow;/* Remember next */
 InitCursor();   /* Normal cursor for MockWrite */
 CloseDeskAcc(w->windowKind);
 SetCursor(*watch);
 w = v; /* Look at next one */
 }
}

/*
DO_COMMAND - Do something from the menu bar
*/
void do_command(result) unsigned long result; {
 register word menu, item, i;
 char   name[64];

 menu = result >> 16;
 item = result;
 switch (menu) {
   case appleMenu:
 if (item == 1) {/* About  */
 about();
 break;
 }
 GetItem(menus[appleMenu - 1], item, name);
 OpenDeskAcc(name);
 break;
   case fileMenu:
 switch (item) {
   case 4:/* Quit */
 SetCursor(*watch);
 close_all();
 ExitToShell();  /* IAZNotify restores */
 /*  original _DetachResource */
 }
 break;
   case editMenu:
 SystemEdit(item - 1);
 break;
 }
 HiliteMenu(0);  /* Make sure menu bar isn’t highlighted*/
}

/*
INITIALIZE
*/
void initialize() {
 InitGraf(&thePort);
 watch = GetCursor(4);    /* Get watch from resource */
 SetCursor(*watch);
 FlushEvents(everyEvent, 0);
 InitFonts();
 InitWindows();
 InitDialogs(0L);/* No disaster function */
 TEInit();
 MaxApplZone();  /* Pre-grow */
 setup_menus();
 install_patch();
}

/*
INSTALL_PATCH -- Install patch (in Application Heap for now)
*/
void install_patch() {
register word  saveRef, first, numTypes;
Handle  sicn, creators;
OSErr   error;

asm {
 bra    @around
 dc.l 0 ; for oldTrap
 dc.l 0 ; for creators handle
 dc.l 0 ; for sicn handle
#define oCreators-8
#define oSicn    -4
myPatch:
 lea    @myPatch,A0
 move.l oCreators(A0),-(SP)
 move.l oSicn(A0),-(SP)
 move.l 12(SP),A0; original parameter to _DetachResource
 move.l A0,-(SP)
 jsr    aesthetize ; aesthetize(rsrc,sicn,creators)
 add.l  #12,SP
 move.l oldTrap,A0
 jmp    (A0); pass call to original _DetachResource
 _Debugger; should never get here!
around:
 move.w #0xA992,D0
 _GetTrapAddress
 move.l A0,oldTrap ; save original trap address
 lea    @myPatch,A0
 move.w #0xA992,D0
 _SetTrapAddress ; install our patch
}
 sicn = double_SICN();
 creators = GetResource(‘SIG#’,0);
 DetachResource(creators);
asm {
 lea    @myPatch,A0
 move.l sicn,oSicn(A0)    ; stash it
 move.l creators,oCreators(A0); stash it
}
}

/*
KILL_PATCH -- Unpatch our mods to _DetachResource
*/
void kill_patch () {
asm {
 move.l oldTrap,A0 ; global holds old address
 move.w #0xA992,D0
 _SetTrapAddress ; everything’s back to original state
}
}

/*
NOTIFY - Clean things up if we _ExitToShell from debugger
 This routine gets called from our own _ExitToShell too
*/
void notify() {
 long trap;
 Handle h;
 
 *IAZGlobal = oldIAZ;
 kill_patch();   /* Restore original _DetachResource */
}

/*
SETUP_MENUS - Set up menus
*/
void setup_menus() {
 register word i;
 register Handle handle;
 long   type;
 word   code;
 char   string[256];

 InitMenus();
 for (i = 0; i < LAST_MENU; ) menus[i] = GetMenu(++i);
 AddResMenu(menus[appleMenu - 1], ‘DRVR’);
 for (i = 0; i < LAST_MENU; i++) {
 InsertMenu(menus[i], 0);
 }
 DrawMenuBar();
}

/*
SHOW_STRING
*/
void show_string(h, v, rsrc) word h, v, rsrc; {
 register StringHandle  s;

 MoveTo(h, v);
 s = GetString(rsrc);
 HLock(s);
 DrawString(*s);
 HUnlock(s);
}

«listing 3»

The INIT

Once the patch worked, it was simple to write an INIT that installed it (see listing 4). I follow the convention that the user can disable the INIT by holding the [Shift] key at startup time, and show an icon using Paul Mercer’s routines (here encapsulated into the code resource SHOW 0). I then blow my SICNs up to the size of ICN#s, detach my list of creators from the resource map (before installing my _DetachResource patch, you’ll notice), then install my patch into the trap table.

I find the resource I’m in by doing GetResource(‘INIT’,0) which works, but means the INIT can’t be renumbered. It would have been better to use RecoverHandle(main). It’s too late for that now, and I want to point out that not all INITs can be renumbered (if, for example, you wanted to combine several into one file).

When you compile the INIT, be sure that it’s nonpurgeable, and loaded into the system heap.

/* 5 */

/*
Install the DetachResource patch
*/
void main() {
 Handle handle;
 Handle sicn, creators;

asm {
 btst   #0,0x17B ; check KeyMap+7 -- is [Shift] key down?
 bne    @noInstall ; yes -- don’t install patch
}
 handle = GetResource(‘INIT’, 0);
 DetachResource(handle);  /* Detach ourselves */
 handle = GetResource(‘SHOW’, 0);  /* Get a handle to PROC */
 if (handle != 0L) { /* Loaded OK */
 HLock(handle);  /* Hold down the PROC */
 CallPascal(128, -1, *handle);/* ShowICON() */
 HUnlock(handle);/* Let it float in the heap again */
 }
asm {
 bra    @around
 dc.l 0 ; for oldTrap
 dc.l 0 ; for creators handle
 dc.l 0 ; for sicn handle
#define oldTrap  -12
#define oCreators-8
#define oSicn    -4
myPatch:
 lea    @myPatch,A0
 move.l oCreators(A0),-(SP)
 move.l oSicn(A0),-(SP)
 move.l 12(SP),A0; original parameter to _DetachResource
 move.l A0,-(SP)
 jsr    aesthetize ; aesthetize(rsrc,sicn,creators)
 add.l  #12,SP
 lea    @myPatch,A0
 move.l oldTrap(A0),A0
 jmp    (A0); pass call to original _DetachResource
 _Debugger; should never get here!
}

/* 6 */

asm {
around:
}
 sicn = double_SICN();    /* Get double-size sicns */
 creators = GetResource(‘SIG#’, 0);
 DetachResource(creators);

/* 7 */

asm {
 move.w #0xA992,D0
 _GetTrapAddress
 lea    @myPatch,A1
 move.l A0,oldTrap(A1)    ; save original trap address
 lea    @myPatch,A0
 move.w #0xA992,D0
 _SetTrapAddress ; install our patch
 lea    @myPatch,A0
 move.l sicn,oSicn(A0)    ; stash it
 move.l creators,oCreators(A0)
noInstall:
}
}

«listing 4»

Wrapping things up

I’ve included an edited listing of the important resources (see listing 5). Personally, I create them all with ResEdit. Note that any file, not just applications, can have a unique icon by including the various bundle resources; just make sure the Bundle bit’s set.

Why the name? If someone who’s athletic is an athlete, someone aesthetic must be an aesthete

/* 8 */

/* Aesthete resources */
resource ‘SICN’ (0, sysheap) {
 { /* array: 29 elements */
 }
};
resource ‘ICN#’ (128, purgeable) {
 /* Our Finder icon (also used at startup) */
};
resource ‘FREF’ (128) {
 ‘INIT’,
 0,
 “”
};

resource ‘BNDL’ (128) {
 ‘æsth’,
 0,
 { /* array TypeArray: 2 elements */
 ‘ICN#’,
 {
 0, 128
 },
 ‘FREF’,
 {
 0, 128
 }
 }
};
data ‘æsth’ (0, purgeable) {/* Signature resource */
 $”2D A9 31 39 38 39 20 44 61 76 69 64 20 44 75 6E”
 $”68 61 6D 0D 43 6F 72 72 65 63 74 73 20 4D 75 6C”
 $”74 69 46 69 6E 64 65 72 20 69 63 6F 6E 73"
};
data ‘sysz’ (0, purgeable) {/* Reserve system heap */
 $”00 00 0C 90"
};
resource ‘cicn’ (128) {
 /* The color icon displayed at startup */
};
data ‘TMPL’ (6001, “SIG#”, purgeable) {
 $”05 2A 2A 2A 2A 2A 4C 53 54 42 07 43 72 65 61 74"
 $”6F 72 54 4E 41 4D 07 49 43 4E 23 20 69 64 44 57"
 $”52 44 05 2A 2A 2A 2A 2A 4C 53 54 45"
};
data ‘SIG#’ (0, sysheap) {/* Application signature list */
 $”4D 41 43 53 00 03 41 43 54 41 00 80 41 43 54 41"
 $”01 02 57 49 4C 44 00 80 52 53 45 44 00 80 54 57"
 $”4B 53 00 80 47 45 4F 4C 00 80 6D 61 63 73 00 80"
 $”52 65 64 78 00 80 53 49 54 21 00 80 70 72 6D 74"
 $”00 80 54 52 50 5A 00 80 44 46 42 4F 00 80 53 70"
 $”69 6E 00 80 4D 57 49 49 03 E8 41 4C 50 45 00 80"
 $”58 43 45 4C 00 80 53 4F 4C 49 00 80 50 45 52 4C”
 $”00 80 51 45 44 31 00 80 44 5A 54 34 00 80 44 4D”
 $”4F 56 00 80 46 45 44 2B 00 80 74 74 78 74 00 80"
 $”41 52 54 5A 00 80 53 50 4E 54 00 80 43 52 50 52"
 $”00 80 4D 50 4E 54 00 80 4D 53 57 44 00 80"
};

«listing 5»

Appendix: Extending Aesthete

If you’d like to add your own SICNs to Aesthete, you’ll need to add the small icon to SICN 0. Open it in ResEdit, choose a small icon near the end, and choose New (or Duplicate). You can Paste a picture from the clipboard, if you’ve drawn it in another program.

You’ll also have to add an entry in the SIG# 0 resource. Aesthete as distributed contains a ResEdit TMPL, so you can easily edit this. Find the place you added your icon, select the “*****,” and choose New. The entries are the application’s signature, and the id of its ICN# (usually 128).

Note that it’s possible for two versions of an application to use two different ICN#s.

If you make a lot of additions, you may want to increase the amount of space Aesthete reserves at boot time by editing sysz 0.

David Dunham

10635 NE 29th #119

Bellevue, WA 98004-2007

 
AAPL
$423.00
Apple Inc.
+0.00
MSFT
$34.59
Microsoft Corpora
+0.00
GOOG
$900.68
Google Inc.
+0.00

MacTech Search:
Community Search:

Software Updates via MacUpdate

Default Folder X 4.5.9 - Enhances Open a...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click... Read more
The Cave 1.0.1 - Adventure game featurin...
The Cave is an adventure game that offers a unique blend of fast-paced action, mind-bending puzzles, and winning humor. Assemble your team and embark on a journey into the shadowy underworld. Once... Read more
Apple Java 2013-004 - For OS X 10.7 and...
Apple Java for OS X 2013-004 supersedes all previous versions of Java for OS X. This release updates the Apple-provided system Java SE 6 to version 1.6.0_51 and is for OS X versions 10.7 or later.... Read more
Google Chrome 27.0.1453.116 - Modern and...
Google Chrome is a Web browser by Google, created to be a modern platform for Web pages and applications. It utilizes very fast loading of Web pages and has a V8 engine, which is a custom built... Read more
EarthDesk 6.2 - Striking animated image...
EarthDesk replaces your static desktop picture with a rendered image of Earth showing correct sun, moon and city illumination. With an Internet connection, EarthDesk displays near real-time global... Read more
Apple Configurator 1.3 - Configure and d...
Apple Configurator makes it easy for anyone to mass configure and deploy iPhone, iPad, and iPod touch in a school, business, or institution. Three simple workflows let you prepare new iOS devices... Read more
Apple Java for Mac OS X 10.6 Update 16 -...
Apple Java for Mac OS X 10.6 Update 16 delivers improved security, reliability, and compatibility by updating Java SE 6 to 1.6.0_51.Version Update 16: See http://support.apple.com/kb/HT5744 for more... Read more
Neat 4.0.3 - Digital filing system for r...
Neat (formerly NeatWorks) is a powerful scanning and digital filing system that enables you to scan and organize receipts, business cards, and documents. Unlike other scanning software, NeatWorks... Read more
Adobe Muse CC 5.0 - Design and publish H...
Adobe Muse enables designers to create websites as easily as creating a layout for print. Design and publish original HTML pages using the latest Web standards, and without writing code. Now in beta... Read more
Adobe Creative Cloud 1.0 - Everything ne...
Adobe Creative Cloud costs $49.99/month (or less if you're a previous Creative Suite customer). Creative Suite 6 is still available for purchase (without a monthly plan) if you prefer. Introducing... Read more

Latest Forum Discussions

See All

Calendars+ by Readdle Goes Free For A Ve...
Calendars+ by Readdle Goes Free For A Very Limited Time Posted by Andrew Stevens on June 19th, 2013 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Modern Combat 4: Zero Hour Has A Meltdow...
Modern Combat 4: Zero Hour Has A Meltdown, Gets New Maps, Multiplayer Modes, and More Posted by Andrew Stevens on June 19th, 2013 [ permalink ] | Read more »
XCOM: Enemy Unknown – Commander’s Log: H...
Part of the series 148Apps Goes Deep on XCOM: Enemy Unknown I’m still haunted by visions of a parallel world (classified as Xbox 360) as it wasn’t long ago that I was in charge of the XCOM project and led a squadron of soldiers against an alien... | Read more »
Rovio Stars: The Angry Birds’ New Publis...
Rovio Entertainment, creators of Angry Birds, has a new publishing initiative called Rovio Stars that will see its first titles Icebreaker and Tiny Thief released soon. Kalle Kaivola, Senior Vice President of Product & Publishing at Rovio... | Read more »
Favorite Four: Soccer Games
As a soccer fan, I’m getting twitchy. The Confederations Cup might be helping a little, but I miss the English Premier League week in, week out. This is where I sink time into FIFA 13 on my console in order to counteract the problem. What about... | Read more »
Knights of Pen & Paper Adds More Dun...
Knights of Pen & Paper Adds More Dungeons and Loot In Free Update Posted by Andrew Stevens on June 19th, 2013 [ permalink ] | Read more »
Froot ‘n’ Nutz Review
Froot ‘n’ Nutz Review By Blake Grundman on June 19th, 2013 Our Rating: :: VISUALLY DICEYUniversal App - Designed for iPhone and iPad While Froot ‘n’ Nutz may not look very modern, it is very likable.   | Read more »
148Apps Goes Deep on XCOM: Enemy Unknown
XCOM: Enemy Unknown will be released tonight for iPad and iPhone. And we’re very excited. While XCOM isn’t the first console game to be ported over to iOS, it is one of the most ambitious. XCOM: Enemy Unknown while first released for XBox 360 and... | Read more »
A Cautionary Tail – An Interactive Book...
A Cautionary Tail – An Interactive Book That Teaches Self-Acceptance Posted by Andrew Stevens on June 19th, 2013 [ permalink ] | Read more »
XCOM: Enemy Unknown – Cheats, Tips, and...
The X-Com series, particularly the earlier games, are notoriously unforgiving. Although while XCOM: Enemy Unknown has been modernized, and is therefore more player friendly, it’s no slouch either. In fact, even on the Normal difficulty there’s a... | Read more »

Price Scanner via MacPrices.net

Smaller Tablets Forecast To Get Even More Popular...
The DisplaySearch Blog’s Richard Shim notes that tablet PCs with screen sizes smaller than 9 inches are currently forecast to account for 66% of tablet PC shipments for the year but that share is... Read more
Updated iPad Price Trackers
We’ve updated our iPad Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers. Read more
Apple refurbished iPod nanos available for $99
The Apple Store has Apple Certified Refurbished 16GB iPod nanos available for $99 including free shipping and Apple’s standard one-year warranty. That’s $50 off the cost of new nanos. All colors are... Read more
iFixIt Tears Down mid-2013 11.6-inch MacBook Air
iFixIt Chief Information Architect Miroslav Djuric says: The epic week of disassembly continues: Today, the MacBook Air 11″ found its way onto our teardown table and was soon just another Apple in... Read more
Mature Consumers Know When They Need a PC
Tech.Pinions’ Ben Bajarin sensibly observes that one of the fundamental characteristics of a mature market is mature consumers – mature in the sense that they know what they want and more importantly... Read more
Windows 8 Continues Ascension in User Popularity R...
Softpedia’s Bogdan Popa notes that Windows 8 is now the fourth most popular operating system in the world, and according to some new statistics, it continues to gain new users every day. Popa cites... Read more
Apple iOS and OS X Updates Put Bluetooth Smart Rea...
From its Worldwide Developers Conference last week, Apple announced unprecedented integration of Bluetooth technology into its operating systems – a move that sets the bar for Bluetooth integration... Read more
Buy a 13″ MacBook Pro, get AppleCare for as little...
Adorama has 13″ MacBook Pros bundled with 3-year AppleCare Protection Plans for as little as $40 extra (AppleCare has an MSRP of $249 for 13-inch MacBook Pros). Shipping is free, and Adorama charges... Read more
Updated MacBook Price Trackers
We’ve updated our MacBook Price Trackers with the latest information on prices, bundles, and availability on MacBook Airs, MacBook Pros, and the MacBook Pros with Retina Displays from Apple’s... Read more
Save $140 on the 15″ 2.3GHz MacBook Pro
B&H Photo has the 15″ 2.3GHz MacBook Pro on sale for $1659 including free shipping. Their price is $140 off MSRP. B&H will include free copies of Parallels Desktop, Bento Database, and LoJack... Read more

Jobs Board

*Apple* At-Home Team Manager - Apple (U...
Changing the world is all in a day's work at Apple . If you love innovation, here's your chance to make a career of it. You'll work hard. But the job comes with more than Read more
*Apple* Retail - Manager - Apple (Unite...
Job SummaryKeeping an Apple Store thriving requires a diverse set of leadership skills, and as a Manager, youre a master of them all. In the stores fast-paced, dynamic Read more
*Apple* - Solution Architect - CompuCom...
Job Location: US-TX-Dallas Posted Date: 4/18/2013 Overview: The Apple Solution Architect (SA) will be responsible for supporting pre-sales and post-sales solutions in Read more
*Apple* Support Technician; Mid-level -...
A Kforce client in Washington, DC area is seeking an Apple Support Technician. This contractor will have the following types of responsibilities including, but not Read more
Systems Engineer - *Apple* TV - Apple...
Job Summary The Apple TV team is looking for an experienced engineer with a passion for delivering first in class home entertainment solutions. The individual must be Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.