TweetFollow Us on Twitter

Menu Selection
Volume Number:2
Issue Number:3
Column Tag:Inside Macintosh

Menu Selection for Desk Accessories

By Jan Eugenides, Assembly Corner, Maynard, MA

Writing Desk Accessories with Megamax C

Writing Desk Accessories is one of the more interesting (frustrating?) programming tasks on the Macintosh. I have written a number of them using Megamax C, and have found it to be an excellent system. There are some things they don't tell you in the manual though, which I will point out. This article will present a complete working desk accessory in Font/DA Mover format. It is modestly complex, and you can use it as a shell for your own DA's.

The dCtlFlags

Each desk accessory has certain information about it stored in a “Device Control Entry” (DCE) when it is opened. This includes such things as the driver's reference number, a pointer to its window, the number of ticks between periodic actions, the menu id, and the dCtlFlags.

Examine Listing #1. The first thing you see after the standard includes is the letters"ACC" followed by some arcane numbers. This is a macro for setting the dCtlFlags. The Megamax system makes it fairly simple to set up the flags properly, with the ACC macro.

The first number, 0x6400, indicates that this driver needs to be locked (all DAs do), will respond to control calls, and needs to perform periodic actions. The flags are two bytes in length, but only the high order one is used. The bits of that byte are defined as follows:

Bit Number  Meaning
    0       dReadEnable - set if driver can respond to read calls
    1       dWritEnable - set if driver can respond to write calls
    2       dCtlEnable - set if driver can respond to control calls
    3       dStatEnable - set if driver can respond to status calls
    4       dNeedGoodbye - set if driver needs to be called before the 
heap              is reinitialized.
    5       dNeedTime - set if driver needs to be called periodically.
    6       dNeedLock - set if driver needs to be locked in memory.

The next number in the header is the number of ticks needed between calls (a tick is 1/60 second). Next comes the event mask, which is just like an application event mask. This program uses 0xFFFB which allows all events except mouse-up. Next the menu ID, which must be a negative number and unique in the system, and finally the title preceded by a length byte. The title must be an odd length, add a space if necessary. Note that by convention a DA title must begin with a zero, or NULL. This is to prevent conflicts with other files on the disk which may have the same name as the DA.

Figure 1 Our Desk Accessory Window

Private Storage

When you write a DA, you must take care to allocate and lock down any private variable storage to be sure that it won't move out from under you. The Mac memory manager will move things around in the heap as neccessary according to its needs of the moment. If you don't lock down your storage, it may not be there next time you look for it.

There are two methods to keep in mind for dealing with this situation.

1. Use auto variables as much as possible, and limit your globals to only those that must retain a value between functions.

2. Define all your globals up front, and then instruct the memory manager to lock them down. Be sure to unlock and release them when you are done.

In the DCE there is a space called dCtlStorage, where you can store the handle to your private storage area, if any. Most systems require that you deal with all this yourself, but Megamax C makes it automatic. All global variables are put into a DATA segment, which is loaded with the DA at runtime. During the open, close, and control calls to the DA, this segment is locked in memory, and can be referenced in the usual way. The only problem is that nowhere in the manual is this explained, or even mentioned. If you attempt to use dCtlStorage yourself, your DA will bomb inexplicably. Use globals, and there will be no problem. In this program, the only global is the variable whichstr, which needs to retain its value across calls.

The Five main routines

There are five basic routines which must be present in every Desk Accessory - Open, Close, Control, Status, and Prime. The system will pass these routines pointers to the Device Control Entry (dctl) and the Parameter Block (pb). The Open routine opens the DA and does any initialization. The Close routine closes everything and releases all storage, the Control routine takes care of all the various events, the Prime routine must implement all read and write calls made to the driver (rare for DAs), and the status routine must return requested status information (also rare for DAs). Even if you do not use all the routines, they must still be defined at least as simple returns. In order for Megamax to correctly compile your DA, you must name your open routine accopen(), your close routine accclose(), your control routine accctl(), your status routine accstatus(), and your prime routine accprime().

Open

Examine the accopen() routine now. Notice that the first thing it does is check to see if the DA is already open by looking for its menu. Under no circumstances must you allow the DA to be opened twice! Another way of checking is to look at the dCtlWindow field. Assuming that you are handling this field correctly (see below), if it is NULL, the DA is not open.

