TweetFollow Us on Twitter

Dec 01 REALbasic

Volume Number: 17 (2001)
Issue Number: 12
Column Tag: Declaring External Functions

by Joe Strout

Declaring External Functions in REALbasic

Harness the power of toolbox calls without resorting to C

Declaring Your Intentions

REALbasic’s extensive library floats on top of the sea of low-level functions that comprise the operating system like a cruise ship, protecting you from the dangers that lurk below. This is a two-sided coin; it frees you to concentrate on what you want to accomplish, rather than the details of how it’s done on this or that platform, but it also places limits on what you can do. When you hit one of those limits, and need to accomplish something not directly supported by the REALbasic library, then you’re going to have to get wet.

But even then, you have a choice. You could write a plug-in in C++, and link this plug-in into your REALbasic application [reference - Erik’s article on plugin dev]. That gives you full power to do pretty much anything, but it also requires quite a bit of learning, especially for developers not already familiar with C++. The other option is to use the Declare statement, which makes a function in REALbasic that links to a function in the operating system or a shared library. This is done entirely within REALbasic; no C++ is involved. To extend the ocean analogy a just bit further: writing a plug-in is like diving into the ocean untethered, while using a Declare is like hanging onto a life preserver on a rope — there are still restrictions on what you can do, but it’s also a lot easier to avoid drowning.

Though the Declare statement is used entirely within REALbasic, it’s still an advanced feature and a source of difficulty for many experienced REALbasic coders. This article will attempt to clarify and illustrate its use. After reading this article, you’ll be able to declare calls to classic MacOS, Carbon, or Win32 system routines, as well as to other shared libraries. This gives you the power to do a wide range of things not otherwise possible in REALbasic.

The Declare Statement: A Close Look

With no further ado, let’s jump right into an example. Listing 1 shows a REALbasic function that toggles the “modified” flag of a window. (Under Mac OS X, this sets or clears the little dot in the red Close widget in the title bar.) Since the functions involved are only available in MacOS 8.5 and higher, it also checks the system version before attempting to call them.

Listing 1: ToggleModified

ToggleModified

Toggle the “Modified” flag of the given REALbasic window.  This subroutine requires MacOS 8.5 or higher,
and does nothing under older versions of MacOS.  It will not compile as-is for 68k (these functions are 
not available to 68k apps).

Sub ToggleModified(win As Window)
   // Toggle the given window’s Modified flag
   Dim sysVersion, err As Integer

   #if TargetCarbon
   Declare Function IsWindowModified Lib “CarbonLib” (window 
         as WindowPtr) as Boolean
   Declare Function SetWindowModified Lib “CarbonLib” (window 
         as WindowPtr, modified as Boolean) as Integer
   #else
   Declare Function IsWindowModified Lib “WindowsLib” (window 
         as WindowPtr) as Boolean
   Declare Function SetWindowModified Lib “WindowsLib” (window 
         as WindowPtr, modified as Boolean) as Integer
   #endif

   // check the system version, since these functions are only available
   // in MacOS 8.5 and higher
   if System.Gestalt(“sysv”, sysVersion) and sysVersion >= 
            &h0850 then
      // system version is OK, so do the toggle
      err = SetWindowModified(win, not IsWindowModified(win))
    end if
  end if
end Sub

There are several things to notice in this example. First, if we want to compile for both “classic” Macintosh and for Carbon, we need two versions of each Declare — one linking against CarbonLib, and the other against the appropriate toolbox library (often InterfaceLib, but WindowsLib in this case). Next, built-in REALbasic functions generally do the right thing for any version of MacOS your code may be running on, but it can’t do so with a Declare; it’s your responsibility to make sure that the function you’re calling will be available. In this case, that means checking that the system version is at least 8.5. But once you’ve done all this, using the function you’ve declared is easy — just call it like any ordinary REALbasic method.

Let’s look at the Declare statements more closely. First, note that the declaration occurs within some other REALbasic method (subroutine or function). Just like a local variable, these declarations are local to where they’re declared; they will not be visible outside of this method. There is currently no way to make such a declaration global, though you could make a global wrapper function (as we’ve essentially done in this example).

