TweetFollow Us on Twitter

Printer Driver
Volume Number:3
Issue Number:11
Column Tag:Advanced Mac'ing

How to Write a Printer Driver

By Earle Horton, Hanover, NH

Writing a Simple Macintosh Printer Driver

Part 1: Overview

A Macintosh Printer Resource File is a file containing resources to emulate QuickDraw drawing commands on a printer or other remote device. This article describes how to write a Printer Resource File which implements text-drawing commands only on serial dot matrix and daisy wheel printers. The printer file is installed using the Chooser in the same manner as one would install the ImageWriter or LaserWriter file. The printers which are serviced by this printer driver are assumed to be capable of printing in only one font, which is mono-spaced. An ability is included in the printer driver to change serial port settings and printer control strings, so that a large number of different types of printers may be used with one driver. In addition, it is shown where more advanced drawing commands (graphics and proportional fonts) would fit into the structure of the Printer Resource File. This Printer Resource File was written, from scratch, by the author in two weeks. I am not a professional programmer, or even a registered Macintosh developer. I feel that the dearth of Printer Drivers for the Macintosh is due more to apparent, rather than real, difficulties in writing them.

I wrote my Printer Resource, which I christened Daisy, using LightspeedC for the code resources and RMaker for the data resources in the file. The table below summarizes all the resources in my Printer Resource. Most of these follow the standard types and IDs used by Apple, but some are particular to my implementation.

Printer File Resources and Function (Public)

Type ID Function

''DRVR’ -8192 Device driver: performs low-level printer calls and maintains storage

‘PREC’ -8192 Driver’s private storage

‘PREC’ 0 Default print record

‘PREC’ 1 Copy of last used print record

‘PDEF’ 0 Draft mode printing routine

‘PDEF’ 1 Spooling routine (not used)

‘PDEF’ 2&3 Printer specific printing code (not used)

‘PDEF’ 4 Code for handling printing dialogs

‘PDEF’ 5 Spool file printing routine (not used)

‘PACK’ -4096 Chooser interface code, used for device configuration

‘STR ‘ -8191 Default spool file name

‘STR ‘ -4096 Type name for AppleTalk devices (not used)

‘STR ‘ -4093 Title for Chooser left button (not used)

‘STR ‘ -4092 Title for Chooser right button

‘STR ‘ -4091 Chooser list label

‘STR ‘ -4090 Chooser reserved string (not used)

‘DLOG’ -8192 Paper Style dialog

‘DITL’ -8192 Paper Style dialog template

‘DLOG’ -8191 Paper Job dialog

‘DITL’ -8191 Paper Job dialog template (Private)

‘DLOG’ -4080 PACK resource installation dialog

‘DITL’ -4080 Installation dialog template

‘Stng’ -8192 Private type, printer settings

‘STR#’ -4080 Printer control string list

‘BNDL’ 128 For the file’s icon

‘DasY’ 0 File creator, resource is a ‘STR ‘

‘FREF’ 128 For the file’s icon

‘ICN#’ 128 Icon and mask

‘DLOG’ -8193 Sheet feed dialog

‘DITL’ -8193 Sheet feed dialog template

The Printer Resource file contains a fairly large number of resources, but each one has a well defined function. All of the “public” resources have type names and ID numbers rigidly defined by Apple, and also specific formats. The private resources should have ID numbers of “owned” resources, owned by either the driver, one of the ‘PREC’ resources, or the ‘PACK’ resource so as not to conflict with any application resources. The file must have a ‘BNDL’, ‘FREF’, ‘ICN#’, and creator resource in order to appear in the Chooser display. This printer file uses the Chooser interface code, ‘PACK’ -4096, so it therefore needs to be installed with the Chooser, and not Choose Printer.

This printer resource file is of type ‘PRER’, or ‘non-serial printer’, so that it can use the Chooser interface to query the user for application-independent installation parameters. These include the baud rate, what kind of flow control to use, and some printer control strings. If your printer resource does not need to use the Chooser interface, then you can make it of type ‘PRES’, or serial printer, and the Chooser will take care of all details of installation. If you do it this way, then the Chooser will put the user’s choice of serial port in parameter RAM, so that your printer driver can find it later.

The Printer Resource File contains a number of resources containing pure 68000 (or 68020) machine code. The ‘DRVR’ handles low-level Printing Manager calls, while the ‘PDEF’ resources handle high level printing. It is possible to define up to four different kinds of high level printing, but it is only necessary to provide one. The ‘PACK’ resource contains a single routine to handle the printer installation process. I wrote all of my code resources using LightspeedC, and used some InLine assembly to format the headers of the resources where required. There are some requirements which must be met by the development system used. The development system must be capable of producing stand alone code resources, and must allow you to specify the format of the code resource header. The development system must also be capable of producing functions that are called according to the Pascal conventions in Inside Macintosh. It appears then that most commonly available C and Pascal development systems are capable of producing working printer files, with perhaps a few dozen lines of assembler “glue” to make things fit together.

Fig. 1 Our Printer Driver is Chooser Compatible!

Of the non-code resources, most are standard, except for the ‘Stng’ type and the ‘PREC’ type. The ‘Stng’ type is just an array of integers I use to store configuration values, and is unique to my implementation. The ‘PREC’ type (0 and 1) is a disk copy of the Printing Record data structure, which the printing code uses to keep track of paper size and other parameters associated with each printing “job”. The printing record will be discussed in detail when I treat the style and job dialog code. The ‘PREC’ -8192 is merely a disk copy of whatever storage is needed by the driver and printing code. A good way to initialize variables used by the printer file is to keep their default values in a data structure in this resource. When LightspeedC creates a device driver, it uses a resource of type ‘DATA’ to do this, and creates initialization code to load it in when the driver is opened. Since the Chooser does not install the ‘DATA’ resource for me, I keep it in the printer resource file, and give it a type reserved for printer driver storage. When the driver is opened, I open the printer resource file, and load my storage resource before doing anything else.

