TweetFollow Us on Twitter

Drivers
Volume Number:7
Issue Number:7
Column Tag:C Workshop

Related Info: Device Manager Menu Manager

Addicted to DRiVeRs

By Dan Green, Manassas, VA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Evolution of a Desk Accessory or Addicted to DRiVeRs

MenuMem is the name that I have given to a desk accessory that I created one weekend back in ’87. It’s function is to display the amount of free memory (in the application heap) in the menu bar (in K). Sort of like the way those clock INITs keep the time in the menu bar.

A Brief History

Actually, when I first came up with the idea, I wanted to make an INIT, but the only way that I could think of to update the menu was to install a VBL task. Since the Memory Manger needs to be called, a VBL task is out of the question. A desk accessory seemed like a nice compromise since it would enable the addition of menu items.

This worked fine. As long as you didn’t mind choosing the desk accessory every time you opened a new application. After a few seconds I realized that if a trap was patched when the DA received a goodbye kiss, the DA could be opened back up again when a new application is started. Which one to patch? SystemTask seemed a logical choice. This way, the DA would never open up in an application that didn’t support DAs.

The code went something like this:

 case goodBye:
 .
 .
 .
 INSTALLPATCH()
 break;

where ThePatch() and INSTALLPATCH() are two assembly language routines, the code for which is given below (in MPW Asm).

;1

;   ThePatch() 
SystemTask      EQU     $1B4


ThePatch        PROC     EXPORT
STRING ASIS
ENTRY(StartSize,EndSize,DAName,OldTrapAddr) : CODE

StartSize
 subq   #2,A7  ; open the DA back up
 pea    DAName 
 _OpenDeskAcc
 addq   #2,A7
 move.w #SystemTask,D0
 move.l OldTrapAddr(PC),A0
 _SetTrapAddress ; remove our patch
 lea    ThePatch(PC),A0
 _DisposPtr ; deallocate our memory
 move.l OldTrapAddr(PC),A0
 jmp    (A0); and call SystemTask

OldTrapAddr         DC.L    0

; DAs begin with a NULL char so there are 8 total
DAName         DC.W    $0800
               DC.B    ‘MenuMem’
               ALIGN   2
EndSize
 ENDP

;   INSTALLPATCH() 
InstallPatch   PROC     EXPORT
STRING ASIS

PatchSize EQU     -6
NewTrapAddress EQU     -4

 link        A6,#-6
 move.l #(EndSize-StartSize),D0  move.wD0,Size(A6)
 _NewPtr,sys        ; Need patch in System Heap
 move.l A0,NewTrapAddress(A6)
 beq.s  Return
 move.w #SystemTask,D0     ; D0 = TrapNum
 _GetTrapAddress
 lea    OldTrapAddr(PC),A1
 move.l A0,(A1)+           ; Patch routine
 move.w PatchSize(A6),D0
 move.l NewTrapAddress(A6),A1 ; A1 = DstPtr
 lea    ThePatch(PC),A0       ; A0 = SrcPtr
 _BlockMove
 move.w #SystemTask,D0
 move.l NewTrapAddress(A6),A0
 _SetTrapAddress 

Return
 unlk   A6
 rts       ; Back to C Land

 ENDP

The Problem

This worked exactly as desired until MultiFinder appeared. When opened under MultiFinder, MenuMem acted like every other DA. That is, it would open up within the DA handler (unless the option key was held down when it was opened. In that case, like all other DAs, it would open up within the application running in the foreground). But when I switched to another application, no menu. Even worse, if I tried to open MenuMem again from within another application, MultiFinder would automatically switch me to the layer it was currently opened in. Furthermore, since MultiFinder keeps track of any patches that are installed by the application, and removes them when you switch to another one, my patch would never be called, and a small block of memory in the System Heap would be lost. This was totally unacceptable.

The Solution

The answer lies in opening the DA up before MultiFinder is ever run, and repeatedly making control calls to allow it to update it’s menu. If opened at startup time, there is an added advantage: No more trips to the Apple Menu. This approach is also perhaps somewhat simpler as there are no traps to patch, and no Assembly is required. You’ll still need two AA batteries though (Couldn’t resist). Of course, the internals of the DA have changed somewhat. It is now written in THINKC (The previous version was written in MPW C), and uses only 2 global variables.

