mac-rom/Tidbits/PictWhap.a

1288 lines
65 KiB
Plaintext
Raw Permalink Normal View History

;
; File: PictWhap.a
;
; Contains: This is an FKEY that will save the screen as a PICT file, for use by all other programs
; as data files.
;
; Written by: Bo3b Johnson
;
; Copyright: <09> 1988-1991 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <5> 8/23/91 JSM Remove benign redefinition of FALSE, which is now defined by the
; build script.
; <3> 1/21/91 csd & PKE: Use SANE and the Script Manager to build the file name.
; This will let us localize the file name so that it works for all of the
; various script cases, i.e. right to left text in Hebrew.
; <2> 9/25/90 dba Check in Bo3b<33>s change to use D7 instead of D2. This caused a crash in
; 32-bit mode.
; <1> 1/17/90 fjs Recover from stupid mistakes
;
; PICTWhap.a;
;
; For best viewing, use Palatino 12.
;
; Copyright <20> 1988, 1989, 1990, 1991 Apple Computer, Inc.
; All rights reserved.
;
; This is an FKEY that will save the screen as a PICT file, for use by all other programs
; as data files. The PICT file is the Apple standard for transferring graphics between
; programs.
;
; Bo3b Johnson: 9/21/88
;
;
; Build using the PictWhap.make file, and the standard build menu.
;
;---------------------------------------------------------------
; *** bugs list:
; Bus Error during ClosePicture, DisposCCursor, if Heap Scramble is on.
; Unlikely to be in this code.
;---------------------------------------------------------------
;
; 9/26/88:
; Fixed the problem with overwriting the header of the PICT, now leaving space
; for the header before starting the spooling.
; Killed the SetGDevice/GetGDevice stuff as Darin noted it isn<73>t necessary.
; 9/27/88:
; Skip the buffer finding operation, the spooling is sufficiently fast to make it
; unnecessary, even on floppies. Timing results with Kong: about 5 seconds
; with floppy using bottlenecks, 4 seconds straight buffer. On Mac+ with old
; HD20, 2 seconds with BN, 1.5 seconds without. On Mac II HD, 1.5 second with
; BN, 0.5 without. There is thus little incentive to make the code more complicated.
; 9/28/88:
; Add the jShieldCursor hack to make the cursor appear in the saved image.
; As per a Darin suggestion to avoid having to go find the cursor data and CopyBits.
; 3/29/89:
; About to release it for D1 BigBang. Change the creator of the PICT files to
; 'View' to match the PAT/Viewer program. Added the asm/link lines in the
; docs below so it can be done the same each time. Set the place to create the
; files as the root directory of the startup volume, instead of default. Also
; change the other file system calls to find the file in the root. This makes it
; HFS only.
; 5/1/89:
; Change the use of globals in the FKEY code block to a StackFrame instead. The
; PictWriter globals are still stored in the code block however. Killed the use of
; MyNum2String in deference to the ROM based Pack for _NumToString.
; Changed the use of BootDrive into the sysVRefNum field from SysEnvirons, since
; we need to call SysEnvirons to determine color QD or not anyway.
; Change the creator to 'TTxt' for TeachText to be the owner instead.
; 5/7/89:
; Added the code to make it do all available screens, not just main screen. Uses the
; GrayRgn to get to all the devices in the world.
; Change the CrsrCouple low memory global to keep mouse from moving while we
; snapshot. Added code to save the registers we trash around both the FKEY proc
; itself, as well as the PictWriter routine.
; 5/10/89:
; Clearing the incredibly mindless ioFFlType for HFS since it is a swine and
; sets us up for the big fall. If this isn't cleared too, then Standard file will barf on
; the file, and not display it in the list. Notably there is also ioFileType which must
; be cleared too. Neither are used by the file system, or the Mac, but we still have
; to do this. Thank you HFS.
; 5/11/89:
; Finalizing the code. Changed the init code to clear all the variables in a big loop,
; added a few comment lines in PB blocks to show all parameters expected for each
; call. Verified each call to be sure each field is cleared in advance, or explicitly set.
; Removed the code that would handle a missing STR resource, check for error instead.
; 5/19/89:
; Calling it 1.0B2 now, since it is essentially complete. Change creator one more time,
; to 'ttxt' to have TeachText open the files. (lower case is it?, Hah! tricked myself.)
; Added change to have it use A5 as the pointer to QD globals, as per a Darin<69> concept.
; This also makes it possible to remove the global in the code.
; 10/26/89:
; Version 1.0B3, and changed to DC.L for the 'FKEY' string. Also made big changes
; to make it do banding in low RAM/Big Ass screen situations. The banding is done
; in a simple fashion to make it easy to write/debug, and relies on QD returning from
; the CopyBits with no change in the picture size, or setting QDError.
; 10/30/89:
; Numerous minor fixes that were suggested in code reviews. Things like MOVEQ
; instead of CLR and so on. Up the version number to 1.0B4 since I half released B3.
; 11/13/89:
; More fixes related to code review. Set up string to be 'Picture ^0' so we can Munger
; in the file number, killed MyConCat. Changed the cursor protection stuff to set
; CrsrVis to false, and decrement the cursor state. This freezes the mouse, as well
; as keeping it on the screen. No more ShieldCursor hack, nor CrsrCouple.
; 11/14/89:
; Set up a hard coded string to handle the case where the STR resource may get lost.
; The 'Picture ^0' is thus in the code, turned into a StringHandle in case we can't
; get the STR. This is unlikely, but may happen, and we can handle it easily.
; 8/20/90:
; Added the code to SndPlay a camera noise when we start up. This gives immediate
; feedback that it is doing something, then goes on to do the saving. If we get an error,
; you get a beep as well as the camera click. Also fixed a bug where it would bail
; out to DeathCity in the middle of the device loop, causing the mouse to remain
; frozen in place. Moved the mousefreeze and state save to early on, and restore
; the state in CloseNClean.
; 9/3/90:
; Fixed a bug introduced by system build, where the file name as a string was bigger
; than the length byte, so GetHandleSize was adding that extra pad byte to names.
; Now it uses the length bytes and does all the math to get string length. Change
; build file so that the resources don't have any names, as desired by build guys.
; 9/25/90:
; Fixed stupid bug where SndPlay would trash register D2 in 32-bit mode. I was being
; dumb using a scary register. I moved it to use D7 instead, and save and restore D7.
; 1/21/91:
; Changed the way that the picture file name is generated by making the number
; calculation and appending code use the Script Manager. This required: 1) Using
; SANE to increment an extended number, 2) using the script manager routine
; FormatX2Str to convert it to a string, and 3) using the script manager routine
; ReplaceText instead of Munger to merge the base name and the calculated number.
;
; Strategy:
; The FKEY 3 is intended to take a snapshot of the screen when invoked, saving the image
; into a file for editing/printing by an appropriate graphic editor.
; Codewise the basic idea behind the FKEY is that when invoked it will do a CopyBits call using
; the screen as the source and the destination, while an OpenPicture is under way. The screen
; is obviously the source, but it is also the destination to avoid having to create some huge old
; buffer to copy the bits into. This approach will still save the bits as long as a Picture is
; Open. The bits get saved, and we can write the bits out as a file with the right header in it to
; make it a PICT file. The sequence it will use is:
; 1) Create and open the file to use. This will use a string out of the system file, to allow
; for localization of the generated files. The numbers will be appended to the name, starting
; at 1, and going up.
; 2) We will use the bottlenecks in QuickDraw to spool the data out line by line. This is
; sort of slow but takes no RAM.
; 3) We create a new port that we can play with, using OpenPort. This port will be changed
; in size, as well as installing bottlenecks in the PutPicProc.
; 4) After the bottlenecks are set up we can do the OpenPicture to create a new picture.
; 5) Do the CopyBits (main, main, ...) to write the bits through our proc with the bottlenecks.
; 6) As CopyBits runs, it will call our proc to have the bits written to the disk as it goes along.
; 7) Do each monitor in turn, if we are on colorQD machines.
; 8) Close the file.
; 9) Close the port we created.
; 10) Split.
;
; Some overall observations:
; There appeared to be a dependancy on A0 or A1, causing crashes, so just to be extra safe
; we will save all registers we use, even though we shouldn't have to save A0-A1/D0-D2.
; For a long, disk writing routine like this, it won't hurt to be extra robust. All registers
; are thus preserved around both the FKEY itself, and the PictWriter routine, since it is
; not clear what can be trashed as part of a bottleneck routine. The philosophy here is to
; be extra safe, to save development time.
;
; I looked into setting up the parameter block we use in A2, then using that throughout
; the FKEY, but it didn't appear to be worth it. It would save only two lines of code, at the
; expense of making it harder to figure out, and more prone to bugs when modified. The
; current approach is to load A0 with the address of the block each time it is required.
;
; In keeping with the goal of trying to keep it small, the spurious lines of initialization
; for parameter blocks have been commented out. They are left there as a notice that
; those lines are required for an unknown parameter block, but that we know they were
; cleared or set immediately before. Thus, it should be clear what is required for the
; call, without taking up code space to actually do it. This is in contrast to my normal
; approach of explicity setting each field used, to minimize copy/paste errors.
;
; We don't preflight for space on the disk to write the file. We have to handle any write
; errors later in the code anyway, and DiskFull is just another form of a write error,
; so there seems to be little point in taking the extra code to preflight. Since the files are
; of non-obvious lengths (they are packed with PackBits), we couldn't do a good job
; preflighting, even if we wanted to.
;
; Error checking and handling on all possible calls. The DeathCity error handler
; can be called at anytime, and it will do the right thing in terms of closing the port,
; killing the picture, closing the file and so on, as long as the variables are cleared
; before the first possible error.
;
; If I get an error during creating, naming or preflighting the file I can<61>t finish
; the job, so I need to clean up and split. A SysBeep is wimpy user feedback on
; errors, but there isn't a whole lot better. It doesn't really seem appropriate to
; put up a dialog from an FKEY either. The only Expected error is DiskFull.
;
; Any errors during the Write or during the bottleneck Writes will be handled
; similarly. This makes it possible to error out in the bottleneck, and avoid getting
; recurring errors.
;
; The Lone Ranger in the form of Darin showed up to get around the problem of
; needing to access our globals from the PictWriter bottleneck routine. The solution
; is to set A5 to point into our global space like usual, and to set the contents
; of that first location to point to the current QD globals. This way we use the
; A5 world of whoever we are invoked within, and thus the PictWriter can get
; to our globals via A5 (the only register we can rely on there, other than PC).
; This is preferable to storing a pointer in the code block and doing a PC relative
; move during the bottleneck routine. The setup for the A5 is a little funky but
; it should be possible to understand it without too much work. Of course, if I
; had pictures I'd show you stack frames and A5's pointing around and so on,
; but until then, we'll just have to make do with words. I can't bring myself to
; use NoRes graphics after all this time.
;
; HFS is a pig. It is poorly documented and the interface is clunky and hard to use.
; When doing all HFS calls, I would expect things to be more lucid. There are two
; file version number parameters, one for Create, and one for SetFileInfo it would
; appear. These are not documented, with the exception of two technotes, one talking
; about one flag, the other talking about the second. HGetFileInfo will return me a
; bogus number in ioFFlType, thank you very much. This number if then applied via
; SetFileInfo will then make the file not show up in StdFile. Another particularly fine
; approach. If these numbers are used by anybody, then why does StdFile filter them?
; Should StdFile be fixed to not care? What this also points out is that the documentation
; is not sufficient to use HFS. A better approach will always be to clear the full
; parameter block in a loop, and not try to be accurate about what needs to be set and
; what doesn't. In fact, I'm going to change this code to do that instead.
;
; Given the poor shape of the HFS interface and documentation, we will now adopt the
; philosophy of clearing the parameter block before we start, and not explicitly set
; any of the fields in the block, knowing they are zeroed. This will buy us a little
; code savings, and should be a more robust approach. HFS seems to be pretty consistent
; about expecting zero for unused, or unknown, fields. This is less than optimal, since
; it would be better to know what exactly is necessary, and set those specific fields.
; But, since we are in doubt, it is better to play it safe and overkill. It is more
; important to be robust than accurate. Thus all our globals on the stack will be
; cleared in a single loop, where we will walk through the entire stack frame. This will
; set all our variables to zero, as well as the parameter block. (Notably, this is what
; Andy Hertzfeld did in RMaker... are you scared yet?) In keeping with HFS paranoia,
; we will set all fields in the parameter block, whether it is an HFS version of the call
; or not. The documentation is not clear if this is necessary, but The Debugger indicates
; it is, so better safe than spending hours tracking down something bogus.
;
; We are going to save and restore A5 around the FKEY, and will modify it from inside
; the FKEY. This will allow us to have no globals stored in the code, and PictWriter as
; the bottleneck will still be able to get to our globals. This way we avoid the self-modifying
; code in order to store PC relative variables. The QD globals still need to be accessed
; by QD however, so we still have to provide that path. The way to do that here,
; is to copy the 0(A5) pointer that QD uses to hunt down the globals, and store it in
; our variables block, keeping the same link.
;
; Another change here that seems to be dictated by 32-bit QD, and old systems. We want
; to band the image in the cases where there is not enough RAM. Under 32-bit QD, a
; big buffer might need to be made to copy the bits into before saving them in the picture.
; For a two-page screen this may be megs of data, causing us to create an empty picture,
; which is lame. Instead, we will look for the failing condition, and chop the image
; up into small pieces (bands). The technique we will use here will just keep shrinking
; the rect that we use as the source and destination. If it gets too small we'll bail out and
; signal an error. Otherwise we will keep a copyHeight that will be used to size each band
; vertically. Each time we fail during a CopyBits, we will divide the copyHeight by two,
; until it either succeeds or we get too small (like a single horizontal row of pixels).
; Part of the trouble is that a color table gets saved for each band, but we don't
; really expect to have to do that many bands. Also, it seems better to make a bigger
; banded picture (on disk), than to fail from too little RAM. In system 6.0 there is a patch
; that handles the source and destination both being the screen, and if so it will not
; create a buffer. It appears that we cannot necessarily rely on that, due to the problem
; of needing to access 32 bit screens. 32 bit QD handles the drawing, but the OpenPicture
; can't do it directly, and needs a buffer. It may be fixed in the future, and this approach
; will cleanly default to a no buffer/single copybits situation. This banding will be done
; on a per device basis. The only slightly skanky aspect of doing it this way means that
; we need to look at the picture size after the CopyBits call, and if it didn't change size,
; we know it failed. We'll also check QDError to be safer, but it is a implementation
; detail of the picture mechanism. Another possible approach would be to do the
; buffer allocation ourselves, knowing about the various depths and so on, which seems
; excessively complicated, and requires even more knowledge of the QD model, like
; pixelSize. The algorithmic pseudo-code is like:
;
; copyHeight = screenRect.b-screenRect.t
; copyRect = screenRect
; copyTop = screenRect.t
; Copy:
; copyRect.t = copyTop
; copyRect.b = copyTop + copyHeight
; If copyRect.b > screenRect.b Then copyRect.b = screenRect.b
; gWroteBits = false
; CopyBits (screen, screen, copyRect, copyRect, scrCopy, nil)
; If NOT gWroteBits or QDError Then
; copyHeight = copyHeight / 2
; If copyHeight < 2 Then Failure
; Goto Copy
; copyTop = copyTop + copyHeight
; If copyTop < screenRect.b Then Goto Copy
;
; Also remember that the banding loop is surrounded by the device loop for color QD,
; so they are nested.
;
; A little info about the sound. I looked into doing a 3:1 compression on the sound, to save
; disk space, but it doesn't seem to be worth it. The sound is already sampled at 11K,
; which seems the best compromise. At 3:1, that is 3:1 off of the 22K sample, so it is
; still 2/3 the size of the 11K sound. It is not a big savings, and the sound is worsened.
; In addition, the compression jazz doesn't work in 6x systems, in case this gets copied
; over there. At 6:1 compression, it is totally gross. The compression seems really lame,
; and not an advantage over just going down in sample speed. Trying some more
; tests, the 3:1 compression actually does sound better than 11K sample, so it would
; be preferred, but it isn't a huge win, so the lack of 6x compatibility wins out.
;
;
; Some changes suggested via Code Review:
; Another minor change, the string that defines the file name is now 'Picture ^0' to start
; with, and can be changed at will for internationalization reasons. The ^0 is replaced
; at run time with the current file number string (from Num2String). This is easy to
; do using Munger and took about the same amount of code as my old ConCat routine,
; but is better for localizing.
;
; Rather than setting the CrsrVis flag specifically when we are done, I will save
; and restore the old value, in case it is more sophisticated in the future.
;
; I changed the code to decrement the CrsrState, set the CrsrVis to false,
; then do the CopyBits. This freezes the mouse, keeps it on the screen, and
; allows it to be restarted after the CopyBits, so we capture the bits of the cursor in the
; picture as well. Previously I was doing a fake jShieldCursor, which was too sick, and
; this approach seems to have the same effect, whilst merely futzing with low memory
; globals. Unfortunately, I have slightly more confidence that the vector will behave
; the same way in the future, but am unconvinced that the low memory globals even
; work as they are described. Just how screwed up is the cursor code anyway?
; This appears to work fine, but if there is any compatibility risk in this code, that's it.
;
;
; Yes, I realize some of the comments get a little redundant, but you know, I'd rather say
; it twice than to not say it once...
;
;
; Open Questions:
; 1) Should we do the menu down snapshots? This is pretty tough to do, will make
; it bigger, and risk more compatibility. Is it worth the effort? The proposed approach
; is to create our own heap block when invoked, turn it into a heap only we use, set
; up our own A5 world and QD world in this heap, start up a Time Manager task to
; call us back later, wait 3 seconds or so, when called by TM, change to our heap which
; will be valid at interrupt time, swap out QD globals that need it (unknown list, to date),
; do the CopyBits with bottleneck spooling, close file, set up a Deferred Task Manager
; task to call us back, at DTM time (which is GNE time) dispose the heap block we
; created. So... Technically challenging, but feasible. Again, is it worth it?
; -- answer: not. Do it in the system.
; 2) Is it too sick to use GrayRgn? To dangerous? Any better way to do it?
; 3) This only runs on the latest system. It will only be shipped with that system,
; but if anyone copies and pastes it around, it won't work. System 6.0.3 is required
; in order to have the multiple monitor part work. The CopyBits bug is fixed in
; that version. Is it bad to require the newest (I prefer to make it live with anything).
; Requires HFS too.
; -- answer: no biggy. works now on system 4.2 with banding, HFS is OK.
; 4) This saving of the picture will create a PICT 2 type picture, even if the current
; color depth is only 1 bit deep. Is this bad? Will it make pictures that are hard for
; Mac+ to read? If I remember correctly, the Mac+ system patches allow it to read
; PICT 2, and extract the picts if they are B&W...
; 5) If the string for the file name isn't available in the system we will bail out with
; an error. Is this reasonable? Should we try to be more robust here with a default
; string?
; -- answer: added the change to make it default to 'Pic ' as the name.
PRINT PUSH,OFF ; don't print any of the headers.
INCLUDE 'Traps.a'
INCLUDE 'PackMacs.a'
INCLUDE 'ToolEqu.a'
INCLUDE 'QuickEqu.a'
INCLUDE 'SaneMacs.a'
INCLUDE 'ScriptEqu.a'
INCLUDE 'SysEqu.a'
INCLUDE 'SysErr.a'
PRINT POP ; restore the PRINT options
; Constants that are defined here instead of putting them in the code.
headerSize EQU 512 ; 512 bytes in PICT files for header info.
fileNameId EQU -16385 ; resource ID number for 'Picture' string.
pFileType EQU 'PICT' ; file type we create.
pFileCreator EQU 'ttxt' ; file creator for teachtext.
pSoundType EQU 'snd ' ; resource type for sound resources.
cameraId EQU -16504 ; resource Id of camera noise.
PICTWhap PROC
; The StackFrame we are using here is just for local variables for the FKEY. They
; would be globals, but FKEY's can't have globals, and this puts them on the stack,
; instead of in the code. The QDglobalPtr is to be the 0th location in the list, so that
; 0(A5) will be that pointer. This is the way QD expects it. Thus baseName will be
; -4(A5), baseNum is -8(A5) and so on.
FkeyGlobals RECORD {QDglobalPtr},DECR ; build a stack frame record
;------------------------------------------------------------------
;------------------------------------------------------------------
; Here I define all the variables that are used. These are
; used in a global variable fashion, even though they are allocated on the stack.
; FKEYs can<61>t have global vars though, so this is a reasonable way out.
; RA5 will still be used to point to our globals, so we set up the first element of
; our globals to be the pointer to the QD variables as QD expects. This is relatively
; complicated, so peruse the Segment loader and Memory manager chapters to see
; how all the variables are to be set up, or take my word for it.
; Our globals will still be referenced as some negative number off of A5.
; A6 is not used as such, except to give us a handy frame easily.
; It is possible to use a straight Link A5, but the sequence of code is thus critical,
; since we need to pull out the old 0(A5) before we change A5. Rather than make it
; extra tricky, hard to read, hard to modify, and hard to maintain, in order to save 2
; lines of code; I left it as LINK A6.
QDglobalPtr DS.L 1 ; pointer to QD globals, so QD can still work.
baseName DS.L 1 ; This will become a StringHandle to the name.
copyBaseName DS.L 1 ; copy of the handle, will be modified.
baseNum DS.B 10 ; an 80-bit extended, SANE compatible
baseNumString DS.B 32 ; smaller than Str255, to save unused space.
hBaseNumString DS.L 1 ; handle to copy of the number string.
fileName DS.B 32
filePB DS.B ioFQElSize ; big enough for the SetFileInfo too.
newGrafs DS.B cqdProcsRec
picPort DS.B portRec
portIsValid DS.W 1
oldCrsrVis DS.W 1 ; saved value from CrsrVis.
currPort DS.L 1
sysInfo DS SysEnvRec ; for colorQD test, and boot volume refnum.
maxRect DS.W 4 ; big rect containing all monitors.
nextGD DS.L 1 ; handle for current GDevice when doing Mac II
; The next few variables are for the banding operation. These are used on a per device basis,
; and only really change in really low RAM situations, or Big Ass monitors. All these variables
; are defined on a per band basis. One band can be the entire device though.
copyHeight DS.W 1 ; the height used for each CopyBits band.
copyRect DS.W 4 ; the rectangle used for each band.
copyTop DS.W 1 ; the starting location of a given band.
; These next few are used by the PictWriter routine directly.
gPictRefNum DS.W 1
gPictSize DS.W 1
gPictError DS.W 1
gPictHandle DS.L 1
gWroteBits DS.W 1 ; for memory full checking.
; Here we have some variables which are used while doing file name construction in an
; international-friendly manner.
itlHandle DS.L 1 ; handle to itl resource with the NumberPartsTable
numPartsOffset DS.L 1 ; offset into the itl of the NumberPartsTable
itlState DS.W 1 ; so we can save/restore the state of the handle
tableLength DS.L 1 ; length of the table (not used)
; The end of the Stack Frame as our globals. This LocalSize will thus be subtracted from the
; stack to store the variables.
ALIGN 4 ; make sure we stop at a nice 4 byte boundary.
; This is so we can clear the whole block easily.
GlobalSize EQU *-FkeyGlobals ; size of all the local variables
ENDR
WITH FkeyGlobals ; use our 'global' stack frame
;------------------------------------------------------------------
;------------------------------------------------------------------
; The start of the FKEY, with the conventional FKEY header on it to identify it and the
; current version of this little wag.
Start
BRA.S @0 ; skip over the text, obviously.
STRING ASIS ; turn off length byte.
DC.L 'FKEY' ; a hunk of data in the code.
DC.W $0003 ; FKey 3 for the hex viewer.
DC.W '1.0B6' ; version number.
; Set up some of our variables to initial values. And save off the current gDevice
; and port. In case of errors, we can restore the environment to what it was.
; Call SysEnvirons to determine if color QD is here or not, since that determines
; the use of gDevices or not.
@0
LINK A6,#GlobalSize ; allocate our local stack frame 'globals'.
MOVEM.L A0-A2/A5/D0-D2/D7,-(SP) ; save registers we trash. (9/25/90)
; Since we don't trust HFS, or anybody else, including ourselves, we will clear all of the
; global data space to zero. This starts us in a nice known condition, a welcome change of
; pace. Do all the variables in a big loop, 4 bytes at a time. The number stowed in D0 is
; a positive DBRA count of the number of longwords in our global area. GlobalSize is a
; negative number, so we invert it before dividing by 4, then subtracting 1 to make it DBRA.
MOVEQ #(-GlobalSize/4)-1,D0 ; total size of our space, in longwords, DBRA count.
MOVE.L A6,A0 ; the start of the global space.
@1
CLR.L -(A0) ; clear the next 4 bytes. A6 offsets are negative.
DBRA D0,@1 ; until we got them all.
; Now set up the globals pointer in A5. This will point to our block on the stack, but we will
; use A5 to get there. We also save off the QD link, so we can use some one else's QD world for
; our picture creation.
MOVE.L (A5),A0 ; save off the link.
MOVE.L A6,A5 ; swap globals over to our space,
SUBQ #4,A5 ; make it so 0(A5) is QDGlobalPtr.
MOVE.L A0,QDGlobalPtr(A5) ; set the link back so QD is all set.
; 8/17/90: B3B:
; Before we start the show, make the noise of a camera so that they know
; that something happened. If we get an error somewhere inside, we'll get a beep as well,
; but it doesn't come out too bad. We handle the case of the sound being missing, by
; just skipping the attemp. It won't cause a problem. We lock the sound handle just to
; be extra safe. We shouldn't have to, but the sound manager has a sordid history, and
; it is preferable to be safe, rather than explicitly correct. We want to
; do it async, but can't really do that since we might error out somewhere in the middle,
; giving a weird noise effect. Also, we can't leave until the sound is done, and don't
; want to screw around trying to find out if it is done or not.
CLR.L -(SP) ; room for handle result.
MOVE.L #pSoundType,-(SP) ; the type of resource, sound.
MOVE.W #cameraID,-(SP) ; the resource ID of the camera noise.
_GetResource ; get the sound as a resource.
MOVE.L (SP)+,D7 ; retrieve the handle to the sound. (9/25/90)
BEQ.S GetSysInfo ; if it is gone, skip making a sound.
MOVE.L D7,A0 ; the handle to the sound. (9/25/90)
_HLock ; lock it down while we play.
CLR.W -(SP) ; room for function result.
CLR.L -(SP) ; no sound channel from us,
MOVE.L D7,-(SP) ; the handle to the noise, locked. (9/25/90)
MOVE.W #False,-(SP) ; and do it is synchronously.
_SndPlay ; play that camera noise.
MOVE.W (SP)+,D0 ; get the result code, but ignore it.
MOVE.L D7,A0 ; retrieve the handle to the sound, (9/25/90)
_HUnLock ; and unlock it, so it can be purged.
; GetSysInfo:
; We need to determine if colorQD exists on the machine or not, so call SysEnvirons
; to find out. This also gets the boot volume vRefNum, which is more robust than the
; BootDrive low memory global. We don<6F>t link with the glue here since we know that
; the trap has to exist on machines where this FKEY will run. We can always expect
; Version 1 to exist, and thus don't check the error code on return.
GetSysInfo
LEA sysInfo(A5),A0 ; address of the record.
MOVEQ #1,D0 ; version 1 record, and info desired.
_SysEnvirons ; fill the environment record on stack.
; Save the current port so we can restore it at the end.
PEA currPort(A5) ; the var to set,
_GetPort ; save off the current port
; Now we need to do a sick thing. This is to make the cursor visible in the pict, once it
; is saved off. The problem is that CopyBits will ShieldCursor in order to make it not
; appear in an offscreen picture. We need it there in order to make it a real screen
; snapshot. We will mark the cursor as Invisible so that QD will think that it doesn't
; have to ShieldCursor. We do this by setting it invisible, and decrementing the CrsrState.
; We also want to freeze the mouse while this goes on, since we don't want the mouse to
; change around during the save. It takes long enough that we can get multiple smashed
; copies of the cursor. The mouse is frozen by the CrsrState change. This has to be done
; before the first possible DeathCity, so that we can be sure to restore the state properly.
MOVE.B CrsrVis,oldCrsrVis(A5) ; save the old value.
SF CrsrVis ; Pretend cursor is invisible.
SUB.B #1,CrsrState ; Turn cursor off.
; Now try to get the STR resource for the name of the file we want to use.
CLR.W -(SP) ; returned refnum, will be left here.
_CurResFile ; where are we currently? (save this place.)
CLR.W -(SP) ; switch to the system file.
_UseResFile ; use the system file, to avoid getting wrong string.
CLR.L -(SP) ; the handle result.
MOVE.W #fileNameId,-(SP) ; the rez id for it.
_GetString ; get it if we can.
MOVE.L (SP)+,baseName(A5) ; save handle to the string.
; the refnum is still on the stack.
_UseResFile ; return resource chain to how it was.
; Get the international table for number parts so we can use it when building
; the file name.
MOVE.W #iuSystemScript, -(SP) ; desktop items are in the System script
MOVE.W #iuNumberPartsTable, -(SP) ; we need the number parts table
PEA itlHandle(A5) ; place to store the itl4 handle
PEA numPartsOffset(A5) ; offset into the itl4 of the NumberPartsTable
PEA tableLength(A5) ; somewhere to put this, not used by us
_IUGetItlTable ; try to get the number parts table
MOVE.L itlHandle(A5), D0 ; did we get the table?
BEQ DeathCity ; if not, probably low on memory
MOVE.L D0, A0 ; the itl4 resource handle
_HGetState ; remember its present state
MOVE.B D0, itlState(A5) ; save it for a later restore
_HLock ; don<6F>t let it go anywhere (or get purged)
; We have the handle to the STR resource. Make sure it is valid.
TST.L baseName(A5) ; is it non-nil?
BNE.S TryFileName ; if it is valid, go ahead and use it.
; For the case where we couldn't find the string, a silly case arguably, but one we want to
; allow them the option of making, let's hard code a file name that will be used instead
; of the string. Turn this hardcoded string into a handle, and store the handle.
CLR.L -(SP) ; returned handle result.
PEA hardName(PC) ; address of string in code,
_NewString ; turn it into a handle,
MOVE.L (SP)+,baseName(A5) ; save off this handle instead.
BEQ DeathCity ; if it is nil, bail out, no ram.
; If there are files there already, I want to keep upping the number to
; find an unused file name. There is not much point in stopping at 9,
; so I<>ll just keep trying until it fails, or I get an OK file name.
; We have a valid handle to the string, which starts as 'Picture ^0'. Make a copy of it so we
; can change the handle with ReplaceText. We do this each time through the TryFileName
; loop so we can start with a fresh 'Picture ^0' each time. We copy the handle instead
; of using the resource, to avoid having to hit the disk each time we try a new name.
TryFileName
MOVEQ #0, D0 ; start with a clean register
MOVE.L baseName(A5),A0 ; the handle to current string,
MOVE.L (A0), A1 ; point to the current string
MOVE.B (A1), D0 ; get the length of the string
_NewHandle ; try to make a handle of the right size
BNE DeathCity ; get out if we run out of memory
MOVE.L A0, copyBaseName(A5) ; save this handle so we can nuke it later
MOVE.L (A0), A0 ; point to the handle data
MOVEQ #0, D0 ; start with a clean register
MOVE.B (A1)+, D0 ; get the length to copy & point to first charater
EXG A0, A1 ; make source & dest registers correct
_BlockMove
; Increment our number by one. We use SANE because we don<6F>t want this code to
; know about the extended type implementation. Also, since we<77>re going to use the
; international utilities to convert this to a string, and they call SANE, there<72>s no
; extra hit because we call SANE here. Finally, SANE in ROM is likely to be used,
; saving us from loading it in any case.
PEA extendedOne ; the constant value 1
PEA baseNum(A5) ; our local counter
FADDX ; baseNum := baseNum + 1
; Now convert the number to a string so we can hook it into the picture string.
; We use the script manager to be completely international-friendly. This will almost
; certainly cause a RAM hit, unfortunately.
PEA baseNum(A5) ; the number to convert
PEA numFormat ; how to format the number
MOVE.L itlHandle(A5), A0 ; handle to itl4 resource
MOVE.L (A0), A0 ; pointer to the (locked) itl4 resource
ADDA.L numPartsOffset(A5), A0 ; adjust pointer to point at table
MOVE.L A0, -(SP) ; push the NumberPartsTablePtr
PEA baseNumString(A5) ; where to put the formatted string
_FormatX2Str ; script manager-friendly NumToString
; Now combine the two strings, the name, plus the number as a string to build a
; file name to try. We will build the string using ReplaceText, to replace the ^0 in the
; string with the current baseNumString. Since ReplaceText requires two handles, we have
; to first copy the number string into a handle.
MOVEQ #0, D0 ; start with a clean register
MOVE.B baseNumString(A5), D0 ; get the length byte
_NewHandle ; try to make a handle to hold it
BNE DeathCity ; if we get an error, just leave
MOVE.L A0, hBaseNumString(A5) ; save the handle for later
MOVE.L (A0), A1 ; point to the new handle data
MOVEQ #0, D0 ; start with a clean register
LEA baseNumString(A5), A0 ; point at our copy of the string
MOVE.B (A0)+, D0 ; get the length & point at the first character
_BlockMove ; copy the string into the handle
CLR.W -(SP) ; return result from ReplaceText
MOVE.L copyBaseName(A5), -(SP) ; the handle to the copy of the file name
MOVE.L hBaseNumString(A5), -(SP) ; the handle to the number string
PEA upSnick0Str(PC) ; the key to replace
_ReplaceText ; substitute the number for ^0
; function result removed below
; Throw away the handle which contained a copy of the number string
MOVE.L hBaseNumString(A5), A0
_DisposHandle ; kill it
; Set the length byte for this new string, using the length of the handle resulting from
; the ReplaceText call. Then copy the string data from the handle into our local file
; name string.
MOVE.L copyBaseName(A5), A0 ; handle to complete file name
_GetHandleSize ; how many bytes?
MOVE.L (A0), A0 ; point at data
LEA fileName(A5), A1 ; point at local file name string
MOVE.B D0, (A1)+ ; move the length byte & point past it
_BlockMove ; copy the rest of the file name
; Now we have a copy of the next name to try, kill the string handle, since we have to start
; with the resource in order to get the ^0 in the string.
MOVE.L copyBaseName(A5),A0 ; the handle to string,
_DisposHandle ; kill it.
; Now we have cleaned up the handle, see if ReplaceText worked or not. If it didn't, it
; couldn't find the ^0 in the string which would imply something very bad happened.
; We do this sort of late here, so that we can blow away the StringHandle without
; having to worry about it in the error handler.
MOVE.W (SP)+,D0 ; get result from ReplaceText,
BMI DeathCity ; if couldn't find it, something is wrong.
; Now try to create the file based on that file name, on the current volume.
; The parameter block is set up for both Create and Open.
; Create the file in the root of the startup volume. The rest of the parameter block
; is zeroed, from above, just in case.
LEA filePB(A5),A0 ; the file parameter block,
LEA fileName(A5),A1 ; the address of the file name string,
MOVE.L A1,ioFileName(A0) ; set up parameter,
MOVE.W sysInfo.sysVRefNum(A5),ioVRefNum(A0) ; use startup volume.
; CLR.B ioFVersNum(A0) ; clear file type, of course.
MOVE.L #fsRtDirId,ioDirID(A0) ; create in the root directory.
_HCreate ; try to create that file.
; If we have an error other than dupFNErr, then we are toast and have to exit.
; If it is a dupFNErr, then we need to up the file name/number by one and
; try again.
CMP.W #dupFNErr,D0 ; is it a duplicate file name?
BEQ TryFileName ; if so, try next one.
TST.W D0 ; was result OK?
BNE DeathCity ; If not, we must die.
; The file is valid, set the file information so that it is marked as a PICT file. Start
; with a GetFileInfo to set up all the junk we don't need to fool with. Recall that we
; comment out the lines that aren't specifically required in this location in the code,
; but leave the lines there to remind you that they are required for the call. They
; are already set up by the above HCreate call, or by the clearing process at the start
; of the code.
; LEA filePB(A5),A0 ; the file parameter block,
; LEA fileName(A5),A1 ; the address of the file name string,
; MOVE.L A1,ioFileName(A0) ; set up parameter,
; MOVE.W sysInfo.sysVRefNum(A5),ioVRefNum(A0) ; use startup volume,
; CLR.W ioFileType(A0) ; no version number, any permission.
; CLR.W ioFDirIndex(A0) ; use name and vRefNum.
; MOVE.L #fsRtDirId,ioDirID(A0) ; get in the root directory.
_HGetFileInfo ; get the info on the slime.
BNE DeathCity ; everything OK?
; Now change the file type and creator and Set the info.
; LEA filePB(A5),A0 ; the file parameter block,
; LEA fileName(A5),A1 ; the address of the file name string,
; MOVE.L A1,ioFileName(A0) ; set up parameter,
; MOVE.W sysInfo.sysVRefNum(A5),ioVRefNum(A0) ; use startup volume,
; CLR.W ioFileType(A0) ; no version number, any permission.
; CLR.B ioFFlType(A0) ; and the other file type Must be cleared too.
MOVE.L #pFileType,ioFLUsrWds+fdType(A0) ; change file type to PICT.
MOVE.L #pFileCreator,ioFLUsrWds+fdCreator(A0) ; and the file creator to 'ttxt' for TeachText
; Creation and ModDates are set in block,
; from the GetFileInfo.
MOVE.L #fsRtDirId,ioDirID(A0) ; set in the root directory. (HGetFileInfo pounds
; the dirId on the way back.)
_HSetFileInfo ; Set the info on the slime.
BNE DeathCity ; everything OK?
; We have a valid file name, and the file has been created on the default volume.
; So... it is time to open the slime dog. We use the 'write' permission, but there are
; a couple of other stupid things in the file system, like not reporting an error here if
; the disk is write protected. We get that later in the Write call, so we'll handle a
; no write access error there too. If everything is OK, we'll just get Rd/Wr permission
; back by default. This is fsWrPerm though, just to be explicit.
; LEA filePB(A5),A0 ; the file parameter block,
; LEA fileName(A5),A1 ; the address of the file name string,
; MOVE.L A1,ioFileName(A0) ; set up parameter,
; MOVE.W sysInfo.sysVRefNum(A5),ioVRefNum(A0) ; use startup volume,
MOVE.B #fsWrPerm,ioPermssn(A0) ; desire Write permission to file.
; CLR.B ioFileType(A0) ; clear the, worse than useless, version #.
; CLR.L ioOwnBuf(A0) ; use standard buffer.
; MOVE.L #fsRtDirId,ioDirID(A0) ; open in the root directory.
_HOpen ; open the file for writing.
BNE DeathCity ; if it cannot be opened, we cannot continue.
MOVE.W ioRefNum(A0),gPictRefNum(A5) ; saved off for future use.
; Now write the header information into the file, so that it is a PICT file. It is actually
; 512+10 bytes long, since we throw in the picture header as well. The space needs to
; be allocated in the file, although it will be updated after the picture is done.
; This is kind of a cheater approach to writing an empty header, but serves two purposes.
; It will get clear block we can write at one shot to clear the header on the file, and it also
; has to get 522 bytes of storage or we die here. Since QD will crash on no memory for
; the upcoming OpenPort, this is a minor preflight to be sure we won't die when we try
; to open the port, or open the picture. OK, so I'm a slacker, but I really just wanted to
; use the Clear bit.
MOVE.L #headerSize+picData,D0 ; size of the block.
_NewHandle CLEAR ; make a block of zeros.
BNE DeathCity ; are we so low we can't get 512 bytes?
MOVE.L A0,A2 ; save off the handle for later disposal.
LEA filePB(A5),A0 ; the parameter block again,
; MOVE.W gPictRefNum(A5),ioRefNum(A0) ; the refnum to use.
MOVE.L (A2),ioBuffer(A0) ; pointer to the handle of data.
MOVE.L #headerSize+picData,ioReqCount(A0) ; number of bytes to write.
MOVE.W #fsFromStart,ioPosMode(A0) ; start at start in file,
CLR.L ioPosOffset(A0) ; no offset from start.
_Write ; Write the empty header to the file.
; Now dispose the handle we used to write the empty header to the file. Disposed
; before the error check to avoid having to dispose it in the error handler.
MOVE.L A2,A0 ; handle to the data,
_DisposHandle ; freed back to system.
; Check the result of the write.
TST.W filePB+ioResult(A5) ; retrieve the error code from Write.
BNE DeathCity ; if we got an error, leave.
; Now open a port for our
; use to allow us to easily copy the screen to itself, and to avoid having to worry if
; a port is valid currently. We have preflighted at least 500 bytes out of the heap,
; which should be enough for a port, so we won<6F>t blow up on this call at least.
; In addition to the Open, this becomes the current port. (as: thePort) The
; same record is used whether it is color or not.
PEA picPort(A5) ; address of record to fill
TST.B sysInfo.hasColorQD(A5) ; do we have color QD?
BEQ.S @0 ; if not, open a regular port.
_OpenCPort ; fill it with color port junk.
BRA.S @1
@0
_OpenPort ; open a standard port.
@1
ST portIsValid(A5) ; Set bool to Yes, for the picPort being in use.
; Now that we have a new port to draw into, we want to be sure to get all the monitors/
; devices in the system. So... use the GrayRgn from low memory to Union with the current
; visRgn we have in our new port. We want to do the Union since the new Port's visRgn will
; include the menu bar area, and GrayRgn doesn't. But of course, the new port doesn't have
; the other devices, so we need the union. This is also considered a little sick, that of
; changing the visRgn of a port, but it needs to be done. Once we've changed the visRgn,
; we'll get the whole system when we CopyBits.
MOVE.L picPort+visRgn(A5),-(SP) ; the handle to the visRgn for the new port.
MOVE.L GrayRgn,-(SP) ; the handle to GrayRgn out of low mem.
MOVE.L picPort+visRgn(A5),-(SP) ; the visRgn as the destination as well.
_UnionRgn ; modify the visRgn to include all monitors.
; Now save the bounding rect that encompasses all the monitors.
MOVE.L picPort+visRgn(A5),A0 ; the handle to the visRgn.
MOVE.L (A0),A0 ; pointer to the region.
LEA maxRect(A5),A1 ; the address of our Rect,
MOVE.L rgnBBox+topLeft(A0),(A1)+ ; copy over the topLeft,
MOVE.L rgnBBox+botRight(A0),(A1)+ ; and the botRight
; Set the new GrafProc record up to have the standard pieces. Here again,
; use the same record for both color and not, but use the larger color record,
; not filling all the space in the non-color case.
PEA newGrafs(A5) ; the new array to use.
TST.B sysInfo.hasColorQD(A5) ; do we have color QD?
BEQ.S @2 ; if not, set std procs, not color.
_SetStdCProcs ; fill the array with procs.
BRA.S @3 ; go on to change procs.
@2
_SetStdProcs ; fill the array with B&W procs.
@3
; Change the port to use those GrafProcs instead.
; thePort^.grafProcs := @newGrafs
LEA picPort(A5),A0 ; get address of thePort.
LEA newGrafs(A5),A1 ; address of new procs record,
MOVE.L A1,grafProcs(A0) ; set the port to the new record.
; We are in our offscreen port. Change the GrafProc pointer for picture saving
; to our routine that actually writes the bytes to the disk. Works for both color and
; regular.
LEA PictWriter(PC),A0 ; the address of the routine to use,
MOVE.L A0,putPicProc(A1) ; change the newGraf record.
; Now when the putPicProc gets called, it will run to our PictWriter routine. This
; way we get to save the data off to the disk instead of use up huge hunks of RAM to
; build the picture. PictWriter will use the gPictRefNum as the file to write to, but we
; need to init a couple of other global variables to set up for the picture. The two other
; variables were already cleared by the top loop that cleared all the globals.
MOVE.W #picData,gPictSize(A5) ; start off as just header size.
; CLR.W gPictError(A5) ; starting as no error.
; CLR.L gPictHandle(A5) ; since we have no picture handle yet.
; Actually open the picture and do the CopyBits in order to process the picture.
; The data will be written by PictWriter as it is called by QuickDraw.
CLR.L -(SP) ; the handle result comes back on stack.
PEA maxRect(A5) ; the rect to use, full screen.
_OpenPicture ; start up the process.
MOVE.L (SP)+,gPictHandle(A5) ; save the handle created.
; Now the picture is open and ready to cruise. Start by setting the clip region down,
; to make it a more mellow picture (clipping weirdnesses abound). This clip rect is the
; rect that surrounds all monitors in the system.
PEA maxRect(A5) ; the rect to use again,
_ClipRect ; clip to the screen bounds.
; Loop through each monitor in the device list. With each monitor, do a CopyBits of the
; full device, just the bounds rectangle.
TST.B sysInfo.hasColorQD(A5) ; do we have color QD?
BEQ.S CopyLips ; if not, skip the GDevice stuff.
; Otherwise, we need to drive through the device list, so get the first one.
CLR.L -(SP) ; returned GDHandle here.
_GetDeviceList ; get start of list.
MOVE.L (SP)+,nextGD(A5) ; save the handle.
BEQ DeathCity ; if no devices, get out of here.
; Loop back here to CopyBits the next device in the list. The maxRect as we use it here is
; for a given gDevice/screen, and isn't the total area of all monitors, as it was when we
; setup the clipping.
DoNextDevice
MOVE.L nextGD(A5),A0 ; get the handle to gDevice,
MOVE.L (A0),A0 ; deref the handle,
MOVE.L gdPMap(A0),A0 ; get PixMapHandle,
MOVE.L (A0),A0 ; deref to pointer of PixMap.
LEA maxRect(A5),A1 ; the address of rect to save,
MOVE.L bounds+topLeft(A0),(A1)+ ; save off the TopLeft of the rect,
MOVE.L bounds+botRight(A0),(A1)+ ; save the botRight too.
; For each gDevice that we copy, we want to start off the banding parameters as being the
; full screen size, basically the maxRect parameters. If this is a single monitor old system,
; the concept still holds. Start the copyTop at the top of the current screen, start the copyHeight
; as the full screen size, and start the copyRect as the full screen. These will all get changed
; if there isn't enough ram to do a full screen copybits. If there is enough ram, we'll get
; a single CopyBits saved in the picture for this screen/gDevice.
CopyLips
MOVE.W maxRect+top(A5),copyTop(A5) ; start first band at top of screen.
MOVE.W maxRect+bottom(A5),D0 ; get the bottom of screen,
SUB.W maxRect+top(A5),D0 ; giving height of screen,
MOVE.W D0,copyHeight(A5) ; save it off as starting copyHeight.
LEA copyRect(A5),A1 ; address of our copyRect,
MOVE.L maxRect+topLeft(A5),(A1)+ ; save off the TopLeft of the rect,
MOVE.L maxRect+botRight(A5),(A1)+ ; save the botRight too.
; Set up the banding parameters for this time through the band loop. This may be a one
; time shot, but we can't be sure, and don't care, so just do it generally.
BandLoop
MOVE.W copyTop(A5),D0 ; get the top of the next band to do,
MOVE.W D0,copyRect+top(A5) ; set the top of the copyRect to new top.
ADD.W copyHeight(A5),D0 ; add in current band height, giving bottom.
CMP.W maxRect+bottom(A5),D0 ; are we off bottom of full screen rect?
BLT.S @0 ; if not, stow the value.
MOVE.W maxRect+bottom(A5),D0 ; if we are, just stop at the max bottom.
@0
MOVE.W D0,copyRect+bottom(A5) ; the bottom of the next rectangle (band).
; The parameters are all setup for this band (maybe full screen).
; The picture is ready to roll. CopyBits the screen to itself. While inside of an open
; picture this will save all the bits off into the picture handle. Since the handle is
; being written to disk as it is created by the PictWriter, this CopyBits will automatically
; spool the data out to the disk. We use the copyRect since we may only be doing a band
; of the full screen right now.
SF gWroteBits(A5) ; set up to handle full memory case.
PEA picPort+portBits(A5) ; the source bitMap,
PEA picPort+portBits(A5) ; the destination bitMap too.
PEA copyRect(A5) ; the source rectangle, max screensize.
PEA copyRect(A5) ; the dest rect too.
MOVE.W #srcCopy,-(SP) ; use the copy mode.
CLR.L -(SP) ; and a Nil clipping region.
_CopyBits ; copy the screen bits.
; We have written that band out to the file using the bottleneck. If QD failed by not having
; enough RAM we need to shrink the band size down. We know whether it failed or not
; by checking the gWroteBits variable to be sure the bottleneck got called, and we'll also
; check QDError just to be sure. QDError is only valid for color machines.
TST.B sysInfo.hasColorQD(A5) ; do we have color QD?
BEQ.S @1 ; if not, skip QDError check.
CLR.W -(SP) ; return result,
_QDError ; get last error,
MOVE.W (SP)+,D0 ; get value QD last set,
BNE.S ShrinkCopyHeight ; if nonZero, try shrinking the band Height.
@1
TST.B gWroteBits(A5) ; and check the write flag,
BNE.S UpCopyTop ; if it is true, we wrote bits, and can go on.
; We have a case where the CopyBits to save the image didn't work, so the bottleneck never
; got called to write any data. Working under the assumption that it is a memory full error,
; we want to shrink our band by half, and try again.
ShrinkCopyHeight
MOVE.W copyHeight(A5),D0 ; get the current copyHeight
LSR.W #1,D0 ; divide the height by two.
MOVE.W D0,copyHeight(A5) ; save off the new number.
CMP.W #1,D0 ; are we down to 1 pixel high yet?
BLE.S DeathCity ; if we are, just skip it, not enough ram.
BRA.S BandLoop ; try this smaller band instead.
; We didn't get any error while doing that band. Up the copyTop to the next band to be done,
; and check if we are done with the entire max rect yet. If we are done, we can fall into the
; outer loop of the gDevices on the system. If not, we'll go back to do another band for this
; current gDevice.
UpCopyTop
MOVE.W copyTop(A5),D0 ; get the band top we just used,
ADD.W copyHeight(A5),D0 ; add in the current bandsize,
MOVE.W D0,copyTop(A5) ; save that new band top.
CMP.W maxRect+bottom(A5),D0 ; are we off the end of the device yet?
BLT.S BandLoop ; not off bottom of device, so do another band.
; If we have colorQD we need to get the next device and copy it. If we don't have color QD
; skip this stuff, since it will cause a system error if we run it. Can you say 'special case?'
; Sure, I knew you could.
EndDeviceLoop
TST.B sysInfo.hasColorQD(A5) ; do we have color QD?
BEQ.S PictureDone ; if not, skip the GDevice stuff.
; We have done that monitor. Now go to the next one in the list. When we get to the
; end we will get NIL back.
CLR.L -(SP) ; make room for the result.
MOVE.L nextGD(A5),-(SP) ; where we are now,
_GetNextDevice ; get us the next device in the list.
MOVE.L (SP)+,nextGD(A5) ; move the gDevice handle.
BNE DoNextDevice ; jump back to CopyBits more if there is more.
;------------------------------------------------------------------
; That was a ton of work, but we didn't see it, PictWriter did.
; Now we are done saving the bits to disk, we can shut down the picture saving operation.
PictureDone
_ClosePicture ; close the picture.
; We are done with the picture operation, see if we had an error while it was going on.
; We have to check afterwards, since QD has no error checking, by design, for some
; unknown reason.
MOVE gPictError(A5),D0 ; was there an error during spooling?
BNE.S DeathCity ; if so, we are dead meat, and must leave.
; Now we know the file was written OK, so we have to drive back to the front of the
; file and update the pict size so it is correct. Do the Positioning and Write operation
; in the same call.
; Now write the 10 byte header off the pict handle to the disk, so that info is correct
; in the Pict file. The size and rect need to be written, since they won't be right
; until the picture is complete.
LEA filePB(A5),A0 ; the PB to use.
MOVE.W gPictRefNum(A5),ioRefNum(A0) ; use the refnum we opened with,
MOVE.L gPictHandle(A5),A1 ; the handle to the pict,
MOVE.L (A1),ioBuffer(A0) ; the buffer to write.
MOVE.L #picData,ioReqCount(A0) ; the number of bytes to write.
MOVE.W #fsFromStart,ioPosMode(A0) ; do offset from the start of file,
MOVE.L #headerSize,ioPosOffset(A0) ; and offset past the 512 byte header.
_Write ; write the final data.
BNE.S DeathCity ; if an error, split.
; From here on out, we are cleaning up the world to what it was before we came in.
; That means we won<6F>t have any failures (recoverable anyway) during this exit.
BSR.S CloseNClean ; close the file, clean up blocks.
BRA.S CommonExit ; and leave cleanly.
;------------------------------------------------------------------
; This is the error handler. This is where we have died from some error during the
; whole operation. When we get here, we have to dispose of all the junk we created,
; clean up, let the user know we chooked, and exit. *** does there need to be something
; more sophisticated to let the user know we died? Notification request perhaps?
DeathCity
MOVE.W #8,-(SP) ; length for SysBeep,
_SysBeep ; a beep to signify errors.
BSR.S CloseNClean ; close the file and cleanup.
; Since we had some skanky error, attempt to delete the file we created at the start,
; to clean up bogus junk. If we get an error on the delete that<61>s OK.
LEA filePB(A5),A0 ; the parameter block,
; LEA fileName(A5),A1 ; the pointer to the file name string,
; MOVE.L A1,ioFileName(A0) ; the file name to delete,
; MOVE.W sysInfo.sysVRefNum(A5),ioVRefNum(A0) ; use startup volume, as above.
; CLR.W ioFileType(A0) ; and no file type.
MOVE.L #fsRtDirId,ioDirID(A0) ; file was in the root directory.
_HDelete ; try to kill it.
;------------------------------------------------------------------
CommonExit
MOVEM.L (SP)+,A0-A2/A5/D0-D2/D7 ; restore registers we trashed. (9/25/90)
UNLK A6 ; kill local vars from stack.
RTS ; Exit from FKEY, successful or not.
;------------------------------------------------------------------
; Close the File and clean up our tracks.
; Everything is done, put the CrsrVis/CrsrState back before someone sees that
; we were playing with it. If we get caught, we face life in prison. This is done
; whether we have an error or not.
CloseNClean
MOVE.B oldCrsrVis(A5),CrsrVis ; restore the cursor visibility.
ADD.B #1,CrsrState ; Turn cursor back on.
; Restore the state of the itl4 resource handle if we got one
MOVE.L itlHandle(A5), D0 ; did we have this handle
BEQ.S @3 ; if not, don<6F>t bother
MOVE.L D0, A0 ; get the handle into A0
MOVEQ #0, D0 ; a clean register is probably not needed
MOVE.B itlState(A5), D0 ; get the saved handle state
_HSetState ; and make it like it used to be
@3
; Close it in case it is open. If it is not open, gPictRefNum will be zero.
LEA filePB(A5),A0 ; the pb block to use,
MOVE.W gPictRefNum(A5),ioRefNum(A0) ; the file refnum we just used.
BEQ.S @2 ; if no file was open, skip it.
_Close ; close the file.
@2
; Kill the picture handle that was created at OpenPicture time, if it exists.
; If it never made it, the handle will be nil, allowing us to skip it.
MOVE.L gPictHandle(A5),D0 ; get the handle to the picture,
BEQ.S @1 ; if it is NIL, skip the Kill.
MOVE.L D0,-(SP) ; else, the handle to the stub on stack,
_KillPicture ; kill the swine.
@1
MOVE.L currPort(A5),-(SP) ; and the old port,
_SetPort ; so we are back.
; And dispose the port that we created before, since we are done.
TST.B portIsValid(A5) ; is the port valid, set up?
BEQ.S @0 ; if not, skip the close,
PEA picPort(A5) ; address of port to blast.
_ClosePort ; close the dog, releasing memory.
@0
RTS
;------------------------------------------------------------------
; PictWriter:
; This routine is the bottleneck procedure that actually gets called when QuickDraw
; is slamming through the bits while copying the screen. For each time it gets called
; it will save off the bytes passed and check the error stuff.
; As found in a Pascal routine:
;
; { This routine will save the current image as it is created. As the data requests
; go by that data will be written to the file. The data is being created by the
; OpenPicture/CopyBits in DoWrite, this is the bottleneck for that operation.
; Any errors found while doing this will make us skip any further requests
; to write data to the disk. No memory is allocated. Communication with
; DoWrite is done through globals, since bottlenecks must be at the main
; level. The bottleneck must also keep track of how many bytes are written,
; so that the header on the picture can be fixed up to be correct. This must
; be done to avoid creating bogus pictures. The picSize field of the handle
; must be updated continuously so that when the picture is done, the ClosePicture
; can create a valid picture. The check for the NIL handle is to handle the
; problem of when the OpenPicture is called. The proc gets called before
; the handle is valid. Be very careful of these bottleneck things, it is
; easy to run into problems that are very hard to figure out. QuickDraw
; has no facilities to give you info when things go wrong so it makes it
; a bit tougher. }
;PROCEDURE PictWriter (dPointer: Ptr; nextHunk: Integer);
;
;VAR longHunk: LongInt;
;
;BEGIN
; IF gPictError = noErr THEN BEGIN
; longHunk := nextHunk;
; gPictError := FSWrite(gPictRefNum, longHunk, dPointer);
; gPictSize := gPictSize + longHunk;
; IF gPictHandle <> NIL THEN gPictHandle^^.picSize := LoWord (gPictSize);
; END;
;END;
; For the PictWriter parameters, we will just use an A7 relative stack frame. The
; A5 stack frame for our globals will still be in effect from the WITH at the top, and
; we will explicitly use the PictWriterStack parameters when we need them here,
; doing the relative (SP) as expected, instead of (A5).
PWStack RECORD {EndRegs},DECR
PBegin EQU * ; start parameters after this point
dPointer DS.L 1 ; offset to the pointer,
nextHunk DS.W 1 ; offset onto stack to parameter,
PWSize EQU PBegin-* ; to remove these dudes.
RetAddr DS.L 1 ; return address on stack.
SaveRegs DS.L 6 ; save/restore 6 registers in here.
EndRegs EQU * ; to set the location.
ENDR
PictWriter
MOVEM.L A0-A2/D0-D2,-(SP) ; save registers we trash.
MOVE.W gPictError(A5),D0 ; get current error result.
BNE.S NoCanDo ; if we have an error pending, exit now.
ST gWroteBits(A5) ; mark us as having had enough ram.
CLR.L D0 ; start with empty register
MOVE.W PWStack.nextHunk(SP),D0 ; get size of next piece to write.
LEA filePB(A5),A0 ; address of i/o parameter block,
MOVE.W gPictRefNum(A5),ioRefNum(A0) ; the refnum to use.
MOVE.L PWStack.dPointer(SP),ioBuffer(A0) ; address of data to write.
MOVE.L D0,ioReqCount(A0) ; number of bytes to write, as longInt.
MOVE.W #fsAtMark,ioPosMode(A0) ; start at mark in file,
CLR.L ioPosOffset(A0) ; no offset from current mark.
_Write ; write the data to the open file.
MOVE.W D0,gPictError(A5) ; set the error code to yea/nay.
; Now the data is out there in the file, update the picture size variable.
MOVE.W PWStack.nextHunk(SP),D0 ; retrieve the count of bytes to do,
ADD.W D0,gPictSize(A5) ; add, ignore overflows, QD does.
; And update the word in the header of the Picture Handle to match.
MOVE.L gPictHandle(A5),D0 ; handle to the data block,
BEQ.S NoCanDo ; if nil, skip it for now.
MOVE.L D0,A0 ; put back in Address land.
MOVE.L (A0),A0 ; deref to pointer.
MOVE.W gPictSize(A5),picSize(A0) ; set the size parameter.
NoCanDo
MOVEM.L (SP)+,A0-A2/D0-D2 ; restore registers we trashed.
MOVE.L (SP)+,A0 ; retrieve return address.
ADD.L #PWStack.PWSize,SP ; kill the parameters,
JMP (A0) ; and return.
ENDWITH ; done with the global Frame.
;------------------------------------------------------------------
; Here we have a string constant that we want to use in order to do a Munger thing
; on the creation of the filename. We will search a given file string for the ^0 and replace
; it with the current file number we are creating. This ^0 allows us to be more
; internationally compatible, since the number can be at the front of the string which is
; nicer and more grammatically correct for some languages. Not to mention it is easy
; for us to do it here. This ^0 is defined here in the code as a constant, and isn't modified,
; but is used as the source for the string search in Munger.
String Pascal ; put length byte on this one.
upSnick0Str DC.W '^0' ; the string piece to find and replace.
String Pascal ; put length byte on this one.
hardName DC.W 'Pic ^0' ; default name, in case STR is lost.
; set to funny name to show something is wrong.
extendedOne ; the constant 1 in extended format
DC.W $3FFF, $8000, $0000, $0000, $0000, $0000
numFormat
DC.B $30,$00,$00,$05,$00,$00 ; internal representation of the format
DC.B $00,$00,$00,$05,$00,$05 ; for our number <20>####<23>
DC.B $00,$05,$00,$04,$00,$00
DC.B $00,$00,$00,$00,$00,$00
DC.B $00,$00,$01,$00,$00,$00
DC.B $00,$00,$00,$00,$00,$00
DC.B $00,$00,$00,$00,$00,$00
DC.B $00,$00,$04,$05,$05,$05
DC.B $05
ENDP ; end of proc.
END