When the printer is installed by the Chooser, the driver is added to the system resource file, and the ID is changed from -8192 to 2. The name is also changed from “.XPrint” to “.Print”. The structure of the printer resource file is admittedly fairly complicated. Taken a piece at a time, however, it is not a particularly difficult task to write one.

Part 2: The driver

The Printer Resource File code is written as a series of separate overlays, each of which has a specific function and a rigidly defined format. We will start with the driver. The driver has the same structure as any Macintosh device driver or desk accessory, and responds to Open, Close, Control, and Status calls. It does not appear to respond to Prime (read/write) calls. The driver’s private storage was originally contained, as a resource, in both the Printer Resource File and the System Resource File as a resource of type ‘PREC’ with the same resource ID as the driver. Apple appears to have discontinued this practice, but we will use it to store data structures used by various parts of the printing code. The driver shown here is written using the LightspeedC compiler, device driver option. When global variables are defined in a LightspeedC device driver project, they are stored in a resource of type ‘DATA’ and accessed via offsets from register A4. This is changed to type ‘PREC’, and code is provided to load this resource in at open time. The driver’s private storage is found in a Handle stored in the driver’s Device Control Entry, and is referenced through the dereferenced Handle.

The initialization code I have written for the device driver opens the Printer Resource File, loads the resource containing the driver’s private storage, and stores the Handle to it in the driver’s DCE. I have to provide code for opening the Printer Resource File, because it is possible to open the printer driver without the Printer Resource File being open at the time. The standard means of opening the printer driver is to call PrOpen, which opens the Printer Resource File. Assume the worst, however, and give the driver a means of opening the resource file. I use SysEnvirons to get the RefNum of the folder with the system resource file in it, and OpenRFPerm to open the printer resouce file. If the machine is older than 128k ROMs, I use OpenResFile, and hope that this routine looks on the volume containing the system (it probably does). I have not done verification tests on ANY parts of my printer driver for 64k ROMs or Mac XLs. Sorry.

The driver’s private storage is defined in a header file, so that it can be shared between the driver and other printing code overlays. Using conditional compilation, the same identifiers are used for static variables in the driver code, and data structure members in the other overlays.

The functions the driver open routine is expected to perform are as follows: It initializes a “permanent” storage area used by the printing code. It opens a channel to the serial driver used for printing. In my printer driver, it obtains settings to be used during printing from the printer resource file. The driver responds to several device manager control calls which are expected to perform useful printer functions. My driver loads a string list from the Printer Resource File which contains strings to perform these functions, so they can be changed without recompiling the code. In addition to hardware-oriented device control calls, the driver answers one control call and one status call from the Font Manager. These are for the purpose of determining which fonts and spacings the printer is capable of. Most applications appear to ignore this information, and attempt to print what is shown on the screen. We provide the font information anyway, since it can’t hurt.

LightspeedC provides a rather different programming shell for device drivers and desk accessories from that which is used by other development systems. The programmer writes one function, main(), which is called with the same parameters that are used to call the Open, Control, Status, Prime, and Close routines of the driver. A third parameter, an integer, indicates to main() which of the five driver routines was, in fact, intended. A switch statement in my driver code takes the place of the five device driver routines which you would have to write if using another development system. Note that this is certainly non-standard!

The first driver routine to be called is the open routine. This may be called several times during the course of an application session. We are called here, and everywhere, with three parameters: a parameter block, a device control entry pointer, and (particular to LightspeedC) an integer indicating which switch block to execute. The open routine checks the dCtlStorage field of our device control entry to make sure storage has been allocated, and allocates it if not. Since we read in our storage from the Printer Resource File, we have to find the “Blessed Folder” first. I use SysEnvirons to do this, then OpenRFPerm to open the resource file. (SysEnvirons is available as an OSTrap with system 4.1 and newer, and as glue for MPW and LightspeedC. It will return the correct VRefNum for the system folder on all Macs, I have been told.) Then we read in our storage from the resource file. Other tasks are to check to see if other required initializations have been performed, and to open and configure the serial port. It does not hurt anything to open the serial port more than once, so we open it every time the open routine is called. Also, the serial port parameters can be changed by the user in our Chooser interface, so the open routine sets the serial port parameters to whatever was “Chosen” last. The serial port is never closed, to maintain compatibility with 64k ROMs.

The driver control routine does most of the work of hardware-related printer functions, as well as part of the interface to the Font Manager. Since this driver is used for text-only functions, we perform only a subset of the low-level printer driver calls here. When the control routine is called, the csCode of the parameter block determines which driver function is required. I will discuss first the csCodes which I do not implement.

Printer driver parameter blocks have the usual driver control parameter block structure, but the fields following csCode are almost always three longs, lParam1, lParam2, and lParam3. A modified definition of the control parameter block, included in the master header file, is used to define these. When csCode is iPrBitsCtl or iPrEvtCtl, some sort of bitmap transfer to the printer is desired. With iPrBitsCtl, lParam1 is a pointer to the bitmap, lParam2 is a pointer to the rectangle to be printed, and lParam3 contains resolution information (defined for the ImageWriter). When csCode is iPrEvtCtl, this means that either (command-shift-4) or (command-shift-capslock-4) has been pressed, depending on lParam1. lParam1 is iPrEvtAll for printing the entire screen, and iPrEvtTop for printing the top window on the screen. If you have a printer capable of printing bitmaps in some fashion, then you can at this point translate the appropriate QuickDraw bitmap to one which your printer can handle, then simply send the graphics commands to your printer to do this. Doing so is the quickest way to get some sort of graphics capability for a non-Apple printer with the Macintosh. A few hours with Inside Macintosh and the manual for your printer should be enough to do this, but that is not the topic of this article...