The menu ID method also prevents opening the DA should a menu ID conflict arise. Since I have coded the menu ID into the DA rather than putting it in a resource, it is possible (though unlikely) that some other program will already be using that ID. The only way to avoid this problem is to put the menu in a named resource, and at run time adjust the ID and your code as necessary (see the section on Owned Resources below).

The current Font/DA Mover has a problem with menus, so you cannot be sure that your menu's ID will match up with your DRVR ID once it has been installed. Font/DA Mover does not properly update the ID numbers of owned MENU resources. If you are using GetMenu to get an owned MENU resource, you must patch the first word pointed to by the returned handle with the actual resource ID of the MENU. I have chosen the simplest solution, hardwire the menu ID and then check for it before opening. At least there's no way to bomb anything with this method, and only in a very rare instance would you be unable to open your DA.

Ok, once you've determined that the Menu ID is available, you must check to see if there's enough memory. IM says that if there is insufficient memory you should put up a dialog that says so. I have settled for a simple beep

Next, the menu is defined and installed, a window is opened and the TextMode and Font are specified. The next two lines are quite interesting.

wp->windowKind = dctl->dCtlRefNum;
dctl->dCtlWindow = wp;

You must set the windowKind field to your driver reference number in order for the system to pass you events relating to your window, such as a click in the GoAway box. This is how the system identifies your window as belonging to a DA. You should also put a pointer to your window in dCtlWindow.

Note that you can create your window as a dialog, and use the dialog manager routines to control it. I have discovered that certain dialog manager routines will not work unless the windowKind field is 2 (dialogKind). If you run into this problem, just change the windowKind field to 2 temporarily, then call the dialog manager, and then change it back to the dCtlRefNum when you are finished. This DA is not defined as a dialog, so there is no problem.

Close

This routine is called when someone clicks the GoAway box. Notice how the window pointer is fetched from the DCE.

After zeroing the window pointer in the DCE, the window is disposed of, and the menu bar is restored.

I should mention here that some DAs will need to call the close routine before the heap gets reinitialized when the user quits an application. If your DA is open when the application terminates and you have files open, you need to take care of that stuff before you get blasted away. To do this, you must set your flags to indicate that you need a Goodbye call, and check for an event code of -1, called the GoodByeKiss. Then you can take care of whatever chores you have before the DA gets wiped from memory.

Control

This routine is called when there is an event your DA might have to handle. The event code is passed in the Parameter Block. All you need to do is case out on it and perform the required actions. This is where you would look for the GoodByeKiss, too.

The only events that this DA looks for are accRun, which is called periodically and in turn calls the display() routine, and accMenu which has one item which opens a dialog box. The dialog template for this DA is in a resource, which brings us to the problem of using resources with a DA.

Owned Resources