Before I discuss the structure of the latest (and hopefully last) version of MenuMem I would like to mention a few words about THINKC. My advice for the few people out there who don’t have THINKC? GET IT. It is an absolutely wonderful development environment. Especially if you are into creating Desk Accessories or code resources (FKEYs, INITs, XCMDs, XFCNs etc.).

Since the beginning of time (January 1, 1904 to be exact) the creation of code resources in HLLs such as FORTRAN or Pascal has been hindered by the fact that no global variables were allowed (Most Mac compilers generate code that accesses global variables indirectly using register A5. Read the Memory and Segment Loader chapters of Inside Macintosh for more details on this). The more creative compiler writers have developed various implementations to allow the declaration of global variables in code resources, including appending globals to the end of the code. THINKC accesses global variables in code resources indirectly from register A4. In addition, if you are creating a device DRiVeR, THINKC will dynamically allocate a handle for your globals and store it in the dCtlStorage field of the DRiVeR’s device control entry, lock it, and dereference it into register A4. Though it is not stated in the manual, it is important to note that THINKC allocates this memory by calling GetResource(‘DATA’, drvrID), where drvrID is the owned resource ID of the DRiVeR, and then passing the returned handle to DetachResource() to remove it from the resource map. So if you need your globals to stick around between applications, you can use ResEdit to set the System Heap attribute.

Below is a description of the program and all of the routines, followed by the source code. This code is written under the assumption that the INIT, DRVR and DATA resources will be in the resource fork of the same file.

MenuMem: The INIT

pascal void main()

The INIT portion of MenuMem needs to load in and open the DRiVeR portion. However, if there is a resource of type ‘DRVR’ with the same ID as ours in the System file, or a DRiVeR with the same ID as ours is already installed in the Unit Table (but possibly not in the System file, just like MenuMem), then both our DRVR and DATA resources need to be renumbered. If the MenuMem DRiVeR can’t be loaded, then a beep will be heard during startup.

LoadDRVR(theName, theID)

This function calls GetResource(‘DRVR’, theID) to load MenuMem DRVR into memory. The System Heap attribute of both this and the associated DATA resource should be set to insure that both will hang around when the application heap is re-initialized. If the DRiVeR is loaded OK then DetachResource() is called to insure that the code will hang around when the resource fork of this startup document is closed. NOTE: THINKC detaches the associated ‘DATA’ from the resource fork automatically, so the global variables that are needed will be fine as long as the System Heap attribute is set. If the DRiVeR is opened successfully, the function returns noErr as its result.

Boolean DRVRXistsP(theID)

This function will return TRUE if a DRiVeR with a resource ID = theID exists in the resource fork of the System file or if one with the same ID has already been installed in the Unit Table (sort of like a librarian for DRiVeRs. This table is used by the system to keep track of data for opened ones) and FALSE otherwise. NOTE: It is important to check both the System file and the Unit Table since:

1) A DRiVeR installed in the System will not have an entry in the Unit Table at startup time, but WILL be entered when it is opened for the first time, and

2) A DRiVeR can be installed in the Unit Table and NOT in the System file (as is the case here).

If we don’t check the Unit Table, we could install ourselves over an installed DRiVeR that was meant to do some REAL work.

Boolean vacant_slotP(SUN, theSlot)

This function repeatedly searches the System file and checks the unit table. If/When it finds an ID for which no DRiVeR has been installed, it sets theSlot to the ID and returns TRUE. Otherwise it returns FALSE and the value of theSlot is undefined.

Boolean slot_takenP(unit_number)

This function checks to see if a DRiVeR with a reference number corresponding to ‘unit_number’ has been installed in the Unit Table. It does so by calling GetDCtlEntry(). If the call returns a device control entry, then a DRiVeR has already been opened (and thus installed), in which case the function returns TRUE. Otherwise it returns FALSE.

MenuMem: The DRiVeR

main(p,d,i)

This function gets called by THINKC when we receive Open, Close and Control calls. We just need to check the selector (i) to determine what the request is and call the appropriate function.

OpenDRVR(d,p)