The declaration always begins with the keyword “Declare” followed by either “Function” or “Sub”; REALbasic doesn’t actually care which you use, but by convention you use “Sub” when the C return type is void. Next is the name of the function, exactly as it appears in the external library. This is also the name used to invoke the function, unless you specify an “Alias” clause (which we’ll cover a bit later).

The next part of the declaration is the library name. This is the fragment name as it appears to the Code Fragment Manager, not the file name as it appears in the Finder; and unlike most words in REALbasic, this name is case sensitive. Finding this name is not hard, but we’ll come back to it a bit later.

Next come the function parameters, and finally the return type. This is where things get interesting. The documentation or header files on which you’re basing your Declare probably specify the parameter and return types with C syntax, rather than REALbasic. It may say something like “char *” or “Str255” or something unpalatable. What to do?

The Right Type for the Job

Most of the work in creating a Declare statement is in finding the right conversions between the C types you see in the headers and documentation, and the REALbasic types that serve the same purpose. Fortunately you have this article, which contains the very handy Table 1. This shows you some of the most common C types, and the corresponding REALbasic types you should use in a Declare statement.

Table 1: C types and REALbasic
equivalents for Declare statements

C data type Declare type Variable Type
Boolean Boolean Boolean
long Integer, OSType, Color Integer, String, Color
int
UInt32
SInt32
void*
Ptr
(etc.)
OSType OSType (parameters only) String
ResType
DescType
short Short Integer
unsigned short
UInt16
SInt16
float Single Single
double Double Double
Str255 PString String
Str63
Str31
unsigned char *
char * CString (parameters only) String
WindowPtr WindowPtr Window
WindowRef (parameters only)
void* Ptr MemoryBlock
Ptr
(or pointer to data structure)

The first column of this table is the data type the function is expecting, as you might see it in the documentation or the C header file. The middle column shows the corresponding types you could use in the Declare statement. Finally, the last column shows the “natural” REALbasic types which most closely match the declared types; these are what you would pass (or get as a result) when calling the function.

REALbasic uses the type you specify in the Declare to figure out how much data to push onto the call stack when making the call; so the most important thing is to get the size right. If the function is expecting any 4-byte value, you can probably declare this as type Integer. If you declare it as Short instead, REALbasic (when compiling for 68k processors) will push only two bytes, the function will try to grab four, and you’ll almost certainly crash.

Many of the type choices are very straightforward; if the function wants a float, declare it as a Single and pass it a number. If the function is expecting a Pascal string, declare it as type PString, and when you invoke the function, give it any ordinary string. REALbasic will automatically convert its internal string representation into a Str255 when calling the function (but note: if the function expects a shorter string than that, it’s up to you to make sure your string isn’t too long before making the call). Or if the return type is a Pascal string, the returned value will be automatically converted into a normal REALbasic string.

With other types, it’s not so clear cut. Suppose the function expects a pointer. Should you declare this as type Integer, or type Ptr? The answer depends on the details. If it’s a function that allocates some data structure, returns you a pointer to it, and you simply pass it back to other functions without trying to peek at the data, then an Integer will do fine. REALbasic integers are four bytes, and a pointer is four bytes, so treating the pointer as if it were just a number will make life easiest for you. Most modern Apple toolbox calls, such as those in Carbon or QuickDraw 3D, are of this sort, since the underlying data structures are opaque.

If, however, the function is expecting you to allocate storage for some data before calling the function, then a bit more work is required. In this case, declare the parameter as type Ptr. Allocate the memory in the form of a REALbasic MemoryBlock, created with the NewMemoryBlock function (and be sure to make it the right size!). If the function is expecting the data structure to be initialized with some values, you can use the MemoryBlock methods to stuff appropriate values into it. Then, pass this MemoryBlock to the function. From the function’s point of view, it just gets a pointer to some already-allocated memory. It may stuff values into this area, which you can later read by using those MemoryBlock methods again.

Calling a Spade a Spade