When you create your DA, you can specify the ID numbers for the various resources you may wish to use, such as a DLOG (dialog template) and DITL (item list) as I have done here (see Listing #2). The Macintosh system recognizes certain ID numbers as having special significance, and an ID code is used to associate owned system resources with the resources to which they belong. After all, a desk accessory is actually a resource itself, of type DRVR. By using the right ID numbers you can tell the system which other resources are owned by your DA.

DRVRs can have IDs in the range 12 to 26. The numbers 27 to 31 are reserved for dynamic allocation of IDs at runtime for disk drivers, mail servers, etc. For each of these ID numbers, there is another unique range of ID numbers that is associated with it and that indicate ownership. The formula for figuring these out is:

Resource ID = -16384 + (32*DA ID) + Resource number

All you need to do to assure that the system will recognize your DRVR's resources is to give them ID numbers in these ranges. Each resource type can include resources in these ranges, so you can have, say, a WIND (window) resource with ID -16000 and a DLOG (dialog template) resource with ID -16000 and both will be associated with the DRVR whose ID is 12. You cannot use the same ID number within the same type of resource, of course. This means that you are limited to 32 resources of any one type.

An owned resource may itself contain the ID of a resource associated with it. For example, a DLOG owned by a DRVR contains the resource ID of its DITL (dialog item list). Even though the item list is associated with the dialog template, it is owned indirectly by the DRVR. Therefor the ID number for the DITL must also conform to the same special numbering system as the ID of the DLOG.

Font/DA Mover

When Apple's Font/DA mover program is used to install a desk accessory, it first checks the system file to see what ID numbers are already in use for DRVRs. It then changes your DRVR‘s ID to one that is not in use, and copies your DRVR and its owned resources into the system file. It also changes all your resource IDs to conform to the new DRVR ID. If you do not follow the numbering convention properly, your owned resources will not be copied along with your DRVR, causing unfortunate results.

This has interesting ramifications when you write your desk accessory, because there is no way to know beforehand what ID numbers your resources will actually have. The easiest way I have found to get around this problem is to name all of my resources. Resources can also have names in addition to their ID numbers, and the names will not be changed when the DRVR is copied into the system file. You can then use GetNamedResource to find out the actual ID number of your resource, as I have done here.

Display

The display routine is fairly straightforward, simply a series of MoveTo's and DrawString's. Remember that this routine gets called every 20 ticks by the accRun event, and uses that to time things. The most interesting routine here is the scrolling routine at the end. In order to scroll the bits in a given rectangle, you must first create a new region, using the NewRgn() call, to hold the update area. This program simply discards the region when it‘s done. Then you can call ScrollRect with the rectangle you wish to scroll (in this case the whole window), and the direction(s) and amounts. Positive numbers produce movement down and to the right. I have used a negative vertical movement of 2 pixels each time, which results in a smooth scroll upward.

Creating a Font/DA Mover file

Once you compile this file, you must then link it. The Megamax linker allows you to directly create certain resource types. In this case you want a DRVR type resource named MTUT, with ID = 12. After the linker is through, you'll have a desk accessory ready to run, but there is one problem. Font/DA Mover will not recognize the file! The problem is that the type and creator are not what Font/DA Mover wants to see, even though the file is actually compatible. You must use the “set attributes” feature of FEDIT to change the file type to DFIL and the creator to DMOV. Then you can install the DA with Font/DA Mover. The DA will also take on the suitcase icon if Font/DA Mover is present on the disk. Recompiling will not change the type or creator, so you only need to do this once.

The RMaker File

I have provided you with an RMaker file of the DLOG and DITL used in this program. Compiling it will add those resources to a file named MTUT, which should be your DA's name, the file you just linked. I haven't figured out how to get RMaker to name resources, so you will need to use ResEdit to add the name "MTUTINFO" to the DLOG resource before you install the DA. Otherwise the little scheme of using GetNamedResource() will not work. If anyone knows of a way to get RMaker to produce named resources, please write.

An alternative is to actually create the resources using ResEdit, naming them in the process. Frankly, this is the way I create all my resources these days, but there is no easy way to publish them, since there is no source code generated. Guess we'll have to stick to RMaker for exchanging resource information.

Final Notes

If you follow the basic outline of this DA, you should be able to get some of your own up and running. This is not to say that some things about this program couldn't be improved, but hey, I'm learning too. I have found that writing DAs is a lot of fun. Just seeing them run concurrently with other applications without bombing is a real reward. I hope this article helps you along your way.

Figure 3 Our DA has a Menu for an about box!

/* The MacTutor Desk Accessory 
 *      
 * © 1985 by Jan Eugenides for MacTutor 
 *      P.O. Box 151
 *    Maynard, MA  01754
 *      (617) 897-7749
 *    All rights reserved
 *
 */
 
#include <acc.h>
#include <qd.h>
#include <mem.h>
#include <misc.h>
#include <win.h>
#include <te.h>
#include <control.h>
#include <desk.h>
#include <device.h>
#include <dialog.h>
#include <ctype.h>
#include <errno.h>
#include <event.h>
#include <file.h>
#include <font.h>
#include <res.h>
#include <menu.h>
#include <os.h>
#include <pack.h>
#include <qdvars.h>
#include <seg.h>
#include <stdio.h>
#include <string.h>

#define ENOUGH 3500
#define MENUID -633

 
ACC(0x6400, /* NeedLock,control-enabled,NeedTime */
    20, /* ticks needed */
    0xFFFB, /* all events except mouseup*/
    -633, /* menu id  */
    9, "\0MacTutor") /* Length and text of title - must be odd*/

intwhichstr;/*our only global*/

accopen(dctl, pb)
dctlentry *dctl;
paramblockrec  *pb;
{
 MenuHandle menu;
 long   freemem,grow;
 WindowPeek wp;
 Rect   wr;
 MenuHandle testmenu;
 Handle rhandle;
 ResTyperestype;
 char   resname[64];
 int    id;
 
 if(testmenu = GetMHandle(MENUID))   /*is it open already?*/
 {
 SysBeep(2); 
 return(1);
 }
 else
 { /*no, is there memory?*/
 freemem = MaxMem(&grow);
 if(freemem<ENOUGH)
 {
 SysBeep(2);
 return(1); /*no, return*/
 }
 }
 
 menu = NewMenu(MENUID,"MacTutor"); /*install the menu*/
 AppendMenu(menu,"(-");
 AppendMenu(menu,"About MacTutor");
 InsertMenu(menu,0);
 DrawMenuBar();
 whichstr = 0;   /*initialize counter*/
 
 if (dctl->dCtlWindow == NULL)/*make a new window */
 { 
 SetRect(&wr, 20, 40, 274, 240);
 wp = NewWindow(NULL, &wr, "MacTutor", 0, 22, -1L, -1, 0L);
 SetPort(wp);    
 TextMode(srcCopy);
 TextFont(systemFont);
 wp->windowKind = dctl->dCtlRefNum;  /* windowkind to refnum*/
 dctl->dCtlWindow = wp;
 }
 return 0;
}

accclose(dctl, pb)
dctlentry *dctl;
paramblockrec *pb;
{
WindowPtr tmpwp;
 tmpwp = (WindowPtr)dctl->dCtlWindow;
 dctl->dCtlWindow = NULL; /*clear the window field*/
 if(tmpwp) DisposeWindow(tmpwp);   /*get rid of the window*/
 DeleteMenu(MENUID); /*and the menu*/
 DrawMenuBar();
 return 0;
}
accctl(dctl, pb) 
dctlentry *dctl;
paramblockrec *pb;
{
 register int    item;
 int    itemhit;
 Handle rhandle;
 int    id;
 ResTyperestype;
 char   resname[64];
 DialogPtrmydialog;
 GrafPtrtemport;
 switch(pb->paramunion.CntrlParam.CSCode)    /*get control code*/
 {
 case accEvent:  /*events handled here*/
 break;
 case accRun:    /*called periodically by system*/
 display(dctl);  /*according to tick count*/
 break; 
 case accMenu:
 switch(item = ((int *)&pb->paramunion.CntrlParam.csParam)[1])
 {
 case 2:
 GetPort(&temport);
 rhandle = GetNamedResource("DLOG","MTUTINFO");
 GetResInfo(rhandle,&id,&restype,&resname);  
 mydialog = GetNewDialog(id,NULL,(long)-1);
 SetPort(mydialog);
 itemhit = 0;
 while(itemhit != 1)ModalDialog((long)0,&itemhit); 
 SetPort(temport);
 DisposDialog(mydialog);
 whichstr = 0;
 HiliteMenu(0);
 break;
 
 } /*end of menu switch*/
 default:
 break;
 } /*end of control switch*/
return 0;
}  /*end of control*/
 
accprime()/*these are null but must be defined*/
{
}
accstatus()
{
}
display(dctl)    /*routine to do the display*/
dctlentry *dctl;
{
Rect    wr,drect;
inti;
RgnHandle rgn;
WindowPeekwp;
wp = (WindowPeek)dctl->dCtlWindow;
SetPort(wp);/*set port to our window*/
wr = wp->port.portRect;   /*and get the port rect*/
whichstr += 1;   /*increment string counter*/
if(whichstr == 47) whichstr = 1; /*enforce limit*/
switch(whichstr)
{
case 1:
 EraseRect(&wr); /*begin by erasing window*/
 MoveTo(95,16);
 TextFace(bold);
 DrawString("MacTutor");  /*put title in boldface*/
 TextFace(0);    /*restore normal face*/
 break;
case 4:
 MoveTo(4,32);DrawString("The Macintosh Programming Journal");
 break;
case 7:
 MoveTo(12,48);DrawString("§-§-§-§-§-§-§-§-§-§-§-§-§");
 break;
case 10:
 MoveTo(4,68);DrawString("• Programming information!");
 break; 
case 13:
 MoveTo(4,84);DrawString("• Pascal, C, Assembly, Neon, Basic!");
 break;
case 16:
 MoveTo(4,100);DrawString("• Lisp, Modula 2, Forth, and more!");
 break;
case 19:
 MoveTo(4,116);DrawString("• Technical Information!");
 break;
case 22:
 MoveTo(4,132);DrawString("• The no fluff Macintosh magazine");
 break;
case 25:MoveTo(4,148);DrawString("• $30/yr US-$45 Canada-$60 foreign");
 break;
case 28:MoveTo(4,164);DrawString("===================================");
 break;
case 31:
 MoveTo(20,180);
 TextFace(italic);
 DrawString("PO Box 846, Placentia, CA 92670");
 TextFace(0);
 break;
case 37:
 rgn = NewRgn(); /*we need a new region to scroll*/
 for(i = 0;i<95;i++)
 ScrollRect(&wr,0,-2,rgn);  /*scroll 2 pixels at a time*/
 DisposeRgn(rgn);  /* done with region*/
 break;
default:
 break; 
 } /*end of switch*/ 
}

From Volume 2 Number 5:

Desk Accessories Comments

Jan Eugenides

Since writing my article on desk accessories in the March 1986 issue of MacTutor, I've learned a couple of things that should be passed on to your readers.

I stated in the article that by convention, the name of a DA should start with a NUL character (Ascii code zero). This is what Inside Macintosh says on page I-444 of the desk manager manual. However with the new Mac+ system, which displays DA menu items in alphabetical order, I discovered that all my DA's were showing up at the top of the menu. A little poking around revealed that all of Apple's own DA's such as the Control Panel, etc., do not start with a NUL at all. Instead they end with one! Inside Macintosh is wrong on this point.

I failed to make it sufficiently clear what the memory test at the beginning of my DA is for. From the article, it appears as though I am testing to make sure there is enough memory to open the DA. By the time the test is performed, the DA is already resident in memory, so such a test would be superfluous. The idea is to determine if there is enough memory available to open further windows, etc. After some experimentation, I have decided that the memory test at the beginning is uneccessary. It makes more sense to check available memory when you need it, for example before opening a file, or displaying a new window.

More on Jan's DA

Kenneth Bates

I enjoyed the article on desk accessories by Jan Eugenides (March 1986) very much, and have one additional point of information to add which may help others.

Jan mentions (quite properly) that the ID number of a desk accessory may be changed by the Font/DA mover during installation. In addition, he points out that all 'owned' resources of the desk accessory will also be changed. His solution is to access the resources by name (GetNamedResource()).

Although this is certainly possible, there is an easy way to find out exactly what the ID number of the desk accessory is at run time. The device control entry (passed to the DA on entry) has a field named 'dCtlRefNum'. By negating and decrementing this field, the ID of the DA is obtained. As an example:

accopen(dctl, pb)   /* DA Open routine */

dCtlEntry *dctl;
ParamBlockRec  *pb;
 {
 int  id; /* This will be our new ID */

 id=dctl->dCtlRefNum;
 id=-id-1;

Following the execution of this code, variable 'id' will contain the renumbered ID of the desk accessory. From there, it is a small matter to obtain the needed resource. As another example, to get a dialgo which originally had a sub-resource of 2:

mydialog = GetNewDialog(id * 32 -16384 +2, NULL, -1L);

Owned Resoruces with Megamax C

Ronald Parsons

In the March 1986 MacTutor, Jan Eugenides showed a method to handle owned resources in a desk accessory. I have used a variation of that method which is somewhat more general (no resources need be named using ResEdit) and multiple types of resources are supported.

To make creation of DA's easier with Megamax C, I make a copy of the linker, mmlink, re-name it mmlinkDA and use ResEdit to make the default Res Type be DRVR and the default ID be 0031.

Using RMaker, I add a small resource to my DA with an unusual name (I use 'mine' in lower case). I calculate the ID's of all owned resources in the RMaker source asuming an ID of the DA of 31. (See the example code in listing 1 below.)

In the accopen() routine of the desk accessory, I incude the code shown in listing 2. I first make sure there is one and only one resource named 'mine' in the system. For this example, I beep and exit if there is more or less than one. I then get a handle to that resource using getindresource("mine",1). With that handle and the call getresinfo (hndl, &id, &resourtype, &genstr), I get the current ID of the resource "mine" as it has been changed by the Font/DA mover. The ID of the DA DRVR resource can then be calculated by id=(id+16384)/32. This ID can be used in calls using owned resources such as stopalert(-16384+32*id +1, NULL). The resource ID's are thus correctly calculated. With this method I can use many types of resources without having to name each one.

Listing 1
* Tell RMaker what to name the resource file
!DA
DFILDMOV

type mine = GNRL
 ,-15392;; =-16384 + 32*31 + 0; resource ID 0 for DRVR #31
.I
0

Type Alert
 ,-15391(36);; resource ID (SmGenID)  -16384 + 32*31 +1 
260 80 335 430 ;; top left bottom right
-15391  ;;resource ID of item list
4444    ;; stages word in hex
Listing 2
osstr255 genstr; /* General use string */
handle hndl;/* Handle to allocated array */
restype resourtype;/* resource type */
int id, count; /* DA's resource ID, count having type "mine" */

 count = countresources("mine")  /* find DAs res ID */
 if (count !=1 ) {sysbeep(30); return 1;}
 hndl = getindresource("mine",1);
 getresinfo (hndl, &id, &resourtype, &genstr);
 id=(id+16384)/32;

 stopalert(-16384 + 32*id +1, NULL); /* post alert*/
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

GraphicConverter 10.5.1 - $39.95
GraphicConverter is an all-purpose image-editing program that can import 200 different graphic-based formats, edit the image, and export it to any of 80 available file formats. The high-end editing... Read more
Delicious Library 3.7 - Import, browse a...
Delicious Library allows you to import, browse, and share all your books, movies, music, and video games with Delicious Library. Run your very own library from your home or office using our... Read more
Adobe Animate CC 2017 18.0.0.107 - Anima...
Animate CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Flash Professional customer). Animate CC 2018 (was Flash CC) lets you... Read more
Adobe After Effects CC 2018 15.0 - Creat...
After Effects CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous After Effects customer). The new, more connected After Effects CC... Read more
Adobe Premiere Pro CC 2018 12.0.0 - Digi...
Premiere Pro CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Premiere Pro customer). Adobe Premiere Pro CC 2018 lets you edit... Read more
Alarm Clock Pro 10.3 - $19.95
Alarm Clock Pro isn't just an ordinary alarm clock. Use it to wake you up in the morning, send and compose e-mails, remind you of appointments, randomize the iTunes selection, control an internet... Read more
Adobe Lightroom 20170919-1412-ccb76bd] -...
Adobe Lightroom is available as part of Adobe Creative Cloud for as little as $9.99/month bundled with Photoshop CC as part of the photography package. Lightroom 6 is also available for purchase as a... Read more
Adobe Illustrator CC 2018 22.0.0 - Profe...
Illustrator CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Illustrator customer). Adobe Illustrator CC 2018 is the industry... Read more
Hopper Disassembler 4.3.0- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
Adobe InDesign CC 2018 13.0.0.125 - Prof...
InDesign CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous InDesign customer). Adobe InDesign CC 2018 is part of Creative Cloud.... Read more