This function simply resets the Flags, Delay, and Menu fields of the device control pointer since the device manager will set these fields from the header information.

setup_theMenu(d)

This is the routine that sets up the menu for the DRiVeR. It does not check to see if the menu already exists. Pascal style string constants must be declared as such since the CtoPstr() routine transforms the string in place.

ControlDRVR(d,p)

This routine is called periodically (every 5 seconds) to update the menu and to handle the case when our lone menu item “Compact Memory” is chosen.

update_theMenu(d)

This routine will create and install a menu for the DRiVeR, if the current amount of free memory the previous amount of free memory, or there is no menu with an ID = the owned resource ID of this DRiVeR. The latter will occur when opening a new application (whether running under MultiFinder or not). Any existing menu is deleted and disposed of before the new one is created.

CloseDRVR(d,p,x)

Since the dNeedGoodbye bit of the Flags field is set, the DRiVeR will get a goodbye kiss every time the application heap is re-initialized. This opportunity is taken to delete and dispose of the menu except for the first time (immediately before the Finder is run). In this case, not only has the Menu Manager not been initialized, but we don’t have a menu anyway. This routine will always return closeErr to insure that the DRiVeR is never closed.

And now here’s the code

/*------------------------------------------------

PROGRAM
 MenuMem - A combination INIT/DRVR that      displays the amount of free 
memory in the menu bar.

FILE
 “MenuMem INIT.c”

DESCRIPTION
 This file contains all of the code for the  INIT portion of the program, 
including the  functions LoadDRVR(), DRVRXistsP(),       slot_takenP(), 
and vacant_slotP().  It loads the DRVR and its associated DATA into the 
 system heap. 

Copyright © 1990 Daniel A. Green, and MacTutor. All rights reserved.

---------------------------------------------- */

/* -------------- INCLUDE FILES -------------- */
#include    “MacTypes.h”
#include    “string.h”
#include    “MenuMgr.h”
#include    <DeviceMgr.h>
#include    <ResourceMgr.h>
#include    “MenuMem.h”



/* --------------  PROTOTYPES  -------------- */
Boolean   DRVRXistsP(Integer theID);
Boolean   slot_takenP(Integer slot_num);
Boolean   vacant_slotP(Integer sun, Integer *the_slot);

/* --------------  FUNCTIONS  ---------------- */
pascal void main()
{
  Integer currentID, newID;
  ResType r_type;
  Str255  r_name;
  OSErr   theErr;
  Handle  theDATA, theDRVR, theINIT;

  theINIT = GetNamedResource(‘INIT’, DRVR_NAME);
  GetResInfo(theINIT, &currentID, &r_type, &r_name);

/************************************************* *
*  If there is an installed DRVR with the same ID *  as ours, 
*  then the resource ID of the INIT, DRVR, and its  
*  DATA should be changed.
*************************************************/
  if ( DRVRXistsP(currentID) )
    if ( vacant_slotP(DRVR_RSRCID, &newID) ) {

      SetResInfo(theINIT, newID, &r_name);

      theDRVR = GetResource(‘DRVR’, currentID);
      GetResInfo(theDRVR, &currentID, &r_type, &r_name);
      SetResInfo(theDRVR, newID, &r_name);

      theDATA = GetResource(‘DATA’,OWNEDRSRCID(
                         REFNUM(currentID) ) );
      GetResInfo(theDATA, &currentID, &r_type, &r_name);
      SetResInfo(theDATA, OWNEDRSRCID( 
                     REFNUM(newID) ), &r_name);

      currentID = newID;
  } else {
/* Max # of DRiVeRs installed. Just beep and return. */
      SysBeep(1);
      return;
  }

/* If we made it this far we can open the DRiVeR up */
  if ( LoadDRVR(&r_name, currentID) ) SysBeep(2);
};

/*************************************************
 LoadDRVR
*  Loads the DRiVeR into memory and opens it.  The *  System Heap attribute 
for
*  both the DRVR and DATA resources should be set *  to insure that neither 
will
*  be lost when the application heap is 
*  reinitialized.
*
*************************************************/
 