Your Declare statement must name the shared library which implements the function. In the case of a Windows DLL, you simply use the file name. In the Mac world, we don’t rely on file names, which are too frequently changed to be reliable. So you must use the fragment name as seen by the Code Fragment Manager (CFM). Most of the MacOS toolbox is in the fragment “InterfaceLib” (or “CarbonLib” for Carbon/OS X apps). But suppose you’re calling some other library — for example, the “Quesa” high-level 3D graphics library. The CFM name of this library is “QuickDraw™ 3D” (because it’s a binary-compatible replacement for Apple’s QuickDraw 3D technology). If you don’t happen to have me around to tell you handy facts like this, how would you know?

If you have ResEdit handy, it can give you the answer. Drop the library onto ResEdit and open the “cfrg” (code fragment) resource. This gives you a list of code fragments — usually there’s only one — and the name you’re looking for is labeled “Member Name”. Copy that value, and paste it into your Declare statement as the library name.

You can also use PEF Viewer, a free little utility from Apple. It also lists the code fragments it finds, and even lets you peek into the code, data, and loader information for each. (The last can be handy when Carbonizing — if the shared library you’re using imports any symbols from anywhere other than CarbonLib, you probably can’t use it in a Carbon app.) The CFM name of the library is given right at the top, next to the disclosure triangle.

If you don’t have either of these tools handy, you can use REALbasic itself. Just launch REALbasic, and drag the shared library file into the project window. It will appear there with its CFM name, rather than its file name. If you’re curious, what you have there is the alternative interface to importing functions from shared libraries — useful if you need a large number of functions from a single shared library, and you want them to be global rather than local. Since you’re using Declares rather than importing the library, you can just make note of the name, then delete the library from your project.

TBFinder to the Rescue

Writing Declares has always been a bit of an arcane art. Even armed with the knowledge in this article, you’ll find yourself wondering if it’s really worth the trouble when you just need a quick Mac toolbox call. Fortunately, there is a tool which can take much of the guess-work out of writing a toolbox-related Declare.

TBFinder is an open-source REALbasic project coordinated by Fabian Lidman. First, you point it to your copy of Universal Headers — i.e., Apple’s toolbox interface files, which you can download from Apple’s Development Kits web site. Then you can just find the toolbox call you need by searching or browsing, and it writes the Declare statement for you. There are some caveats, but in many cases it’s as simple as that.

TBFinder’s search window — what you see when you launch the utility — is shown in Figure 1. If you don’t know the name of the call you’re looking for, click “Browse” to get a nice hierarchical list of the functions defined in all the interface files, then just double-click the one you want. If you do know the name of the function you need, type it in the edit field; if you happen to know the header in which it’s declared, you can check the “Look first in:” checkbox and choose the appropriate header from the pop-up menu. Then click Search.


Figure 1. TBFinder search window.

The result, assuming the function actually exists, is a window like that shown in Figure 2.


Figure 2. TBFinder result window.

TBFinder’s search window — what you see when you launch the utility — is shown in Figure 2. The key bit is the second section, where the REALbasic syntax is shown. You can either use the Copy command to move this text to the clipboard, or drag the little text-clipping icon directly into REALbasic. The other icons in the window are actually buttons that do things when you click on them: the upper left icon by the header file name reveals that header file in the Finder; the text file icon by the original C declaration opens the header file (careful — Apple’s headers are set to open in Macintosh Programmer’s Workshop); and the globe at upper right does a web search for the function name in Apple’s documentation.

At the bottom of the window, you’ll notice a handy note telling you whether or not the function appears to be Carbon-compatible. If it is, you can use it in a Carbon application by simply changing the library name to “CarbonLib”. In the case of a classic application, TBFinder correctly identifies the library for you, using a big internal look-up table which I fervently hope was generated automatically by parsing the various system libraries.

TBFinder also supports one other feature of the Declare that I not previously mentioned: the “Inline68K” block. Many Mac toolbox calls on 68k machines are not shared library calls at all, but rather direct jumps to system traps. The C headers give the necessary machine language glue with the xWORDINLINE macros; TBFinder converts this to RB syntax, and even appends the extra glue needed in some cases by RB itself. (Be sure to get the latest version of TBFinder; version 2.0, included on the REALbasic CD, does not do this correctly.)

Finally, note that while TBFinder is invaluable, it isn’t infallible. In particular, it takes its best guess at the appropriate parameter conversions, but these often require human judgment (see Table 1 again). Always double-check the parameter types TBFinder has chosen, and consider whether there are other choices that make more sense for you.

