TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// PreQuickDraw.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program opens a window in which is displayed some information extracted from
// the GDevice structure for the main video device and some colour information extracted
// from the window's colour graphics port structure.  When the monitor is set to 256 
// colours or less, the colours in the colour table in the GDevice structure's pixel map
// structure are also displayed.
//
// A Demonstration menu, which is enabled if the monitor is a direct device set to 256
// colours or less at program start, allows the user to set the monitor to 16-bit colour,
// and restore the original pixel depth, using application-defined functions. 
//
// The program utilises 'MBAR', 'MENU', 'WIND', and 'STR#' resources, and a 'SIZE' 
// resource with the is32BitCompatible flag set.
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

// ............................................................................. includes

#include <Appearance.h>
#include <Devices.h>
#include <Palettes.h>
#include <LowMem.h>
#include <Sound.h>
#include <ToolUtils.h>

// .............................................................................. defines

#define rMenubar              128
#define rWindow               128
#define mApple                128
#define  iAbout               1
#define mFile                 129
#define  iQuit                11
#define mDemonstration        131
#define  iSetDepth            1
#define  iRestoreDepth        2
#define rIndexedStrings       128
#define  sMonitorInadequate   1
#define  sSettingPixelDepth16 2
#define  sMonitorIsDepth16    3  
#define  sMonitorIsDepthStart 4 
#define  sRestoringMonitor    5
#define MAXLONG               0x7FFFFFFF
#define topLeft(r)            (((Point *) &(r))[0])
#define botRight(r)           (((Point *) &(r))[1])

// ..................................................................... global variables

Boolean  gDone;
SInt16   gStartupPixelDepth;

// .................................................................. function prototypes

void    main                        (void);
void    doInitManagers              (void);
void    doEvents                    (EventRecord *);
void    doDisplayInformation        (WindowPtr);
Boolean doCheckMonitor             (void);
void    doSetMonitorPixelDepth      (void);
void    doRestoreMonitorPixelDepth  (void);
void    doMonitorAlert              (Str255);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× main

void  main(void)
{
  Handle        menubarHdl;
  MenuHandle    menuHdl;
  WindowPtr      windowPtr;
  Str255        theString;
  EventRecord    EventStructure;

  // ................................................................ initialise managers

  doInitManagers();
  
  // .......................................................... set up menu bar and menus
  
  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);

  menuHdl = GetMenuHandle(mApple);
  if(menuHdl == NULL)
    ExitToShell();
  else
    AppendResMenu(menuHdl,'DRVR');

  if(!(doCheckMonitor()))
  {
    GetIndString(theString,rIndexedStrings,sMonitorInadequate);
    doMonitorAlert(theString);
    menuHdl = GetMenuHandle(mDemonstration);
    DisableItem(menuHdl,0);
  }
  else
  {
    if(gStartupPixelDepth > 8)
    {
      menuHdl = GetMenuHandle(mDemonstration);
      DisableItem(menuHdl,0);
    }
  }
        
  DrawMenuBar();
  
  // ............................ open windows, set font size, show windows, move windows

  if(!(windowPtr = GetNewCWindow(rWindow,NULL,(WindowPtr)-1)))
    ExitToShell();

  SetPort(windowPtr);
  TextSize(10);


  // .................................................................... enter eventLoop

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&EventStructure,MAXLONG,NULL))
      doEvents(&EventStructure);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInitManagers

void  doInitManagers(void)
{
  MaxApplZone();
  MoreMasters();

  InitGraf(&qd.thePort);
  InitFonts();
  InitWindows();
  InitMenus();
  TEInit();
  InitDialogs(NULL);

  InitCursor();  
  FlushEvents(everyEvent,0);

  RegisterAppearanceClient();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  SInt8     charCode;
  SInt32    menuChoice;
  SInt16    menuID, menuItem;
  SInt16    partCode;
  WindowPtr windowPtr;
  Str255    itemName;
  SInt16    daDriverRefNum;
  Rect      theRect;
  
  switch(eventStrucPtr->what)
  {
    case keyDown:
    case autoKey:
      charCode = eventStrucPtr->message & charCodeMask;
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        menuChoice = MenuEvent(eventStrucPtr);
        menuID = HiWord(menuChoice);
        menuItem = LoWord(menuChoice);
        if(menuID == mFile && menuItem  == iQuit)
          gDone = true;
      }
      break;
  
    case mouseDown:
      if(partCode = FindWindow(eventStrucPtr->where,&windowPtr))
      {
        switch(partCode)
        {
          case inMenuBar:
            menuChoice = MenuSelect(eventStrucPtr->where);
            menuID = HiWord(menuChoice);
            menuItem = LoWord(menuChoice);

            if(menuID == 0)
              return;

            switch(menuID)
            {
              case mApple:
                if(menuItem == iAbout)
                  SysBeep(10);
                else
                {
                  GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
                  daDriverRefNum = OpenDeskAcc(itemName);
                }
                break;

              case mFile:
                if(menuItem == iQuit)
                  gDone = true;
                break;
  
              case mDemonstration:
                if(menuItem == iSetDepth)
                  doSetMonitorPixelDepth();
                else if(menuItem == iRestoreDepth)
                  doRestoreMonitorPixelDepth();
                break;
            }
            HiliteMenu(0);
            break;
          
          case inDrag:
            DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
            theRect = windowPtr->portRect;
            theRect.right = windowPtr->portRect.left + 250;
            InvalRect(&theRect);
            break;
        }
      }
      break;
      
    case updateEvt:
      windowPtr = (WindowPtr) eventStrucPtr->message;
      BeginUpdate(windowPtr);
      SetPort(windowPtr);
      EraseRect(&windowPtr->portRect);
      doDisplayInformation(windowPtr);
      EndUpdate(windowPtr);
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDisplayInformation

void  doDisplayInformation(WindowPtr windowPtr)
{
  RGBColor      whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
  RGBColor      blueColour  = { 0x4444, 0x4444, 0x9999 };
  GDHandle      deviceHdl;
  SInt16        videoDeviceCount = 0;  
  Str255        theString;
  SInt16        deviceType, pixelDepth, bytesPerRow;
  Rect          theRect;
  PixMapHandle  pixMapHdl;
  CGrafPtr      cgrafPtr;
  SInt32        pixelValue;
  SInt16        redComponent, greenComponent, blueComponent;
  CTabHandle    colorTableHdl;
  SInt16        entries = 0, a, b, c = 0;
  RGBColor      theColour;

  RGBForeColor(&whiteColour);
  RGBBackColor(&blueColour);
  EraseRect(&windowPtr->portRect);

  // .................................................................... Get Device List

  deviceHdl = LMGetDeviceList();

  // ................................................. count video devices in device list

  while(deviceHdl != NULL)
  {
    if(TestDeviceAttribute(deviceHdl,screenDevice))
      videoDeviceCount ++;

    deviceHdl = GetNextDevice(deviceHdl);
  }

  NumToString((SInt32) videoDeviceCount,theString);
  MoveTo(10,20);
  DrawString(theString);
  if(videoDeviceCount < 2)
    DrawString("\p video device in the device list.");
  else
    DrawString("\p video devices in the device list.");

  // .................................................................... Get Main Device

  deviceHdl = LMGetMainDevice();

  // .............................................................. determine device type

  MoveTo(10,35);
  if(BitTst(&(**deviceHdl).gdFlags,15 - gdDevType))
    DrawString("\pThe main video device is a colour device.");
  else
    DrawString("\pThe main video device is a monochrome device.");

  MoveTo(10,50);
  deviceType = (**deviceHdl).gdType;
  switch(deviceType)
  {
    case clutType:
      DrawString("\pIt is an indexed device with variable CLUT.");
      break;

    case fixedType:
      DrawString("\pIt is is an indexed device with fixed CLUT.");
      break;

    case directType:
      DrawString("\pIt is a direct device.");
      break;
  }

  // ............................................................ Get Handle to Pixel Map

  pixMapHdl = (**deviceHdl).gdPMap;

  // .............................................................. determine pixel depth

  MoveTo(10,70);
  DrawString("\pPixel depth = ");
  pixelDepth = (**pixMapHdl).pixelSize;
  NumToString((SInt32) pixelDepth,theString);
  DrawString(theString);

  // ..................................................... Get Device's Global Boundaries

  theRect = (**deviceHdl).gdRect;

  // ................................ determine bytes per row and total pixel image bytes

  MoveTo(10,90);
  bytesPerRow = (**pixMapHdl).rowBytes & 0x7FFF;
  DrawString("\pBytes per row = ");
  NumToString((SInt32) bytesPerRow,theString);
  DrawString(theString);

  MoveTo(10,105);
  DrawString("\pTotal pixel image bytes = ");
  NumToString((SInt32) bytesPerRow * theRect.bottom,theString);
  DrawString(theString);

  // ................. convert device's global boundaries to coordinates of graphics port

  GlobalToLocal(&topLeft(theRect));
  GlobalToLocal(&botRight(theRect));
  
  MoveTo(10,125);
  DrawString("\pBoundary rectangle top = ");
  NumToString((SInt32) theRect.top,theString);
  DrawString(theString);

  MoveTo(10,140);
  DrawString("\pBoundary rectangle left = ");
  NumToString((SInt32) theRect.left,theString);
  DrawString(theString);

  MoveTo(10,155);
  DrawString("\pBoundary rectangle bottom = ");
  NumToString((SInt32) theRect.bottom,theString);
  DrawString(theString);

  MoveTo(10,170);
  DrawString("\pBoundary rectangle right = ");
  NumToString((SInt32) theRect.right,theString);
  DrawString(theString);

  // ................................................ Get Pointer to Colour Graphics Port

  cgrafPtr = (CGrafPtr) windowPtr;

  // .............................................. determine requested background colour

  MoveTo(10,190);
  GetBackColor(&blueColour);
  DrawString("\pRequested background colour (rgb) = ");;
  MoveTo(10,205);
  NumToString((SInt32) blueColour.red,theString);
  DrawString(theString);
  DrawString("\p  ");
  NumToString((SInt32) blueColour.green,theString);
  DrawString(theString);
  DrawString("\p  ");
  NumToString((SInt32) blueColour.blue,theString);
  DrawString(theString);

  // .................................................... get actual colour (pixel value)

  pixelValue = cgrafPtr->bkColor;

  // ...... if direct device, extract colour components, else retrieve colour table index 

  MoveTo(10,220);

  if(deviceType == directType)
  {
    if(pixelDepth == 16)
    {
      redComponent    = pixelValue >> 10 & 0x0000001F;
      greenComponent  = pixelValue >> 5 & 0x0000001F;
      blueComponent    = pixelValue & 0x0000001F;
    }
    else if (pixelDepth == 32)
    {
      redComponent    = pixelValue >> 16 & 0x000000FF;
      greenComponent  = pixelValue >> 8 & 0x000000FF;
      blueComponent    = pixelValue & 0x000000FF;
    }

    DrawString("\pBackground colour used (rgb) = ");
    MoveTo(10,235);
    
    NumToString((SInt32) redComponent,theString);
    DrawString(theString);    
    DrawString("\p  ");

    NumToString((SInt32) greenComponent,theString);
    DrawString(theString);    
    DrawString("\p  ");

    NumToString((SInt32) blueComponent,theString);
    DrawString(theString);    
  }
  else if(deviceType == clutType || deviceType == fixedType)
  {
    DrawString("\p Background colour used (color table index) = ");    
    MoveTo(10,235);
    NumToString((SInt32) pixelValue,theString);
    DrawString(theString);
  }

  // ......................................................... Get Handle to Colour Table

  colorTableHdl = (*pixMapHdl)->pmTable;

  // ................................... if any entries in colour table, draw the colours

  MoveTo(250,20);
  DrawString("\pColour table in GDevice's PixMap:");

  entries = (*colorTableHdl)->ctSize;

  if(entries < 2)
  {
    MoveTo(260,105);
    DrawString("\pDummy (one entry) colour table only.");
    MoveTo(260,120);
    DrawString("\pTo get some entries, set the monitor to");
    MoveTo(260,135);
    DrawString("\p 256 colours, causing it to act like an");
    MoveTo(260,150);
    DrawString("\p                    indexed device.");
    SetRect(&theRect,250,28,458,236);
    FrameRect(&theRect);
  }

  for(a=28;a<224;a+=13)
  {
    for(b=250;b<446;b+=13)
    {
      if(c > entries)
        break;
      SetRect(&theRect,b,a,b+12,a+12);
      theColour = (*colorTableHdl)->ctTable[c++].rgb;
      RGBForeColor(&theColour);
      PaintRect(&theRect);
      if((deviceType == clutType || deviceType == fixedType) && c - 1 == pixelValue)
      {
        RGBForeColor(&whiteColour);
        InsetRect(&theRect,-1,-1);
        FrameRect(&theRect);
      }
    }
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCheckMonitor

Boolean  doCheckMonitor(void)
{
  GDHandle      mainDeviceHdl;

  mainDeviceHdl = LMGetMainDevice();

  if(!(HasDepth(mainDeviceHdl,16,0,0)))
    return false;
  else
  {
    gStartupPixelDepth = (**((**mainDeviceHdl).gdPMap)).pixelSize;
    return true;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetMonitorPixelDepth

void  doSetMonitorPixelDepth(void)
{
  GDHandle  mainDeviceHdl;
  Str255    alertString;  
  SInt16    pixelDepth;
  
  mainDeviceHdl = LMGetMainDevice();
  pixelDepth = (**((**mainDeviceHdl).gdPMap)).pixelSize;

  if(pixelDepth != 16)
  {
    GetIndString(alertString,rIndexedStrings,sSettingPixelDepth16);
    doMonitorAlert(alertString);
    SetDepth(mainDeviceHdl,16,0,0);
  }
  else
  {
    GetIndString(alertString,rIndexedStrings,sMonitorIsDepth16);
    doMonitorAlert(alertString);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRestoreMonitorPixelDepth

void  doRestoreMonitorPixelDepth(void)
{
  GDHandle  mainDeviceHdl;
  Str255    alertString;  
  SInt16    pixelDepth;

  mainDeviceHdl = LMGetMainDevice();
  pixelDepth = (**((**mainDeviceHdl).gdPMap)).pixelSize;

  if(pixelDepth != gStartupPixelDepth)
  {
    GetIndString(alertString,rIndexedStrings,sRestoringMonitor);
    doMonitorAlert(alertString);
    SetDepth(mainDeviceHdl,gStartupPixelDepth,0,0);
  }
  else
  {
    GetIndString(alertString,rIndexedStrings,sMonitorIsDepthStart);
    doMonitorAlert(alertString);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMonitorAlert

void  doMonitorAlert(Str255 labelText)
{
  AlertStdAlertParamRec paramRec;
  SInt16                itemHit;
  
  paramRec.movable        = true;
  paramRec.helpButton     = false;
  paramRec.filterProc     = NULL;
  paramRec.defaultText    = (StringPtr) kAlertDefaultOKText;
  paramRec.cancelText     = NULL;
  paramRec.otherText      = NULL;
  paramRec.defaultButton  = kAlertStdAlertOKButton;
  paramRec.cancelButton   = 0;
  paramRec.position       = kWindowDefaultPosition;

  StandardAlert(kAlertNoteAlert,labelText,NULL,¶mRec,&itemHit);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

Demonstration Program Comments

When this program is first run, the user should:

*   Drag the window to various position on the main screen, noting the changes to the
    coordinates of the boundary rectangle.

*   Open the Monitors and Sound control panel and, depending on the characteristics of
    the user's system:

*   Change between the available resolutions, noting the changes in the bytes per row
    and total pixel image bytes figures displayed in the window.

*   Change between the available colour depths, noting the changes to the pixel depth
    and total pixel image bytes figures, and the background colour used figures,
    displayed in the window.

*   Note that, when 256 or less colours are displayed on a direct device (in colours and
    grays), the device creates a CLUT and operates like a direct device.  In this case,
    the background colour used figure is the colour table entry (index), and the relevant
    colour in the colour table display is framed in white.

Assuming the user's monitor is a direct colour device, the user should then run the
program again with the monitor set to display 256 colours prior to program start.  The
Demonstration menu and its items will be enabled.  The user should then choose the items
in the Demonstration menu to set the monitor to a pixel depth of 16 and back to the
startup pixel depth.

main

Before DrawMenuBar is called, a call to the application-defined function doCheckMonitor
assigns the startup pixel depth to a global variable and determines whether the main
device supports 16-bit colour.  If the main device does not support 16-bit colour, the
Demonstration menu is disabled.  If the main device does support support 16-bit colour,
the Demonstration menu is disabled only if the current pixel depth is not 8 (256 colours)
or less.

doEvents

In the case of a mouse-down event, in the inDrag case, when the user releases the mouse
button, the left half of the window is invalidated, causing the left half to be redrawn
with the new boundary rectangle coordinates.

doDisplayInformation

In the first three lines, RGB colours are assigned to the window's colour graphics port's
rgbFgColor and rgbBkColor fields.  The call to EraseRect causes the content region to be
filled with the background colour.

Get Device List

The call to LMGetDeviceList gets a handle to the first GDevice structure in the device
list.  The device list is then "walked" in the while loop.  For every video device found
in the list, the variable videoDeviceCount is incremented.  GetNextDevice gets a handle
to the next device in the device list.

Get Main Device

LMGetMainDevice gets a handle to the startup device, that is, the device on which the
menu bar appears.

The call to BitTest with the gdDevType flag determines whether the main (startup) device
is a colour or black-and-white device.  In the next block, the gdType field of the
GDevice structure is examined to determine whether the device is an indexed device with a
variable CLUT, an indexed device with a fixed CLUT, or a direct device (or a direct
device set to display 256 colours or less and, as a consequence, acting like an indexed
device).

Get Handle to Pixel Map

At the first line of this block, a handle to the GDevice structure's pixel map is
retrieved from the gdPMap field.

In the next block, the pixel depth is extracted from the PixMap structure's pixelSize
field.

Get Device's Global Boundaries

At the first line of this block, the device's global boundaries are extracted from the
GDevice structure's gdRect field.

At the next block, the number of bytes in each row in the pixel map is determined.  (The
high bit in the rowBytes field of the PixMap structure is a flag which indicates whether
the data structure is a PixMap structure or a BitMap structure.)

At the next block, the bytes per row value is multiplied by the height of the boundary
rectangle to arrive at the total number of bytes in the pixel image.

The two calls to GlobalToLocal convert the boundary rectangle coordinates to coordinates
local to the colour graphics port.

Get Pointer To Colour Graphics Port

The first line simply casts the windowPtr to a pointer to a colour graphics port so that,
later on, the bkColor field can be accessed.

The next block gets the current (requested) background colour using the function
GetBackColor, and then extracts the red, green, and blue components.

At the next line, the pixel value in the bkColor field of the colour graphics port is
retrieved.  This is an SInt32 value holding either the red, green, and blue components of
the background colour actually used for drawing (direct device) or the colour table entry
used for drawing (indexed devices).

For direct devices with a pixel depth of 16, the first 15 bits hold the three RGB
components.  For direct devices with a pixel depth of 32, the first 24 bits hold the RGB
components.  These are extracted in the if(deviceType == directType) block.  For indexed
devices the value is simply the colour table entry (index) determined by the Color
Manager to represent the nearest match to the requested colour.

Get Handle To Colour Table

The first and fourth lines get a handle to the colour table in the GDevice structure's
pixel map and the number of entries in that table.

The final block paints small coloured rectangles for each entry in the colour table.  If
the main device is an indexed device (or if it is a direct device set to display 256
colours or less), the colour table entry being used as the best match for the requested
background colour is outlined in white.

doCheckMonitor

doCheckMonitor is called at program start to determine whether the main device supports
16-bit colour and, if it does, to assign the main device's pixel depth at startup to the
global variable gStartupPixelDepth.  

The call to LMGetMainDevice gets a handle to the main device's GDevice structure.  The
function HasDepth is used to determine whether the device supports 16-bit colour.  The
pixel depth is extracted from the pixelSize field of the PixMap structure in the GDevice
structure.

doSetMonitorPixelDepth

doSetMonitorPixelDepth is called when the first item in the Demonstration menu is chosen
to set the main device's pixel depth to 16.

If the current pixel depth determined at the first two lines is not 16, a string is
retrieved from a 'STR#' resource and passed to the application-defined function
doMonitorAlert, which displays a movable modal alert box advising the user that the
monitor's bit depth is about to be changed to 16.  When the user dismisses the alert box,
SetDepth sets the main device's pixel depth to 16.

If the current pixel depth is 16, the last two lines display an alert box advising the
user that the device is currently set to that pixel depth.

doRestoreMonitorPixelDepth

doRestoreMonitorPixelDepth is called when the second item in the Demonstration menu is
chosen to reset the main device's pixel depth to the startup pixel depth.

If the current pixel depth determined at the first two lines is not equal to the startup
pixel depth, a string is retrieved from a 'STR#' resource and passed to the
application-defined function doMonitorAlert, which displays a movable modal alert box
advising the user that the monitor's bit depth is about to be changed to the startup
pixel depth.  When the user dismisses the alert box, SetDepth sets the main device's
pixel depth to the startup pixel depth.

If the current pixel depth is the startup pixel depth, the last two lines display an
alert box advising the user that the device is currently set to that pixel depth.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more

Jobs Board

*Apple* Systems Administrator - JAMF - Syste...
Title: Apple Systems Administrator - JAMF ALTA is supporting a direct hire opportunity. This position is 100% Onsite for initial 3-6 months and then remote 1-2 Read more
Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.