Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Snapshot.c
/* |
File: Snapshot.c |
Contains: This application demonstrates how to quickly and |
efficiently capture the main device's desktop into |
a window. The program basically reads the image |
stored in the the main device's pixmap then copies |
it to a custom pixmap. The custom pixmap is de- |
fined at the same depth of the main device and |
contains an identical copy of that device's color- |
table. This is done to provide the fastest |
performance possible when copying from an offscreen |
to onscreen pixmap. By making sure the pixel values |
map to the exact same colors in both colortables, |
copybits will do a direct transfer of bits without |
wasting time remapping the colors. Also the ctSeed |
field for each colortable should be the same. Finally, |
since the main device's bounding rect is different |
than that of the offscreen's, the copying performance |
for the device to the offscreen is slightly affected |
because of the scaling required. However, the copying |
performance for the offscreen to the window is the |
best possible since the bounding rects for each are |
identical. |
Written by: EL |
Copyright: Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
08/2000 JM Carbonized, non-Carbon code is commented out |
for demonstration purposes. |
11/6/1999 GGS Updated to work with modern (1999) Mac OS. |
Fixed a PixMap disposing bug. Updated casts |
and headers. |
7/14/1999 KG Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#ifdef __APPLE_CC__ |
#include <Carbon/Carbon.h> |
#else |
#include <Carbon.h> |
#endif |
/* Constant Declarations */ |
#define WWIDTH ((qd.screenBits.bounds.right - qd.screenBits.bounds.left) / 2) |
#define WHEIGHT ((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) / 2) |
#define WLEFT (((qd.screenBits.bounds.right - qd.screenBits.bounds.left) - WWIDTH) / 2) |
#define WTOP (((qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) - WHEIGHT) / 2) |
#define MENU_BAR_ID 128 |
#define MENU_BAR_IDX 129 |
#define FILE_MENU 128 |
enum { |
FILE_NEW = 1, |
FILE_REFRESH, |
FILE_CLOSE, |
FILE_SAVE, |
FILE_QUIT |
}; |
/* Global Variable Definitions */ |
WindowPtr gWindow; /* Main window */ |
PixMap gPixMap; /* Offscreen pixmap */ |
Rect gBounds; /* Offscreen pixmap's bounding rect */ |
Boolean gDone = false; // Application termination global |
void initMac(); |
void setUp(); |
void handleMenuSelection(long result); |
void handleKeyPress(EventRecord *); |
void saveToPICTFile(); |
void createWindow(); |
void initPixmap(); |
void createImage(); |
void drawImage(); |
pascal OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn); |
void doEventLoop(); |
int main() |
{ |
initMac(); |
setUp(); |
createWindow(); /* Create a window to display the final image. */ |
initPixmap(); /* Initialize offscreen pixmap. */ |
createImage(); /* Copy the main screen's pixmap into the offscreen's. */ |
drawImage(); /* Copy the offscreen's pixmap onto the window. */ |
doEventLoop(); /* Handle any events. */ |
DisposeWindow( gWindow ); |
return 0; |
} |
void initMac() |
{ |
/*MaxApplZone(); |
InitGraf( &qd.thePort ); |
InitFonts(); |
InitWindows(); |
InitMenus(); |
TEInit(); |
InitDialogs( nil );*/ |
InitCursor(); |
FlushEvents( 0, everyEvent ); |
} |
void setUp() |
{ |
Handle menuBar; |
OSErr anErr = noErr; |
long aLong; |
long response; |
anErr = Gestalt(gestaltSystemVersion, &response); |
// Carbon Porting guidelines say provide alternate menu bar/menu scheme for OS X |
// This is just one way of doing this, which is pretty static |
if (response >= 0x01000) |
menuBar = GetNewMBar(MENU_BAR_IDX); |
else |
menuBar = GetNewMBar(MENU_BAR_ID); |
if ( menuBar == nil || anErr != noErr ) |
ExitToShell(); |
SetMenuBar(menuBar); |
DisposeHandle(menuBar); |
DrawMenuBar(); |
// Install 'quit' event handler |
if ((Gestalt(gestaltAppleEventsAttr, &aLong) == noErr)) { |
anErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, |
NewAEEventHandlerUPP(AEQuitHandler), 0, false); |
if (anErr != noErr) |
ExitToShell(); |
} |
} |
void createWindow() |
{ |
Rect wBounds; |
BitMap bitMap; |
Rect tempRect1; |
int top, left, width, height; |
GetQDGlobalsScreenBits(&bitMap); |
width = ((bitMap.bounds.right - bitMap.bounds.left) / 2); |
height = ((bitMap.bounds.bottom - bitMap.bounds.top) / 2); |
left = (((bitMap.bounds.right - bitMap.bounds.left) - width) / 2); |
top = (((bitMap.bounds.bottom - bitMap.bounds.top) - height) / 2); |
/* Create a window to display the final offscreen image. */ |
//SetRect( &wBounds, WLEFT, WTOP, WLEFT + WWIDTH, WTOP + WHEIGHT ); |
SetRect( &wBounds, left, top, left + width, top + height ); |
gWindow = NewCWindow( 0L, &wBounds, "\pSnapshot Test", false, documentProc, |
(WindowPtr)-1L, true, 0L ); |
//SetRect( &gBounds, 0, 0, gWindow->portRect.right - gWindow->portRect.left, |
// gWindow->portRect.bottom - gWindow->portRect.top ); |
GetPortBounds(GetWindowPort(gWindow), &tempRect1); |
SetRect( &gBounds, 0, 0, tempRect1.right - tempRect1.left, |
tempRect1.bottom - tempRect1.top); |
//SetPort( gWindow ); |
SetPortWindowPort( gWindow ); |
} |
void initPixmap() |
{ |
Ptr offBaseAddr; /* Pointer to the off-screen pixel image */ |
short bytesPerRow; |
GDHandle mainDevice; |
CTabHandle cTable; |
short depth; |
/* Get a handle to the main device. */ |
mainDevice = GetMainDevice(); |
/* Store its current pixel depth. */ |
depth = (**(**mainDevice).gdPMap).pixelSize; |
/* Make an identical copy of its pixmap's colortable. */ |
cTable = (**(**mainDevice).gdPMap).pmTable; |
(void) HandToHand( (Handle*)(&cTable) ); |
bytesPerRow = ((gBounds.right - gBounds.left) * depth) / 8; |
offBaseAddr = NewPtr((unsigned long)bytesPerRow * (gBounds.bottom - gBounds.top)); |
gPixMap.baseAddr = offBaseAddr; /* Point to image */ |
gPixMap.rowBytes = bytesPerRow | 0x8000; /* MSB set for PixMap */ |
gPixMap.bounds = gBounds; /* Use given bounds */ |
gPixMap.pmVersion = 0; /* No special stuff */ |
gPixMap.packType = 0; /* Default PICT pack */ |
gPixMap.packSize = 0; /* Always zero in mem */ |
gPixMap.hRes = 72; /* 72 DPI default res */ |
gPixMap.vRes = 72; /* 72 DPI default res */ |
gPixMap.pixelSize = depth; /* Set # bits/pixel */ |
//gPixMap.planeBytes = 0; /* Not used */ |
//gPixMap.pmReserved = 0; /* Not used */ |
gPixMap.pixelType = 0; /* Indicates indexed */ |
gPixMap.cmpCount = 1; /* Have 1 component */ |
gPixMap.cmpSize = depth; /* Component size=depth */ |
gPixMap.pmTable = cTable; /* Handle to CLUT */ |
} |
void createImage() |
{ |
GDHandle mainDevice; |
mainDevice = GetMainDevice(); |
/* Store the screen's pixmap image in the offscreen pixmap. */ |
CopyBits( (BitMap *)*(**mainDevice).gdPMap, (BitMap *)(&gPixMap), |
&(**(**mainDevice).gdPMap).bounds, &gPixMap.bounds, srcCopy, 0l ); |
} |
void drawImage(WindowPtr theWindow) |
{ |
Rect tempRect1; |
/* Copy the offscreen image back onto the window. */ |
//CopyBits( (BitMap *)&gPixMap, &gWindow->portBits, &gPixMap.bounds, |
// &gWindow->portRect, srcCopy, 0l); |
CopyBits( (BitMap *)&gPixMap, GetPortBitMapForCopyBits(GetWindowPort(theWindow)), &gPixMap.bounds, |
GetPortBounds(GetWindowPort(gWindow), &tempRect1), srcCopy, 0L); |
ShowWindow( gWindow ); |
} |
void saveToPICTFile() |
{ |
/* |
Saving a PixMap as a PICT file isn't too hard. |
1. Open a Picture with the port set to the destination of #2. |
2. CopyBits the PixMap onto itself or another port. (Because CopyBits is |
recorded in Pictures. |
3. Close the picture. |
4. Open the data fork for the file. |
5. Write out 512 bytes of zeros followed by the contents of the Picture |
handle. |
6. Close the file. |
*/ |
PicHandle picHandle; |
OSErr anErr = noErr; |
OSType fileTypeToSave = 'PICT'; |
OSType creatorType = 'ogle'; |
NavReplyRecord reply; |
NavDialogOptions dialogOptions; |
FSSpec documentFSSpec; |
long inOutCount; |
short refNum, count; |
AEKeyword theKeyword; |
DescType actualType; |
unsigned char header[512]; |
Size actualSize; |
Rect tempRect1; |
CopyBits(GetPortBitMapForCopyBits(GetWindowPort(FrontWindow())), (BitMap*) &gPixMap, |
GetPortBounds(GetWindowPort(gWindow), &tempRect1), &gPixMap.bounds, srcCopy, 0L); |
SetPortWindowPort(gWindow); |
picHandle = OpenPicture(&gPixMap.bounds); |
CopyBits((BitMap*) &gPixMap, GetPortBitMapForCopyBits(GetWindowPort(FrontWindow())), &gPixMap.bounds, |
GetPortBounds(GetWindowPort(gWindow), &tempRect1), srcCopy, 0L); |
ClosePicture(); |
for (count = 0; count < 512; count++) |
header[count] = 0x00; |
anErr = NavGetDefaultDialogOptions(&dialogOptions); |
dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation; |
anErr = NavPutFile( nil, |
&reply, |
&dialogOptions, |
nil, |
fileTypeToSave, |
creatorType, |
nil ); |
if (anErr == noErr && reply.validRecord) { |
anErr = AEGetNthPtr(&(reply.selection), 1, typeFSS, |
&theKeyword, &actualType, |
&documentFSSpec, sizeof(documentFSSpec), |
&actualSize ); |
if (anErr == noErr) { |
anErr = FSpCreate(&documentFSSpec, creatorType, fileTypeToSave, smSystemScript); |
if (anErr == dupFNErr) { |
anErr = FSpDelete(&documentFSSpec); |
anErr = FSpCreate(&documentFSSpec, creatorType, fileTypeToSave, smSystemScript); |
} // this is quick 'n' dirty or there'd be more robust handling here |
// write the file |
FSpOpenDF(&documentFSSpec, fsRdWrPerm, &refNum ); |
inOutCount = 512; |
anErr = FSWrite(refNum, &inOutCount, header); // write the header |
if (anErr == noErr) { |
inOutCount = GetHandleSize((Handle)picHandle); |
anErr = FSWrite(refNum,&inOutCount,*picHandle); |
} |
FSClose( refNum ); |
} |
reply.translationNeeded = false; |
anErr = NavCompleteSave(&reply, kNavTranslateInPlace); |
NavDisposeReply(&reply); |
} |
KillPicture(picHandle); |
} |
void handleMenuSelection(long result) |
{ |
int menuID, menuItem; |
RgnHandle rgnHandle = NewRgn(); |
menuID = HiWord(result); |
menuItem = LoWord(result); |
if (menuID == FILE_MENU) { |
if (menuItem == FILE_SAVE) |
saveToPICTFile(); |
else if (menuItem == FILE_QUIT) |
gDone = true; |
else if (menuItem == FILE_CLOSE) { |
DisposeWindow(FrontWindow()); |
if (FrontWindow() == NULL) |
gDone = true; |
} |
else if (menuItem == FILE_NEW) { |
} |
else if (menuItem == FILE_REFRESH) { |
createImage(); |
drawImage(FrontWindow()); |
QDFlushPortBuffer(GetWindowPort(FrontWindow()), GetPortVisibleRegion(GetWindowPort(FrontWindow()), rgnHandle)); |
} |
} |
HiliteMenu(0); |
DisposeRgn(rgnHandle); |
} |
void handleKeyPress(EventRecord *event) |
{ |
char key; |
key = event->message & charCodeMask; |
// just check to see if we want to quit... |
if ( event->modifiers & cmdKey ) /* Command key down? */ |
handleMenuSelection(MenuKey(key)); |
} |
// Apple Event - "Quit" |
pascal OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn) |
{ |
#pragma unused (messagein,refIn,reply) |
gDone = true; |
return noErr; |
} |
void doEventLoop() |
{ |
EventRecord anEvent; |
WindowPtr evtWind; |
short clickArea; |
Rect screenRect; |
while (!gDone) |
{ |
if (WaitNextEvent( everyEvent, &anEvent, 0, nil )) |
{ |
if (anEvent.what == mouseDown) |
{ |
clickArea = FindWindow( anEvent.where, &evtWind ); |
if (clickArea == inMenuBar) |
handleMenuSelection(MenuSelect(anEvent.where)); |
else if (clickArea == inDrag) |
{ |
//screenRect = (**GetGrayRgn ()).rgnBBox; |
GetRegionBounds(GetGrayRgn(), &screenRect); |
DragWindow( evtWind, anEvent.where, &screenRect ); |
} |
else if (clickArea == inContent) |
{ |
if (evtWind != FrontWindow()) |
SelectWindow( evtWind ); |
} |
else if (clickArea == inGoAway) |
if (TrackGoAway( evtWind, anEvent.where )) |
gDone = true; |
} |
else if (anEvent.what == updateEvt) |
{ |
evtWind = (WindowPtr)anEvent.message; |
//SetPort( evtWind ); |
SetPortWindowPort( evtWind ); |
BeginUpdate( evtWind ); |
drawImage( evtWind ); |
EndUpdate (evtWind); |
} |
else if (anEvent.what == autoKey || anEvent.what == keyDown) |
handleKeyPress(&anEvent); |
} |
} |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-10-10