Some Examples

Now that we’ve covered all the basics, let’s look at some real examples. We already saw one in Listing 1, used to toggle the “modified” flag on a window. Listing 2 shows another example, a call to the toolbox routine ObscureCursor, which hides the cursor until the mouse is moved. As this routine takes no parameters and returns no result, the declaration is very straightforward.

Listing 2: ObscureCursor

ObscureCursor
Hide the cursor until the mouse is moved (MacOS only).

Sub ObscureCursor()
   {
  // Declare it with a “Sys” prefix to avoid a name clash
   #if TargetCarbon
  Declare Sub SysObscureCursor Lib “CarbonLib” Alias 
            “ObscureCursor” ()
  #else
  Declare Sub SysObscureCursor Lib “InterfaceLib” Alias 
            “ObscureCursor” () Inline68K(“A856”)
  #endif
  
  // Call it!
  SysObscureCursor
end Sub

Note that in this example, I’ve chosen to name the REALbasic subroutine exactly like the toolbox routine. That means that when I declare the toolbox call within the subroutine, I’d have a name clash unless I use the “Alias” option. “Alias” allows me to declare the system routine as SysObscureCursor, avoiding thus avoiding the clash, and allowing all other users of this subroutine to simply call “ObscureCursor”.

Naturally, if you needed to use HideCursor/ShowCursor to hide the mouse even when it moves — say, because you’re making a game and have a custom cursor or no need for a cursor at all — then the declarations would be very similar. But let’s suppose you need to hide the cursor only within a rectangular region of the screen, perhaps because you’re doing some animation there, or simply because you want to demonstrate a Declare involving a MemoryBlock. The toolbox call for this is ShieldCursor. TBFinder gives the declaration as:

Declare Sub ShieldCursor Lib “InterfaceLib” (shieldRect as Ptr, offsetPt as Point) Inline68K(“A855”)

Apple’s documentation gives the first parameter as a Rect pointer, which TBFinder has rendered as type Ptr. From Table 1, you know that a MemoryBlock is needed for times like this. We use the MemoryBlock to manually construct a Rect structure, recalling that a Rect is just a set of four short integers representing top, left, bottom, and right

Dim m As MemoryBlock

m = NewMemoryBlock(8) // 8 bytes (four 2-byte ints)
m.Short(0) = r.top
m.Short(2) = r.left
m.Short(4) = r.top + r.height
m.Short(6) = r.left + r.width

We can then pass this as the first parameter to ShieldCursor. What about the second parameter, “offsetPt”? Again referring to Apple’s documentation, we find that this is the offset from the global coordinate system. In other words, it’s the top-left corner of the window containing the rectangle to which we’re referring. TBFinder has left this declared as “Point”, which is not a valid Declare parameter type. But since a Point is the same size as an Integer, we can fairly easily construct an integer that contains the information we need:

Dim point As Integer

// put pt.v into the high word, pt.h into the low word:
point = BitwiseAnd(self.top * 65536, &HFFFF0000)
point = BitwiseOr(point, BitwiseAnd(self.left, &H0000FFFF))

Then, we simply change the Declare statement so that the second parameter is of type Integer, and we’re ready to go. The full listing for the ShieldCursor function is shown in Listing 3.

Listing 3: ShieldCursor

ShieldCursor
Hide the cursor within the given rectangle control.

Sub ShieldCursor(r As RectControl)
     Declare Sub SysShieldCursor Lib “InterfaceLib” Alias “ShieldCursor” (shieldRect as Ptr, offsetPt 
     as Integer) Inline68K(“A855”)
  
  Dim m As MemoryBlock
  Dim point As Integer
  
  // construct the rect within which the cursor will be shielded
  m = NewMemoryBlock(8)  // 8 bytes (four 2-byte ints)
  m.Short(0) = r.top
  m.Short(2) = r.left
  m.Short(4) = r.top + r.height
  m.Short(6) = r.left + r.width
  
  // construct the point that converts global to local coordinates
  point = -self.top * 65536 - self.left   
                           // pt.v in the high word, pt.h in the low word
  
  // make the call
  SysShieldCursor m,point
End Sub

Let’s end with a Windows declaration. I know, this is a Mac programming magazine, but sometimes you need to deploy your app for that Other Platform — and the ability to do so easily is one of REALbasic’s strengths. However, with the Declare statement, you’re making a direct call to a toolbox or shared library routine; this is rarely portable. So the first thing you’ll need to do, is conditionalize your code so that Mac declares are included only in Mac builds, and Windows declares are included only in Windows builds. You can use either “#if TargetMacOS” or “#if TargetWin32” for this purpose.

Next, you’ll need information on the Windows API. Two good references are given at the end of this article.

Finally, you’ll need to construct the Declare statement by hand. TBFinder can’t help you here; but with this article on your shelf, you’re well-equipped for the task. So let’s dive into an example.

Windows applications can take command-line parameters, and these are often used to specify options or initial conditions for the application. REALbasic can get these one at a time via the OpenDocument event, but in many cases it’d be preferable to get the whole command line. A search on the MSDN Online Library quickly leads to the GetCommandLine function, declared in the documentation as:

LPTSTR GetCommandLine(VOID);

This function takes no parameters, making our lives easy in that respect. And the return type, it turns out, is a C string of either ASCII or UniCode characters. This is an important point: many Windows functions come in both ASCII and UniCode versions, as signified under “Requirements” with a note such as “Unicode: Implemented as Unicode and ANSI versions on all platforms.” When you see this, it means that the function name given is not the real function name — you need to append “A” for the ASCII version, or “W” for the UniCode version. So the actual function name, for our purposes, is GetCommandLineA.

The return type in this case is an ordinary C string. But, while REALbasic knows how to automatically convert its String type into a CString for use as a parameter, it can’t do the opposite conversion. So we need another approach. A C string is really just a pointer to some data, so we can declare the return type as “Ptr”, and use a MemoryBlock to get at the data. The result is shown in Listing 4.

Listing 4: GetCommandLine

GetCommandLine
Get the full Win32 command line, including executable name and parameters.

Function GetCommandLine()
  #If TargetWin32
  Dim m as MemoryBlock
  Declare Function GetCommandLineA Lib “KERNEL32.DLL” () as Ptr
  m = GetCommandLineA()
  Return m.cstring(0)   // get the C string to which m points
  #Else
  return “Command line available on Win32 only.”
  #EndIf
End Function

There is one other trick that can sometimes make Windows declares easier. If you can find a web site or other documentation with Declare statements for Visual Basic — such as the last reference given at the end of this article — then you can use those Declare statements in REALbasic almost verbatim. You’ll just need to make a few changes: the “ByVal” keyword can be omitted, and data types may have to be translated according to Table 1 (e.g., use “Integer” where VB says “Long”).

Conclusion

REALbasic tries to abstract away the details of the underlying operating system, so you don’t have to worry about whether you’re running on classic MacOS, OS X, or some flavor of Windows. But sometimes you need more control. You may need to talk directly with the OS, or you may need access to some other shared library (such as OpenGL, for example). In those cases, the Declare statement is invaluable.

The Declare statement allows you to do most of the same things you could do directly in C. There are some limitations; you can’t call a function that requires a callback, and you can’t directly invoke a function pointer (e.g., to call a mach-o entry point loaded via the CFBundle API). For those, you’ll still need to write a plug-in. But for most other needs, you can Declare your external calls, providing a neater, more integrated solution.

While Declare gives you nearly the power of C, it gives you nearly the complexity of C as well. Writing a Declare statement is error-prone, and if done incorrectly, can crash your program (and under Classic MacOS, the system as well). Deciphering the headers and documentation for the calls you’re making requires some understanding of how things work in C or Pascal. But, with the tips gained here and a bit of study and experimentation, there will soon be little you can’t do in REALbasic.

References

REALbasic Home Page
http://www.realsoftware.com/realbasic

TBFinder Home Page
http://homepage.mac.com/fabianl/development/

Apple’s Developer Documentation
http://developer.apple.com/techpubs/

Apple’s Software Development Kits
http://developer.apple.com/sdk/

Microsoft’s Official SDK Documentation
http://msdn.microsoft.com/library/

Unofficial Windows API Guide
http://www.vbapi.com/ref/index.html


Joe Strout is a neuroscientist-turned-software engineer currently working for REAL Software, Inc. You can reach him at joe@strout.net.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Luminar 3.1.4 - Powerful, adaptive, conf...
Luminar is the new full-featured image editor that adapts to the way you edit photos. Over 300 essential tools to fix, edit, and enhance your photos with comfort. The future of photo editing is here... Read more
SketchUp 19.3.252 - Create 3D design con...
SketchUp is an easy-to-learn 3D modeling program that enables you to explore the world in 3D. With just a few simple tools, you can create 3D models of houses, sheds, decks, home additions,... Read more
VirtualBox 6.0.14 - x86 virtualization s...
VirtualBox is a family of powerful x86 virtualization products for enterprise as well as home use. Not only is VirtualBox an extremely feature rich, high performance product for enterprise customers... Read more
TinkerTool 7.42 - Expanded preference se...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Paperless 3.0.7 - $69.95
Paperless is a digital documents manager. Remember when everyone talked about how we would soon be a paperless society? Now it seems like we use paper more than ever. Let's face it - we need and we... Read more
Yojimbo 4.1.3 - Data organizer.
Yojimbo empowers Mac users to manage, effortlessly and securely, the onslaught of information encountered every day at work and at home, even across multiple computers. Yojimbo stores different data... Read more
Ableton Live 10.1.3 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Tinderbox 8.1.0 - Store and organize you...
Tinderbox is a personal content management assistant. It stores your notes, ideas, and plans. It can help you organize and understand them. And Tinderbox helps you share ideas through Web journals... Read more
Microsoft Office 365, 2019 16.30 - Popul...
Microsoft Office 365. The essentials to get it all done. Unmistakably Office, designed for Mac Get started quickly with new, modern versions of Word, Excel, PowerPoint, Outlook and OneNote-... Read more
RoboForm 8.6.5 - Password manager; syncs...
RoboForm is a password manager that offers one-click login, mobile syncing, easy form filling, and reliable security. Password Manager. RoboForm remembers your passwords so you don't have to! Just... Read more

Latest Forum Discussions

See All

Alluris is a choose-your-own adventure g...
Alluris is an RPG that the developer's are calling a swipe-your-own adventure game. This is because the game incorporates a Reigns-style - swiping left or right - selection mechanic to make all the decisions you'd usually expect to make across... | Read more »
Hello Hero All Stars receives update wit...
The first Hello Hero game hit global platforms in 2013 and proved a huge success, with developer Fincon adding two more entries to this popular series of casual RPG games since. Released in June this year, Hello Hero All Stars brings many of the... | Read more »
Zombieland: Double Tapper, a cartoon idl...
Zombieland: Double Tapper is the idle RPG tie-in to the upcoming Zombieland: Double Tap. Oddly, it's one of two different Zombieland games launching today, with the other being the Switch title Zombieland: Double Tap - Road Trip. [Read more] | Read more »
Rusty Lake's The White Door launche...
Rusty Lake and Second Maze's intriguing point-and-click adventure game, The White Door, is now up for pre-order on the App Store. This one sees you playing as Robert Hill, a mental health patient who is suffering from severe memory loss. The game... | Read more »
Hellrule is an auto-runner inspired by G...
Hellrule is an upcoming auto-runner game from independent developer Pedrocorp where players will take control of a dapperly dressed gentlemen who comes equipped with a razor-sharp umbrella for slicing up his foes. The game will be available for... | Read more »
Grobo is a gravity bending puzzle platfo...
Grobo is a 2D puzzle platformer that marks the first release from developers Hot Chocolate Games. You'll find yourself manipulating gravity as you make your through this title that's available now for iOS and Android. [Read more] | Read more »
Adrenaline, Compulsive Entertainment’s h...
Compulsive Entertainment’s high-octane arcade racer, Adrenaline, has now made its way to the App Store following a successful launch on Google Play. It’s a ton of challenging, fast-paced fun, boasting easy-to-learn controls and a varied selection... | Read more »
Mario Kart Tour is adding Super Mario Ga...
Earlier today on Twitter, Nintendo announced that Mario Kart Tour is getting a new racer and track. Fans of Super Mario Galaxy will be pleased to hear that Rosalina is the first post-launch character being added, while the iconic Rainbow Road is... | Read more »
$100,000 up for grabs at World of Tanks...
The fourth annual Blitz Twister Cup will be held in Minsk (Belarus) on November 9th. For those not in the know, the Blitz Twister Cup is an eSports championship for the hugely popular World of Tanks Blitz. [Read more] | Read more »
Brown Dust’s crossover event with That T...
Brown Dust, Neowiz’s epic fantasy RPG, is no stranger to special events, though its latest crossover might be its most exciting yet. On top of a challenging new dungeon, fan-favourite characters from the hit anime series That Time I Got... | Read more »

Price Scanner via MacPrices.net

Lease one iPhone Xs, Xr, or X at Sprint and g...
Purchase an Apple iPhone X, Xr, Xs, or Xs Max at Sprint, and get a 64GB iPhone Xr for free. Requires 2 new lines or 1 upgrade-eligible line and 1 new line. The fine print: “iPhone Xs (64 GB) $37.50/... Read more
How to use your Apple Education discount to s...
Purchase a new MacBook Pro or MacBook Air using Apple’s Education discount, and take up to $200 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address... Read more
Base 2019 13″ 1.4GHz 4-Core MacBook Pro on sa...
Amazon has the new 2019 13″ 1.4GHz/128GB 4-Core Space Gray Touch Bar MacBook Pro on sale for $100 off Apple’s MSRP. This is the same MacBook Pro sold by Apple in its retail and online stores: – 2019... Read more
Save up to $30 on Apple’s AirPods at these re...
Amazon is offering discounts on new 2019 Apple AirPods ranging up to $30 off MSRP. Shipping is free: – AirPods with Charging Case: $144 $15 off MSRP – AirPods with Wireless Charging Case: $169 $30... Read more
Save $15 on Apple Watch Series 5 models on Wa...
Walmart has new Apple Watch Series 5 models on sale for $15 off Apple’s MSRP on their online store. Choose free shipping or free local store pickup (if available). Sale prices for online orders only... Read more
Save $750 on the base 8-Core 27″ iMac Pro wit...
Apple has Certified Refurbished 27″ 3.2GHz 8-Core iMac Pros available for $4249 including free shipping. Their price is $750 off the cost of new models. A standard Apple one-year warranty is included... Read more
Apple continues to offer 11″ iPad Pros, refur...
Apple has Certified Refurbished 11″ iPad Pros available on their online store for up to $220 off the cost of new models. Prices start at $679. Each iPad comes with a standard Apple one-year warranty... Read more
Get Beats Solo3 Wireless Headphones today for...
Amazon has Beats Solo3 Wireless On-Ear Headphones in stock and on sale today for $139.99 shipped. Their price is $60 off Apple’s MSRP, and it’s the lowest price available for this model from any of... Read more
Sale! 2019 13″ MacBook Airs for $200 off Appl...
Amazon has new 2019 13″ MacBook Airs on sale for $200 off Apple’s MSRP, with prices starting at $899, each including free shipping. Be sure to select Amazon as the seller during checkout, rather than... Read more
Verizon offers $150 discount on iPhone 8, 8 P...
Use code SMART150 at checkout at Verizon to take $150 off the price of an iPhone 8, iPhone 8 Plus, or iPhone X. New line of service required. Their discount reduces the price of a 64GB iPhone 8 to $... Read more

Jobs Board

Best Buy *Apple* Computing Master - Best Bu...
**730598BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 001129-Wheaton-Store **Job Description:** The Read more
Best Buy *Apple* Computing Master - Best Bu...
**741153BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Sales **Location Number:** 000411-Flint-Store **Job Description:** **What does a Best Read more
Surgical Technologist III, *Apple* Hill Sur...
Surgical Technologist III, Apple Hill Surgical Center - Full Time Tracking Code APPHILLST Job Description Surgical Technologist III Apple Hill Surgical Center 25 Read more
Geek Squad *Apple* Master Consultation Agen...
**741154BR** **Job Title:** Geek Squad Apple Master Consultation Agent **Job Category:** Services/Installation/Repair **Location Number:** Read more
Best Buy *Apple* Computing Master - Best Bu...
**740900BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 000387-Randall Road-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.