ICEY (Games)
ICEY 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ICEY is a 2D side-scrolling action game. As you follow the narrator's omnipresent voice, you will see through ICEY's eyes and learn the... | Read more »
The best new games we played this week -...
We've made it, folks. Another weekend is upon us. It's time to sit back and relax with the best new releases of the week. Puzzles, strategy RPGs, and arcade games abound this week. There's a lot of quality stuff to unpack this week, so let's hop... | Read more »
Wheels of Aurelia (Games)
Wheels of Aurelia 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: | Read more »
Halcyon 6: Starbase Commander guide - ti...
Halcyon 6 is a well-loved indie RPG with stellar tactical combat and some pretty good writing, too. It's now landed on the App Store, so mobile fans, if you're itching for a good intergalactic adventure, here's your game. Being a strategy RPG, the... | Read more »
Game of Thrones: Conquest guide - how to...
Fans of base building games might be excited to know that yet another entry in the genre has materialized - Game of Thrones: Conquest. Yes, you can now join the many kingdoms of the famed book series, or create your own, as you try to conquer... | Read more »
Halcyon 6: Starbase Commander (Games)
Halcyon 6: Starbase Commander 1.4.2.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.4.2.0 (iTunes) Description: An epic space strategy RPG with base building, deep tactical combat, crew management, alien diplomacy,... | Read more »
Legacy of Discord celebrates its 1 year...
It’s been a thrilling first year for fans of Legacy of Discord, the stunning PvP dungeon-crawling ARPG from YOOZOO Games, and now it’s time to celebrate the game’s first anniversary. The developers are amping up the festivities with some exciting... | Read more »
3 reasons to play Thunder Armada - the n...
The bygone days of the Battleship board game might have past, but naval combat simulators still find an audience on mobile. Thunder Armada is Chinese developer Chyogames latest entry into the genre, drawing inspiration from the explosive exchanges... | Read more »
Experience a full 3D fantasy MMORPG, as...
Those hoping to sink their teeth into a meaty hack and slash RPG that encourages you to fight with others might want to check out EZFun’s new Eternity Guardians. Available to download for iOS and Android, Eternity Guardians is an MMORPG that lets... | Read more »
Warhammer Quest 2 (Games)
Warhammer Quest 2 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: Dungeon adventures in the Warhammer World are back! | Read more »