When csCode is iPrIOCtl, then text streaming is being used. lParam1 is a pointer to an area of memory containing characters to be sent to the printer. lParam2 is a long integer which tells us how many characters to send. We simply use these values to fill in the ParamBlockRec we use to communicate with the serial driver, and issue a PBWrite call. Note here that I use inline assembly to issue the PBWrite and some other calls. I do this to suppress loading of libraries, and to save code size thereby. With LightspeedC if we wish to load PBWrite from a library, then we have to load in also all the PB... calls. LightspeedC compiles many of the Macintosh Toolbox calls into inline code, but not register based ones. This is partly a matter of taste, and partly a matter of how your development system handles things. I find that doing things this way helps in debugging too, since most all of the code I see when using MacsBug is code which I have written. Anyway, there is not much to handling iPrIOCtl.

When csCode is iPrDevCtl, we are being called upon to perform one of several printer control actions. lParam1 serves as an opcode to specify the desired function. All functions are performed here by sending Pascal strings to the printer, and the routine that services iPrDevCtl looks up the proper string, and issues a PBWrite to send it to the printer. Error checking could be added here, and in the iPrIOCtl code, to check for possible serial driver or printer errors. A status call to the serial driver might be appropriate, or a status call to the printer, if it accepts these. Errors usually do not occur here, and when they do the user can probably detect them, but it is a nice touch if the driver does so also. For instance, if you are writing a printer driver for a printer that can inform the computer when it has run out of paper, then your driver could put up a dialog with an “Out of Paper” message.

An important point should be made here concerning “Device Independence”. The philosophy of Apple is that there is a different printer driver for each printer, thereby providing device independence to applications. However, many of the printer functions, with the possible exception of the bitmap calls, can be specified to the driver in the format of resources, as I do here. Certainly there is no need to have a different printer driver for each baud rate. In my Chooser interface, I show one method of letting the user specify printer parameters with a dialog box. Another method might be to have one driver, and several user-selectable configuration files. For a simple text-only printer driver, the ability to handle many different kinds of printers is certainly desirable. The more functions you support, however, the less you will be able to do this, particularly if you decide to support graphics.

The printer driver answers to one Font Manager control/status call, when csCode is iFMgrCtl. The status call asks for the printer’s “Font Characterization Table”, and lParam1 points to where to put it. Frankly, since I only support mono-spaced fonts at this point, I just give it a copy of the ImageWriter Font Characterization Table, copied from the Font Manager Chapter of the Promotional Edition of Inside Macintosh. The Font Characterization Table gives information about how much space is taken up by different text styles, and how the fields are used is not well documented, so I have not attempted to do much with it. The Font Manager control call is to inform the printer driver which font has been selected for use, and to give the driver a chance to modify it. The original Inside Macintosh says lParam1 points to an FMOutput record in this case, but use of MacsBug shows that it is a pointer to an FMInput record. The device field of the FMInput contains an integer from one of my style dialogs, so I can use it to determine information which is specific to my driver. I use it here to determine character pitch, either 10, 12, or 15 cpi. At present, I tell the Font Manager I want Monaco 9, and modify the width according to character pitch. Much more experimentation is needed to make full use of these Font Manager calls, particularly if you want to support proportional fonts, or if you hope to work with applications which do fancy formatting. Justification does not work very well with my driver at this point, partly because I do not yet know how to respond to these calls in the best manner.

The driver close routine is simple. I purge the Handles to whatever storage I use, and set my copies of them to “nil”. If your printer driver will only work on 128k ROM machines or better, or if you plan to include the RAM serial driver, then perhaps you would want to do a RAMSDClose at this point. The correct thing to do here is unclear to me, especially remembering what happened when I had 64k ROMs and attempted to close the serial driver... (For those who never had 64k ROMs, the dreaded “mouse freeze” was the usual result.) There is usually not a problem here, unless the user plans to switch cables around with the Macintosh left on (not a good idea, anyway).

These are the functions performed by the driver, and how it is put together. Most development systems provide a “template” for writing desk accessories or device drivers, or a main unit which you link your driver service routines to. In this sense, the driver is the easiest part of all to write, since the code header is already taken care of for you. If you can write a desk accessory, then you can write this part of the printing code, no problem.


/*
 * XPrint.c.  printer driver for daisy-wheel printers
 * for the Macintosh.
 * Earle R. Horton.  August 17, 1987
 * LightspeedC source.
 * Set the project type to Device Driver, ID #2.
 * Run RMaker to put resources in printer resource file.
 * Install with Chooser.
 */
#include <WindowMgr.h>  /* inc. QuickDraw.h, MacTypes.h */
#include <EventMgr.h>
#include <DialogMgr.h>
#include <SerialDvr.h>  
#include <FontMgr.h>
#include <HFS.h>
#include <asm.h>
#define  DRIVER_MODULE
#include “prglobals.h” /* see end of this code for this */
/*
 * LightspeedC doesn’t provide these as InLines.  To save space, I provide 
my own routines.  This way, I get around having to link in MacTraps.
 */
intHNoPurge();
intHPurge();
intHLock();
intCloseDriver();

intcheckabort();

/* Useful constants */
#define OPEN0
#define PRIME  1
#define CONTROL  2
#define STATUS 3
#define CLOSE  4

#define SERRESET 8
#define SERSHAKE 10

#define XONCR  ((char)17)
#define XOFFCR ((char)19)
#define RESFILEID(-8192)
#define PACKID   (-4080)  

main(p, d, n)
PrParam *p; /*  ==> parameter block  */
DCtlPtr d;/*  ==> device control entry  */
int n;  /*  entry point selector  */