LoadDRVR(theName, theID)
Str255  *theName;
Integer theID;
{
  Integer theRefNum;
  OSErr   theErr;
  ResType theType;
  Handle  h, theDRVR;

  h = GetResource(‘DRVR’, theID);
  theErr = RESERROR();
  if (h && (theErr == noErr) )
    if ( (theErr = OpenDriver(theName, 
                           &theRefNum)) == noErr )
      DetachResource(h);

  return(theErr);
};

/*************************************************
 DRVRXistsP
*  returns TRUE if a DRiVeR with an ID of ‘theID’ *  exists in the resource 
fork
*  of the System File or a DRiVeR with an ID of 
*  ‘theID’ is in the Unit Table.
*  Otherwise returns FALSE.
************************************************/
 
Boolean DRVRXistsP(theID)
Integer theID;
{
  Boolean result = FALSE;
  Integer theRefNum;
  Handle  h;

/* Save the reference number of our resource file */
  theRefNum = CurResFile();
  UseResFile(0);  /* Use the System resource file */
  SetResLoad(FALSE);
  h = GetResource(‘DRVR’, theID);

/* If we can’t get a handle to the resource, then 
                          check the unit table  */
  if ( h || slot_takenP(theID) ) result = TRUE;
  if (h) ReleaseResource(h);
  SetResLoad(TRUE);
  UseResFile(theRefNum);
  return(result);
};

/*************************************************
 slot_takenP
*  If we can get a DCtlEntry for a given unit 
*  number, then a DRiVeR with the
*  given unit number has already been installed, 
*  so the function returns TRUE.
*  Otherwise it returns FALSE.
*************************************************/
 
Boolean slot_takenP(unit_number)
Integer unit_number;
{
  return( GetDCtlEntry( REFNUM( unit_number ) ) ? 
           TRUE : FALSE );
};

/*************************************************
 vacant_slotP
*  Repeatedly searches the System file and checks 
*  the unit table until an ID
*  has been found for which:
*      1).  No resource of type DRVR exists in the 
*           System file, and
*      2).  No entry exists in the unit table.
*  If an ID that satisfies the above criteria is 
*  found, the function sets
*  the_slot to the ID and returns TRUE.  Otherwise 
*  it returns FALSE.
 ************************************************/

Boolean vacant_slotP(sun, the_slot)
Integer sun;        /* the Starting Unit Number */
Integer *the_slot;
{
  Boolean vacant_slot = FALSE;
  Integer i, max, refnum, theRefNum;
  Handle  h;

  max = *(Integer *) (UnitNtryCnt);

  theRefNum = CurResFile(); /* Use System resource file */
  UseResFile(0); /* Don’t want to load the resources */
  SetResLoad(FALSE); /* Just check for their existence   */
  i = sun;
  do {
    h = GetResource(‘DRVR’, i);
    if (h == NULL) {
   /* Check the unit table */
      refnum = (-i) - 1;
      if ( GetDCtlEntry(refnum) == NULL) {
        vacant_slot = TRUE;
        *the_slot = i;
      }
    } else
      ReleaseResource(h);
    i++;
  } while ( !vacant_slot && (i < max) );

  SetResLoad(TRUE); /* Reset the search path */
  UseResFile(theRefNum);

  return(vacant_slot);
};

/*------------------------------------------------
PROGRAM
 MenuMem - A combination INIT/DRVR that
 displays the amount of free memory in the menu 
 bar.

FILE
  “MenuMem DRVR.c”

DESCRIPTION
  This file contains all of the code for the DRVR.
  In addition to the standard Open, Control, and 
  Close routines needed by all DRiVeRs, there are 
  two support routines, setup_theMenu() and 
  update_theMenu().  This DRiVeR does not support 
  read, write or status calls.

Copyright © 1990 Daniel A. Green, and MacTutor. All rights reserved.
---------------------------------------------- */

/* -------------- INCLUDE FILES -------------- */
#include    “MacTypes.h”
#include    “string.h”
#include    “MenuMgr.h”
#include    <DeviceMgr.h>
#include    <ResourceMgr.h>
#include    “MenuMem.h”

/* -------------- GLOBAL VARIABLES ---------- */
LongInt     free_memory = 0;   /* only need two */
Integer     first_close = TRUE;