Price Scanner via MacPrices.net

Save $100 on 13″ MacBook Airs, prices start a...
Adorama has 2017 13″ MacBook Airs on sale today for $100 off MSRP including free shipping. Adorama charges NY & NJ sales tax only: – 13″ 1.8GHz/128GB MacBook Air (MQD32LL/A): $899, $100 off MSRP... Read more
1.4GHz Mac mini available for $399, $100 off...
TigerDirect has the 1.4GHz Mac mini on sale today for $399 including free shipping. Their price is $100 off MSRP, and it’s the lowest price available for this model. Although currently out of stock,... Read more
21″ 2.3GHz iMac on sale for $999, save $100
MacMall has the 21″ 2.3GHz iMac (MMQA2LL/A) on sale today for $999 including free shipping. Their price is $100 off MSRP, and it’s the lowest price available for this model. Read more
12″ iPad Pros on sale for $50 off MSRP, no ta...
Adorama has 12″ iPad Pros on sale today for $50 off MSRP. Shipping is free, and Adorama charges sales tax in NY & NJ only: – 12″ 64GB iPad Pro: $749, save $50 – 12″ 256GB iPad Pro: $899, save $50... Read more
9″ iPads on sale for $30 off, starting at $29...
MacMall has 9″ iPads on sale for $30 off including free shipping: – 9″ 32GB iPad: $299 – 9″ 128GB iPad: $399 Read more
Apple restocks full line of refurbished 13″ M...
Apple has restocked a full line of Apple Certified Refurbished 2017 13″ MacBook Pros for $200-$300 off MSRP. A standard Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
13″ 3.1GHz/256GB MacBook Pro on sale for $167...
Amazon has the 2017 13″ 3.1GHz/256GB Space Gray MacBook Pro on sale today for $121 off MSRP including free shipping: – 13″ 3.1GHz/256GB Space Gray MacBook Pro (MPXV2LL/A): $1678 $121 off MSRP Keep an... Read more
13″ MacBook Pros on sale for up to $120 off M...
B&H Photo has 2017 13″ MacBook Pros in stock today and on sale for up to $120 off MSRP, each including free shipping plus NY & NJ sales tax only: – 13-inch 2.3GHz/128GB Space Gray MacBook... Read more
15″ MacBook Pros on sale for up to $200 off M...
B&H Photo has 15″ MacBook Pros on sale for up to $200 off MSRP. Shipping is free, and B&H charges sales tax in NY & NJ only: – 15″ 2.8GHz MacBook Pro Space Gray (MPTR2LL/A): $2249, $150... Read more
Roundup of Apple Certified Refurbished iMacs,...
Apple has a full line of Certified Refurbished 2017 21″ and 27″ iMacs available starting at $1019 and ranging up to $350 off original MSRP. Apple’s one-year warranty is standard, and shipping is free... Read more

Jobs Board

Engineering Manager, *Apple* Retail Enginee...
# Engineering Manager, Apple Retail Engineering Job Number: 58139948 Santa Clara Valley, California, United States Posted: 20-Oct-2017 Weekly Hours: 40.00 **Job Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Commerce Engineer, *Apple* Media Products -...
Commerce Engineer, Apple Media Products (New York City) Job Number: 113028813New York City, New York, United StatesPosted: Sep. 20, 2017Weekly Hours: 40.00 Job Read more
US- *Apple* Store Leader Program - Apple (Un...
US- Apple Store Leader Program Job Number: VariousUnited StatesPosted: Oct. 19, 2017Retail Store Job Summary Learn and grow as you explore the art of leadership at Read more
Product Manager - *Apple* Pay on the *Appl...
Job Summary Apple is looking for a talented product manager to drive the expansion of Apple Pay on the Apple Online Store. This position includes a unique Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.