{
/* Check to make sure our data area was allocated.  It may not have been 
if we were opened with a low-level open or even a device manager PBOpen() 
and our printer resource file was not opened.  Note that we cannot use 
any of LightspeedC’s “global” variables until after we get our storage 
from the resource file and lock it down, making A4 point to it.  This 
probably does the same thing that the prolog code attempts to do with 
the original ‘DATA’ resource created by LightspeedC.*/

inti;
Handle  storage;
 if(d->dCtlStorage == nil && n != CLOSE){
 if (openprinterfile(d) == -1){
              PrintErr = ResError();
              CloseDriver(d->dCtlRefNum);
              return(-1);
      }
 }
 HLock(d->dCtlStorage);
 storage = d->dCtlStorage;
 asm{
 movea.lstorage,a0 ;; LS C uses A4 as the base
 movea.l(a0),a4  ;; for driver globals.
 }
 if (d->dCtlStorage != nil) switch (n){/*  dispatch  */

     case OPEN:  /*  open  */
 openprinterfile(d);
 if((settings = (Pfg)(GetResource(‘Stng’,RESFILEID))) == nil ||
 GetResource(‘STR#’,PACKID) == nil){
 CloseDriver(d->dCtlRefNum);
 return(-1);/* Can’t run without these. */
 }
 LoadResource(settings);
 HNoPurge(settings);
 SetControlStrings();
 SPopen();
      HPurge(settings);
      settings = nil;
 prpb.ioNamePtr = prname;
 break;

     case PRIME: /* For read/write calls, not  here. */
      break;

     case CONTROL: /*  control  */
 switch (p->csCode){ /* Device Control Call */
 /* p->csCode gives opcode, and we switch on */
 /* it to perform low-level Printing calls */
 case iPrBitsCtl:/* Send bitmap to printer (NA). */
     break;
 case iPrIOCtl:  /* Text streaming. */
     iopb.ioParam.ioBuffer = (Ptr)p->lParam1;
     iopb.ioParam.ioReqCount = p->lParam2;
     asm{
      lea iopb,a0
      PBWrite
     }
     break;
 case iPrEvtCtl: /* Screen print (cmd-shift 4, NA.)*/
     break;
 case iPrDevCtl: /* CR, LF, FF, etc. */
     devicecontrol(p->lParam1);
     break;
 case iFMgrCtl:
/* Font manager font table modify request.  p->lParam1 is a pointer to 
an FMInput record.  (IM says we get an FMOutPtr here, but MacsBug says 
we get a pointer to an FMInput.  I go with MacsBug.)  In addition, the 
integer following p->lParam1 contains our driver reference number and 
a private byte we can use internally. The application first does something 
to determine the characteristics of fonts in our printing GrafPort.  
The Font Manager calls our status routine with csParam = 8 and we give 
it a “font characterization table”. The Font Manager selects a font, 
and calls our control routine to confirm the choice.  We change it to 
Monaco 9, horizontal scaling.  If you need to support proportional fonts, 
then you have to do something about it here and in the printing code, 
too.  Good luck.  */
     {
     FMInput *fontset;
        fontset = (FMInput *)p->lParam1;
        fontset->family = monaco;
        fontset->face = 0;
        fontset->needBits = 0;
        fontset->numer = noscale;
        fontset->denom = noscale;
        if(fontset->device == IDEV10){
        fontset->size = 10;
        fontset->numer.h = 7;
        fontset->denom.h = 6;
        }
        else if(fontset->device == IDEV15){
        fontset->size = 7;
        fontset->numer.h = 5;
        fontset->denom.h = 6;
        }
        else fontset->size = 9;
     }
     break;
 default:
     break;
 }
 break;
     case STATUS:
 switch (p->csCode){ /* Device Status Call */
 /* p->csCode gives opcode, and we switch on */
 /* it to perform low-level Printing calls */
 case iFMgrCtl:

/* Font manager font table request.  Help out the Font Manager. p->lParam1 
is a pointer to an area in memory in which to put a copy of the information 
describing our font capabilities.  p->lParam2 has an integer in its high 
order word which contains the “resolution” to use, set in a style dialog 
with the user.  We say we have different horizontal resolutions, based 
on the number of characters per inch used.  I believe this gets called 
whenever the application does a GetFontInfo() or StringWidth() or whatever 
for the first time in one of our printing GrafPorts.  Possibly also when 
the application tries to change the font style.  */

     *((fontab *)p->lParam1) = myfonts;
     break;
 default:
     break;
 }
 break;
     case CLOSE: /*  close  */
      if(storage != nil){
      HPurge(storage);
      d->dCtlStorage = nil;
      }
 break;
 }
 
 /*  done  */
 
 return(0);
}
SPopen()/* Open serial driver and configure it. */
 /* Use low-level ROM routines. */
{
ParmBlkPtrpb;
intserconfig;
 pb = &iopb;
 switch (pport){ /* get the correct port */
 case 0:/* modem port */
 if(!phone) {
 prinit(“\p.AOut”);
 phone = TRUE;
 }
 drivernum = AoutRefNum;
 break;
 case 1:/* printer port */
 if(!printer) {
 prinit(“\p.BOut”);
 printer = TRUE;
 }
 drivernum = BoutRefNum;
 break;
 }
 /* set up the io parameter block for writing to the serial driver. A 
control call resets the baud rate */
 pb->ioParam.ioRefNum = drivernum;
 pb->ioParam.ioCompletion = nil;
 ((cntrlParam *)pb)->csCode = SERRESET;
 serconfig = data8 + noParity + stop20;
 serconfig += mybauds[pbaud].rate;
 ((cntrlParam*)pb)->csParam[0] = serconfig;
 asm{
      move.lpb,a0
      PBControl
 }
#define shake  ((SerShk*)&((cntrlParam*)pb)->csParam[0])
 shake->errs = FALSE;
 shake->evts = FALSE;
 shake->fDTR = FALSE;
 shake->fInX = FALSE;
 if(XonXoff && (theWorld.machineType >= envMachUnknown)){
      shake->fXOn = TRUE;
      shake->fCTS = FALSE;
      shake->xOn = XONCR;
      shake->xOff = XOFFCR;
 }
 else {
      shake->fXOn = FALSE;
      shake->fCTS = TRUE;
 }
 ((cntrlParam *)pb)->csCode = SERSHAKE;
 asm{
      move.lpb,a0
      PBControl
 }
 
#undef shake
 pb->ioParam.ioPosMode = 0;
 pb->ioParam.ioPosOffset = 0;
}

prinit(name) /* Open a driver by name. */
StringPtr name;  /* ROM, RAM serial driver? Who cares? */
{
 iopb.ioParam.ioNamePtr = name;
 iopb.ioParam.ioCompletion = nil;
 iopb.ioParam.ioPermssn = 0;
 asm{
 lea  iopb,a0
 PBOpen
 }
}
/*
 * Set printer control strings based on our STR# resource.
 */
SetControlStrings()
{
unsigned char  *strptr;
unsigned char  **mystrings;

mystrings = (unsigned char **)GetResource(‘STR#’,PACKID);
 if(mystrings != nil){
 LoadResource(mystrings);
 copystr(  (strptr = (*mystrings)+2)  , prlfstr);
 copystr(  (strptr += (*strptr) + 1)  , prinitstr);
 copystr(  (strptr += (*strptr) + 1)  , prtopstr);
 copystr(  (strptr += (*strptr) + 1)  , preopstr);
 copystr(  (strptr += (*strptr) + 1)  , preofstr);
 decode(prlfstr);
 decode(prinitstr);
 decode(prtopstr);
 decode(preopstr);
 decode(preofstr);
 }
}
decode(str)
unsigned char *str;
{
register unsigned char i,count;
 count = 1;
 for(i=0;str[0]-i++;){
 if (str[i] == ‘^’) str[count] = 31 & str[++i];
 else str[count] = str[i];
 count++;
 }
 str[0] = count - 1;
}
/*
 * This is for copying PASCAL strings.  Simple.
 */
copystr(src,dst)
unsigned char *src,*dst;
{
 asm{
 clr.l  d0
 move.l src,a0
 move.l dst,a1
 move.b (a0),d0
 loop:
 move.b (a0)+,(a1)+
 dbra d0,@loop
 }
}
devicecontrol(code)/* printer device control calls. */
long  code;
{
 switch(code){
 case lPrReset:
 printstring(prinitstr);
 break;
 case lPrPageEnd:
 printstring(preopstr);
 break;
 case lPrLineFeed:
 case lPrLFSixth:
 case lPrLFEighth:
 printstring(prlfstr);
 break;
 default: /* Unknown parameter. */
 break;
 }
}
printstring(string)/* printer control string to serial driver. */
unsigned char  *string;   
{
 iopb.ioParam.ioBuffer = (Ptr)(string+1);
 iopb.ioParam.ioReqCount = (long)(string[0]);
 asm{
 lea  iopb,a0
 PBWrite
 }
}
/* Find the system folder, and open our printer resource file, if we 
can. Fill in a global SysEnvRec, which might be useful for other stuff. 
 */
int openprinterfile(d)
DCtlPtr d;
{
SysEnvRec myWorld;
StringHandle   ourname;
inttherefnum;
Handle  storage;

 if(d->dCtlStorage != nil) 
 HPurge(d->dCtlStorage);
 ourname = (StringHandle)GetResource(‘STR ‘,0xE000);
 LoadResource(ourname);
 HNoPurge(ourname);/* Get resource file name. */
 HLock(ourname);
 SysEnvirons(1,&myWorld);
 if(myWorld.machineType >= envMachUnknown)
 therefnum = OpenRFPerm(*ourname, myWorld.sysVRefNum, fsCurPerm);
 else
 therefnum = OpenResFile(*ourname);
 HPurge(ourname);
 if (therefnum != -1){
 d->dCtlStorage = (Handle)GetResource(‘PREC’,RESFILEID);
 if(d->dCtlStorage != nil){
 LoadResource(d->dCtlStorage);
 DetachResource(d->dCtlStorage);
 HNoPurge(d->dCtlStorage);
 HLock(d->dCtlStorage);
 storage = d->dCtlStorage;
 asm{
 movea.l storage,a0;; LightspeedC uses A4
 movea.l (a0),a4 ;; for driver globals.
 }
 theWorld = myWorld;
 }
 }
 return (therefnum);
}
int HNoPurge(c)
char *c;
{
 asm{
 move.l c,a0
 HNoPurge
 }
}
int HPurge(c)
char *c;
{
 asm{
 move.l c,a0
 HPurge
 }
}
int HLock(c)
char *c;
{
 asm{
 move.l c,a0
 HLock
 }
}
intCloseDriver(refnum)
intrefnum;
{
ParamBlockRec  closepb;
 asm{
 lea  closepb,a0
 move.w refnum,24(a0)
 clr.l  12(a0)
 PBClose
 move.w 16(a0),d0
 }
}

/* prglobals.h */
#define nil 0L
#include <PrintMgr.h>
#include <DeviceMgr.h>
#include <FileMgr.h>
#include <Environs.h>
#define DIRTY  0xFFFF
/* LightspeedC doesn’t do this exactly right for a Printer control call. 
*/
typedef struct
 {
 QElemPtr qLink;
 int    qType;
 int    ioTrap;
 Ptr    ioCmdAddr;
 ProcPtrioCompletion;
 OsErr  ioResult;
 StringPtrioNamePtr;
 int    ioVRefNum;
 int    ioRefNum;
 int    csCode;
 long   lParam1;
 long   lParam2;
 long   lParam3;
/* ^^^^ */
} PrParam;
#define WIDTH  164 /* la bombe if not a multiple of 4 */
#define NROWS  66/* 11 inches * lines per inch. */
typedef struct bauds{
 int  rate;
 char label[10];
}BAUDS;
typedef struct{
 int  dirty;
 unsigned char text[WIDTH];
}line,*pline,**hline;
typedef struct{
 int  dummy[5];
} pconfig,*Pcfg,**Pfg;
/* Be a real Mac cowboy, access data by the handle. */
#define pport    ((*settings)->dummy[0])
#define pbaud    ((*settings)->dummy[1])
#define XonXoff  ((*settings)->dummy[2])
/* Yeeeeee-Hah !! I don’t even lock it! */
typedef struct { /* Structure description of STR# */
 int  numstrings;/* resource. */
 unsigned char thestrings[];
}stringlist,**StrList;
#define RES1ID (-4080)
#define RES2ID (-8192)
#define ILLEGAL  0x4AFC
typedef unsigned char Str36[36];
#define HREZZ  72
#define HREZZ10  60/* For 10 cpi. */
#define HREZZ12  72/* For 12 cpi. */
#define HREZZ15  90/* For 15 cpi. */
#define VREZZ  66  /* 6 lines of printed text per inch. */
#define lines_per_inch  6
#define CHARWIDTH6
#define CHARHEIGHT 11

#define iPrPrivate 99

#define VERSION  3

#define _DEBUG 0
#if_DEBUG
#define DEBUG  asm{\
 Debugger\
}
#else
#define DEBUG
#endif
#define IDEV10 0xFD01
#define IDEV12 0xFD02
#define IDEV15 0xFD03
/* HIgh order byte of IDEV is -3 for printer driver, low order byte is 
device specific.  I use it to determine character pitch for daisy wheel 
devices and dot matrix in draft mode. */
#define SETUP_CMD‘Setp’
#define PRINT_CMD‘Prnt’
#define CLOSE_CMD‘Clos’
#define OPEN_CMD ‘Open’

typedef struct{  /* Font characterization table  */
 int    vres;    /* for use by the Font Manager. */
 int    hres;
 SignedByte bold[3];
 SignedByte italic[3];
 SignedByte notused[3];
 SignedByte outline[3];
 SignedByte shadow[3];
 SignedByte condensed[3];
 SignedByte extended[3];
 SignedByte underline[3];
}fontab;

/*
 * Shared storage, defined and initialized by the driver code, accessed 
elsewhere. */
#ifdef DRIVER_MODULE
#else
typedef struct{
#endif
#ifdef DRIVER_MODULE
BAUDS mybauds[] = {/* Baud rate constant table. */
{ baud300,“\p300”},       /* Should use a string list. */
{ baud600,“\p600”},/* But these are numbers, so I */
{ baud1200, “\p1200”},  /* Think it’s OK. */
{ baud1800, “\p1800”},  /* This has to be the FIRST */
{ baud2400, “\p2400”}, /* driver global to be declared, */
{ baud3600, “\p3600”},  /* since it is also used by our */
{ baud4800, “\p4800”},  /* PACK -4096 resource, which  */
{ baud7200, “\p7200”},  /* needs to find it at the head */
{ baud9600, “\p9600”},  /* of PREC -8192. */
{ baud19200,“\p19200”}
}; 
Pfgsettings = nil;
static  PrParam prpb = {
 nil,   /* qLink */
 0,/* qType */
 0,/* ioTrap   */
 nil,   /* ioCmdAddr */
 nil,   /* ioCompletion */
 0,/* ioResult   */
 nil,   /* ioNamePtr */
 0,/* ioVRefNum  */
 iPrDrvrRef,/* ioRefNum   */
 0,/* csCode   */
 0L,0L,0L /* lParam[1-3]  */
};
Str36 prname = sPrDrvr;
#else
 BAUDS  mybauds[10];
 Pfg  settings;
 PrParamprpb;
Str36 prname;
#endif
 ParamBlockRec   iopb;  /* For writing to serial driver. */
#ifdef DRIVER_MODULE
 static int phone   =     FALSE;
 static int printer =   FALSE;
#else
 int phone;
 int printer;
#endif
 int  drivernum; /* RefNum of serial driver to use. */
 Str255 prinitstr;
 Str36  prlfstr;
 Str36  prtopstr;
 Str36  preopstr;
 Str36  preofstr;
#ifdef DRIVER_MODULE
static fontab  myfonts = { 
 VREZZ,
 HREZZ12,
 { 0, 2,2},
 { 1, 8,2},
 { 0, 0,0},
 { 5, 1,2},
 { 5, 2,4},
 { 0, 0,-2},
 { 0, 0,2},
 { 1, 3,2}
};
static Point noscale = {0x0001,0x0001};
#else
 fontab myfonts;
 int  MyRes[3];
 Point  noscale;
#endif
 Str255 stringbuf;
 int  topmargin,nlines;
 TPrint Print;
 int  pagenum;
 SysEnvRec theWorld;
#ifdef DRIVER_MODULE
#else
}Dstorage,*DPstorage,**DHstorage;
#endif
#define SHEETDIALOG(-8193)
#define DONEITEM 1
#define STOPITEM 2



*Style and job dialogs for Daisy printer driver
*Chooser interface dialog
*Settings, strings, resource file BNDL, ICN#, and FREF

src:Printer:dialogs.rsrc
????????

Type DLOG
,-8191
Job
50 20 136 493
Invisible NoGoAway
1 ;; procID
1 ;; refCon
-8191

Type DITL
,-8191
14

BtnItem ;; 1
8 321 28 381
OK

BtnItem ;; 2
9 395 29 455
Cancel

StatText Disabled;; 3
9 6 24 145
Daisy

StatText Disabled;; 4
30 5 46 89
Page Range:

RadioItem ;; 5
30 93 45 138
All

RadioItem ;; 6
30 140 46 200
From:

EditText Disabled;; 7
30 205 46 237


StatText Disabled;; 8
30 242 46 270
To:

EditText Disabled;; 9
30 272 46 304


StatText Disabled;; 10
55 5 71 89
Copies:

EditText Disabled;; 11
55 95 71 127
1

StatText Disabled;; 12
55 145 71 200
Feed:

RadioItem ;; 13
55 205 70 303
Continuous

RadioItem ;; 14
55 305 71 425
Sheet Feed

Type DITL
Printer Dialog Template,-4080
21

*   1
BtnItem Enabled
224 103 240 173
Save

*   2
StatText Enabled
149 7 165 120
Bottom of page

*   3
StatText Disabled
6 91 23 219
Printer Port Setup

*   4
StatText Disabled
30 6 46 51
Port:

*   5
RadioItem Enabled
30 57 46 157
Modem Port

*   6
RadioItem Enabled
30 165 46 265
Printer Port

*   7
StatText Disabled
57 6 73 81
Baud Rate:

*   8
BtnItem Enabled
57 98 73 143
runtime

*   9
StatText Disabled
58 153 74 300
(Hit me for more )

*   10
StatText Disabled
83 5 99 122
Line Terminator

*   11
EditText Enabled
83 128 99 320
runtime

*   12
EditText Enabled
105 128 121 320
runtime

*   13
EditText Enabled
127 128 143 320
runtime

*   14
EditText Enabled
149 128 165 320
runtime

*   15
EditText Enabled
171 128 187 320
runtime

*   16
StatText Disabled
105 6 121 123
Initialize

*   17
StatText Disabled
127 7 143 124
Top of page

*   18
StatText Enabled
171 8 187 121
End of file

*    19
RadioItem Enabled 
197 8 213 121
CTS

*   20
RadioItem Enabled
197 165 213 265
XOn/XOff

*   21
BtnItem Enabled
224 183 240 253
Cancel

Type DLOG
Printer Config Box,-4080
Serial Port Configuration
40 80 296 424
Invisible NoGoAway
1 ;; procID
0 ;; refCon
-4080

Type STR 
Right Button,-4092 ;; Chooser right button title
Setup 

Type STR 
List label,-4091 ;; Chooser list label
Serial Printer Driver

Type Stng = GNRL
Printer Settings,-8192
.H
0001 0002 0000 0000 ;; Printer port, 1200 baud, no Xon/Xoff

Type STR# ;; String list for EOL, EOP, BOP strings
Printer control strings,-4080 ;; Tandy DMP-110
5
^M ;; End of line
^[^W    ;; Initialize printer
 ;; beginning of page
^L ;; end of page
 ;; end of document

Type BNDL
,128
DasY 0
ICN#
0 128
FREF
0 128

Type DasY = STR 
,0
Version 0.000001

Type FREF
,128
PRER 0 

Type ICN# = GNRL
ICN#,128
.H
00000000 7FFFFFFE 40000002 40000002
40003802 40004582 4001C642 40024442
40042842 40021F82 4003FE42 40043D42
40025C82 40019442 40009242 40009382
40387202 403C4C02 401C8002 40038002
4001C002 4002F002 40047002 40087002
40080002 43FFC002 42004002 41FF8002
40810002 40420002 7FFFFFFE 00000000
00000000 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 7FFFFFFE
7FFFFFFE 7FFFFFFE 7FFFFFFE 00000000
* Printer style dialog template
Type DITL
,-8192
8

BtnItem ;;1
10 319 30 379
OK

BtnItem ;;2
10 390 30 450
Cancel

StatText Disabled;;3
10 5 30 308
Daisy: 8.5" by 11"

RadioItem Enabled;;4
70 5 90 100
10 cpi

RadioItem Enabled;;5
70 120 90 180
12 cpi

RadioItem Enabled;;6
70 200 90 260
15 cpi

RadioItem Enabled;;7
40 5 60 150
Straight up

RadioItem Enabled;;8
40 155 60 300
Sideways

* Style dialog box
Type DLOG
,-8192
Stl
31 20 120 488
Invisible NoGoAway
1 ;; procID
1 ;; refCon
-8192

Type DLOG
Next Page Box,-8193
Next Page
48 51 100 300
Visible NoGoAway
1 ;; procID
0 ;; refCon
-8193

Type DITL
Next Page Template,-8193
3

*   1
BtnItem Enabled
30 60 46 140
Done

*   2
BtnItem Enabled
30 160 46 240
Stop

*   3
StatText
8 8 24 292
Daisy: Insert Next Sheet
 
AAPL
$101.32
Apple Inc.
+0.74
MSFT
$45.15
Microsoft Corpora
-0.07
GOOG
$582.56
Google Inc.
-0.81

MacTech Search:
Community Search:

Software Updates via MacUpdate

calibre 2.0.0 - Complete e-library manag...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Apple iMovie 10.0.5 - Edit personal vide...
With an all-new design, Apple iMovie lets you enjoy your videos like never before. Browse your clips more easily, instantly share your favorite moments, and create beautiful HD movies and Hollywood-... Read more
Apple Keynote 6.2.2 - Apple's prese...
Apple Keynote makes it simple to create and deliver beautiful presentations. Powerful tools and dazzling effects bring your ideas to life. You can work seamlessly between Mac and iOS devices. And... Read more
Apple Numbers 3.2.2 - Apple's sprea...
With Apple Numbers, sophisticated spreadsheets are just the start. The whole sheet is your canvas. Just add dramatic interactive charts, tables, and images that paint a revealing picture of your data... Read more
OpenOffice 4.1.1 - Free and open-source...
OpenOffice.org is both an Open Source product and a project. The product is a multi-platform office productivity suite. It includes the key desktop applications, such as a word processor,... Read more
Pages 5.2.2 - Apple's word processo...
Apple Pages is a powerful word processor that gives you everything you need to create documents that look beautiful. And read beautifully. It lets you work seamlessly between Mac and iOS devices. And... Read more
Quicken 2015 2.0.1 - Complete personal f...
The new Quicken 2015 helps you manage all your personal finances in one place, so you can see where you're spending and where you can save. Quicken automatically categorizes your financial... Read more
CleanMyMac 2.2.7 - Delete files that was...
CleanMyMac makes space for the things you love. Sporting a range of ingenious new features, CleanMyMac 2 lets you safely and intelligently scan and clean your entire system, delete large, unused... Read more
MacFamilyTree 7.2.4 - Create and explore...
MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more
Videobox 4.1.1 - Download Flash video th...
Videobox allows you to quickly and easily download Flash video from most all of the popular video sites on the internet. Videobox will convert the video into a native Quicktime format so it's ready... Read more

Latest Forum Discussions

See All

Trolls vs Vikings Update Adds Over One H...
Trolls vs Vikings Update Adds Over One Hundred Levels, Reduces Item Cost, and More Posted by Ellis Spice on August 22nd, 2014 [ permalink ] | Read more »
SNK Celebrates the 20th Anniversary of T...
SNK Celebrates the 20th Anniversary of The King of Fighters With a Big Sale Posted by Ellis Spice on August 22nd, 2014 [ permalink ] | Read more »
It Came From Canada: Star Wars: Commande...
With a brand new Star Wars trilogy on the horizon, prepare yourselves for Disney and George Lucas’s space fantasy throwback to be more omnipresent than ever before. So it should come as no surprise that new adventures in that galaxy far, far away... | Read more »
Swing Copters Review
Swing Copters Review By Jordan Minor on August 22nd, 2014 Our Rating: :: DIE TRYINGUniversal App - Designed for iPhone and iPad The creator of Flappy Bird is back with a vengeance.   | Read more »
The Outcast Review
The Outcast Review By Nadia Oxford on August 22nd, 2014 Our Rating: :: HANDS OFF. WAY OFF.Universal App - Designed for iPhone and iPad It’s easy to see what The Outcast is trying for, but its execution needs a lot of work.   | Read more »
Brain+ Review
Brain+ Review By Nadia Oxford on August 22nd, 2014 Our Rating: :: DIM BULBUniversal App - Designed for iPhone and iPad Brain+ is just another entry in an over-saturated brain-training marketplace – and not a particularly fun entry... | Read more »
The Witcher Battle Arena – New Gameplay...
The Witcher Battle Arena – New Gameplay Trailer Revealed Posted by Jessica Fisher on August 22nd, 2014 [ permalink ] Based in the Witcher universe, | Read more »
Max Gentlemen Review
Max Gentlemen Review By Jennifer Allen on August 22nd, 2014 Our Rating: :: OUTSTAYING ITS WELCOMEiPhone App - Designed for the iPhone, compatible with the iPad Max Gentlemen seems pretty quirky initially but that appeal wears thin... | Read more »
Club Penguin Gets Frozen All Over in Lat...
Club Penguin Gets Frozen All Over in Latest Takeover Event Posted by Ellis Spice on August 22nd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Doctor Who: Legacy Review
Doctor Who: Legacy Review By Jennifer Allen on August 22nd, 2014 Our Rating: :: RUN OF THE MILLUniversal App - Designed for iPhone and iPad Doctor Who: Legacy is an ok match-3 puzzle game, but it lacks some much needed personality... | Read more »

Price Scanner via MacPrices.net

Leftover 15-inch 2.0GHz Retina MacBook Pros a...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Pro.Calendar – New Productivity App for iPad...
Austin, Texas based mobile business and productivity app developer LightArrow, Inc. has announced Pro.Calendar, a powerful and intuitive calendar app with eight versatile calendar options including... Read more
SanDisk Ultra II SSD — Supercharge Your Syste...
SanDisk Corporation has announced the new SanDisk Ultra II SSD with enhanced SSD Dashboard. The new drive is designed to deliver a cost-effective and easy upgrade solution for PC owners looking to... Read more
Samsung and Barnes & Noble Introduce New...
Samsung Electronics America and NOOK Media, a subsidiary of Barnes & Noble, Inc. have announced the introduction of the new Samsung Galaxy Tab 4 NOOK, a 7-inch tablet combining Samsung’s leading... Read more
21-inch iMacs on sale for up to $150 off MSRP
B&H Photo has 21″ iMacs on sale for up to $150 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 2.7GHz iMac: $... Read more
27-inch 3.2GHz iMac on sale for $1698, save $...
Abt has the 27″ 3.2GHz iMac on sale for $1698 including free shipping. Their price is $101 off MSRP. Read more
Mac Backup Guru 2.0 Drive Backup/Cloneing Uti...
Mac Backup Guru developer MacDaddy has released Mac Backup Guru 2.0, offering new and enhanced advanced features, such as bootable backups, synchronised volumes and folders, and a Snapshot mode that... Read more
Operate GE’s New Free-Standing KItchen Range...
Think you accidentally left the oven on? Switch it off while on the go. The new free-standing Profile™ Series gas and electric ranges are GE’s second cooking appliances, following their wall oven, to... Read more
Parallels Announces Parallels Desktop 10 for...
The no. 1-selling software for running Windows applications on a Mac becomes an even easier choice for millions of consumers and IT professionals worldwide with the launch of the most powerful... Read more
Apple now offering certified refurbished 2014...
 The Apple Store is now offering Apple Certified Refurbished 2014 MacBook Airs for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** As more and more people discover Apple , they visit our stores seeking ways to incorporate our products into their lives. It's your job, as a Store Read more
Position Opening at *Apple* - Apple (United...
…Summary** As a Specialist, you help create the energy and excitement around Apple products, providing the right solutions and getting products into customers' hands. You Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.