/* --------------  FUNCTIONS  ---------------- */
main(p, d, i)
cntrlParam *p;      /*  ptr to parameter block  */
DCtlPtr d;          /*  ptr to device control entry */
Integer i;          /*  entry point selector    */
{
  Integer theErr;

  if (d->dCtlStorage == 0) {
      /* couldn’t get our storage */
    if (i == 0)     /* Open request ? */
      CloseDriver(d->dCtlRefNum);
    return(noErr);
  }

  switch (i) {      /* handle the request: */
    case 0:         /* open */
      theErr = OpenDRVR(d, p);
    break;

    case 2:         /* control */
      theErr = ControlDRVR(d, p); 
    break;

    case 4:         /* close */
      theErr = CloseDRVR(d, p);
    break;

    default: 
      theErr = noErr;
    break;
  }

  return(theErr);
};

/*************************************************
 OpenDRVR
*  This routine resets the dCtlFlags, dCtlDelay 
*  and dCtlMenu fields of the
*  DCtlPtr associated with the DRiVeR.
*************************************************/
 
OpenDRVR(d, p)
DCtlPtr d;
cntrlParam *p;
{

/* Don’t need to respond to Read, Write, or Status calls */
  d->dCtlFlags &= ~(dReadEnable | dWritEnable | dStatEnable);
/* but we do need time to update the menus every 5 
   seconds, and a goodbye kiss */
  d->dCtlFlags |= dNeedTime | dNeedGoodBye;
  d->dCtlDelay = 300;
  d->dCtlMenu = OWNEDRSRCID(d->dCtlRefNum);

return(noErr);
};

/*************************************************
 setup_theMenu
*  Creates a menu for which the title is the 
*  amount of free memory in the
*  application heap (in K), the first menu item is *  the number of free 
bytes in
*  the Application Heap (disabled), the second 
*  menu item is the number of free
*  bytes in the System Heap (disabled).  The 
*  fourth menu item, when chosen will
*  compact memory and purge all purgeable blocks 
*  from the application (read 
*  current) heap by calling the MaxMem trap.
*************************************************/
 
void setup_theMenu(d)
DCtlPtr d;
{
  char        *menuTitle;
  LongInt     fm;
  MenuHandle  theMenu;
  THz         saved_zone;
  Str255      fmStr, item1, item2;

  fm = FreeMem() / 1024;
  NumToString(fm, &fmStr);
  menuTitle = strcat( PtoCstr( (char *) &fmStr), “K”);
  theMenu = NewMenu(d->dCtlMenu, CtoPstr(menuTitle));
 /* Add menu to the menu list */
  InsertMenu(theMenu, 0);
 /* Save the current zone */
  saved_zone = GetZone();

 /* Setup the first menu item */
  strcpy((char *) &item1, “(Application Heap: “);
  SetZone( ApplicZone() );
  fm = FreeMem();
  NumToString(fm, &fmStr);
  AppendMenu(theMenu,CtoPstr(strcat((char *) 
  &item1, PtoCstr((char *) &fmStr))));

 /* Setup the second menu item */
  strcpy((char *) item2, “(System Heap: “);
  SetZone( SystemZone() );
  fm = FreeMem();
  NumToString(fm, &fmStr);
  AppendMenu(theMenu,CtoPstr(strcat((char *) 
  &item2, PtoCstr((char *) &fmStr))));

 /* Set up remaining menu items */
  AppendMenu(theMenu, “\P(-” );
  AppendMenu(theMenu, “\PCompact Memory” );

  SetZone(saved_zone);
  DrawMenuBar();
};

/*************************************************
 update_theMenu
*  Delete the DRiVeRs current menu and create a 
*  new one if the amount of free
*  memory has changed since the last call or we 
*  don’t have a menu in the
*  current menu bar.
*************************************************/
 
update_theMenu(d)
DCtlPtr d;
{
  Boolean     not_installed;
  LongInt     fm;
  MenuHandle  theMenu;

  not_installed = ( (theMenu = 
    GetMHandle( d->dCtlMenu ) ) == NULL );
  fm = FreeMem() / 1024;
  if ( not_installed || (free_memory != fm) ) {
    free_memory = fm;
    if ( theMenu ) {
      DeleteMenu(d->dCtlMenu);
      DisposeMenu(theMenu);
    }
    setup_theMenu(d);
  }
  return(noErr);
};

/*************************************************
 ControlDRVR
*  This routine will be called periodically to 
*  update the menu and to handle
*  the case when our one menu item is chosen.
*************************************************/
 
ControlDRVR(d, p)
DCtlPtr d;
cntrlParam *p;
{
  Integer theErr;
  Size    grow, lcf_block; /* largest contiguous free block */

  switch (p->csCode) {
    case accMenu:  
      lcf_block = MaxMem(&grow);
      theErr = update_theMenu(d);
    break;

    case accRun: /* periodicEvent */
      theErr = update_theMenu(d);
    break;

    case goodBye:
       theErr = CloseDRVR(d, p);
    break;

    default:
      theErr = noErr;
    break;
    }

    return(theErr);
};

/*************************************************
 CloseDRVR
*  Whenever we get a close request, we’ll always 
*  return an error so we won’t be
*  closed (This method will not work on 64K 
*  ROMs).
*  NOTE:  The first time we get a goodbye kiss 
*  will be after all INITS have
*  executed.  The Menu Manager has not been 
*  initialized at this time so no
*  calls can be made yet (We don’t have a menu at 
*  this point anyway).
*************************************************/
CloseDRVR(d, p)
DCtlPtr     d;
cntrlParam *p;
{
  MenuHandle  theMenu;

  if ( !first_close ) {
    if (theMenu = GetMHandle(d->dCtlMenu)) {
      DeleteMenu(d->dCtlMenu);
      DisposeMenu(theMenu);
    }
  }
  first_close = FALSE;
  return(closeErr);
};

 
AAPL
$95.79
Apple Inc.
-2.36
MSFT
$43.28
Microsoft Corpora
-0.30
GOOG
$572.85
Google Inc.
-14.57

MacTech Search:
Community Search:

Software Updates via MacUpdate

Ember 1.8 - Versatile digital scrapbook....
Ember (formerly LittleSnapper) is your digital scrapbook of things that inspire you: websites, photos, apps or other things. Just drag in images that you want to keep, organize them into relevant... Read more
OmniPlan 2.3.6 - Robust project manageme...
With OmniPlan, you can create logical, manageable project plans with Gantt charts, schedules, summaries, milestones, and critical paths. Break down the tasks needed to make your project a success,... Read more
Command-C 1.1.1 - Clipboard sharing tool...
Command-C is a revolutionary app which makes easy to share your clipboard between iOS and OS X using your local WiFi network, even if the app is not currently opened. Copy anything (text, pictures,... Read more
Knock 1.1.7 - Unlock your Mac by knockin...
Knock is a faster, safer way to sign in. You keep your iPhone with you all the time. Now you can use it as a password. You never have to open the app -- just knock on your phone twice, even when it's... Read more
Mellel 3.3.6 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
LibreOffice 4.3.0.4 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
Freeway Pro 7.0 - Drag-and-drop Web desi...
Freeway Pro lets you build websites with speed and precision... without writing a line of code! With it's user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of... Read more
Drive Genius 3.2.4 - Powerful system uti...
Drive Genius is an OS X utility designed to provide unsurpassed storage management. Featuring an easy-to-use interface, Drive Genius is packed with powerful tools such as a drive optimizer, a... Read more
Vitamin-R 2.15 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more
Toast Titanium 12.0 - The ultimate media...
Toast Titanium goes way beyond the very basic burning in the Mac OS and iLife software, and sets the standard for burning CDs, DVDs, and now Blu-ray discs on the Mac. Create superior sounding audio... Read more

Latest Forum Discussions

See All

Dawn of the Immortals Review
Dawn of the Immortals Review By Jennifer Allen on July 31st, 2014 Our Rating: :: RESPECTABLE EXPLORATIONUniversal App - Designed for iPhone and iPad Dawn of the Immortals might not re-invent the wheel, but it does tweak it a little... | Read more »
80 Days Review
80 Days Review By Jennifer Allen on July 31st, 2014 Our Rating: :: EPIC ADVENTUREUniversal App - Designed for iPhone and iPad A fantastic and fascinating re-envisioning of the classic novel by Jules Verne, 80 Days is a delightful... | Read more »
Battleheart Legacy Guide
The world of Battleheart Legacy is fun and deep; full of wizards, warriors, and witches. Here are some tips and tactics to help you get the most enjoyment out of this great game. | Read more »
Puzzle Roo Review
Puzzle Roo Review By Jennifer Allen on July 31st, 2014 Our Rating: :: PUZZLE-BASED TWISTUniversal App - Designed for iPhone and iPad A different take on the usual block dropping puzzle game, Puzzle Roo is quite pleasant.   | Read more »
Super Crossfire Re-Release Super Crossfi...
Super Crossfire Re-Release Super Crossfighter Coming Soon, Other Radiangames Titles Go 50% Off Posted by Ellis Spice on July 31st, 2014 [ | Read more »
Hexiled Review
Hexiled Review By Rob Thomas on July 31st, 2014 Our Rating: :: HEX SELLSUniversal App - Designed for iPhone and iPad In space, no one can hear you… spell? Hexiled is a neat concept for a word scramble puzzle, but it doesn’t go too... | Read more »
Summoners War: Sky Arena Passes 10 Milli...
Summoners War: Sky Arena Passes 10 Million Installs! Posted by Jessica Fisher on July 31st, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Deep Loot Review
Deep Loot Review By Jennifer Allen on July 31st, 2014 Our Rating: :: DIVE DEEPUniversal App - Designed for iPhone and iPad Dive deep in this fun explore-em-up that’s a little grind heavy but ultimately quite entertaining.   | Read more »
Despicable Me: Minion Rush is One Year O...
Despicable Me: Minion Rush is One Year Old, Gets its Biggest Update Yet Posted by Jennifer Allen on July 31st, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Fish & Shark Review
Fish & Shark Review By Jordan Minor on July 31st, 2014 Our Rating: :: FLAPPY FISHUniversal App - Designed for iPhone and iPad Fish & Shark’s beauty is only scale deep.   | Read more »

Price Scanner via MacPrices.net

Save up to $130 on an iPad mini with Apple re...
The Apple Store has Certified Refurbished 2nd generation iPad minis with Retina Displays available for up to $130 off the cost of new models, starting at $339. Apple’s one-year warranty is included... Read more
iPad Cannibalization Threat “Overblown”
Seeking Alpha’s Kevin Greenhalgh observes that while many commentators think Apple’s forthcoming 5.5-inch panel iPhone 6 will cannibalize iPad sales, in his estimation, these concerns are being... Read more
Primate Labs Releases July 2014 MacBook Pro P...
Primate Labs’ John Poole has posted Geekbench 3 results for most of the new MacBook Pro models that Apple released on Tuesday. Poole observes that overall performance improvements for the new MacBook... Read more
Apple Re-Releases Bugfixed MacBook Air EFI Fi...
Apple has posted a bugfixed version EFI Firmware Update 2.9 a for MacBook Air (Mid 2011) models. The update addresses an issue where systems may take longer to wake from sleep than expected, and... Read more
Save $50 on the 2.5GHz Mac mini, plus free sh...
B&H Photo has the 2.5GHz Mac mini on sale for $549.99 including free shipping. That’s $50 off MSRP, and B&H will also include a free copy of Parallels Desktop software. NY sales tax only. Read more
Save up to $140 on an iPad Air with Apple ref...
Apple is offering Certified Refurbished iPad Airs for up to $140 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free. Stock tends to come and go with some of these... Read more
$250 price drop on leftover 15-inch Retina Ma...
B&H Photo has dropped prices on 2013 15″ Retina MacBook Pros by $250 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.3GHz Retina MacBook Pro: $2249, $250 off... Read more
More iPad Upgrade Musings – The ‘Book Mystiqu...
Much discussed recently, what with Apple reporting iPad sales shrinkage over two consecutive quarters, is that it had apparently been widely assumed that tablet users would follow a two-year hardware... Read more
13-inch 2.5GHz MacBook Pro on sale for $999,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $999.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Save up to $300 on an iMac with Apple refurbi...
The Apple Store has Apple Certified Refurbished iMacs available for up to $300 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. These are the best prices on... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.