588 lines
13 KiB
C++
588 lines
13 KiB
C++
/*
|
|
* Copyright 2001-2010, Haiku, Inc. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Ithamar R. Adema
|
|
* Michael Pfeiffer
|
|
*/
|
|
#include "Printer.h"
|
|
|
|
#include "BeUtils.h"
|
|
#include "pr_server.h"
|
|
#include "PrintAddOnServer.h"
|
|
#include "PrintServerApp.h"
|
|
|
|
// posix
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
// BeOS API
|
|
#include <Application.h>
|
|
#include <Autolock.h>
|
|
#include <Message.h>
|
|
#include <NodeMonitor.h>
|
|
#include <String.h>
|
|
#include <StorageKit.h>
|
|
#include <SupportDefs.h>
|
|
|
|
|
|
SpoolFolder::SpoolFolder(BLocker* locker, BLooper* looper,
|
|
const BDirectory& spoolDir)
|
|
: Folder(locker, looper, spoolDir)
|
|
{
|
|
}
|
|
|
|
|
|
// Notify print_server that there is a job file waiting for printing
|
|
void
|
|
SpoolFolder::Notify(Job* job, int kind)
|
|
{
|
|
if ((kind == kJobAdded || kind == kJobAttrChanged)
|
|
&& job->IsValid() && job->IsWaiting()) {
|
|
be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB);
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
BObjectList<Printer> Printer::sPrinters;
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// Find [static]
|
|
//
|
|
// Searches the static object list for a printer object with the
|
|
// specified name.
|
|
//
|
|
// Parameters:
|
|
// name - Printer definition name we're looking for.
|
|
//
|
|
// Returns:
|
|
// Pointer to Printer object, or NULL if not found.
|
|
// ---------------------------------------------------------------
|
|
Printer*
|
|
Printer::Find(const BString& name)
|
|
{
|
|
// Look in list to find printer definition
|
|
for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) {
|
|
if (name == sPrinters.ItemAt(idx)->Name())
|
|
return sPrinters.ItemAt(idx);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Printer*
|
|
Printer::Find(node_ref* node)
|
|
{
|
|
node_ref n;
|
|
// Look in list to find printer definition
|
|
for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) {
|
|
Printer* printer = sPrinters.ItemAt(idx);
|
|
printer->SpoolDir()->GetNodeRef(&n);
|
|
if (n == *node)
|
|
return printer;
|
|
}
|
|
|
|
// None found, so return NULL
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Printer*
|
|
Printer::At(int32 idx)
|
|
{
|
|
return sPrinters.ItemAt(idx);
|
|
}
|
|
|
|
|
|
void
|
|
Printer::Remove(Printer* printer)
|
|
{
|
|
sPrinters.RemoveItem(printer);
|
|
}
|
|
|
|
|
|
int32
|
|
Printer::CountPrinters()
|
|
{
|
|
return sPrinters.CountItems();
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// Printer [constructor]
|
|
//
|
|
// Initializes the printer object with data read from the
|
|
// attributes attached to the printer definition node.
|
|
//
|
|
// Parameters:
|
|
// node - Printer definition node for this printer.
|
|
//
|
|
// Returns:
|
|
// none.
|
|
// ---------------------------------------------------------------
|
|
Printer::Printer(const BDirectory* node, Resource* res)
|
|
: Inherited(B_EMPTY_STRING),
|
|
fPrinter(gLock, be_app, *node),
|
|
fResource(res),
|
|
fSinglePrintThread(res->NeedsLocking()),
|
|
fJob(NULL),
|
|
fProcessing(0),
|
|
fAbort(false)
|
|
{
|
|
BString name;
|
|
// Set our name to the name of the passed node
|
|
if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) == B_OK)
|
|
SetName(name.String());
|
|
|
|
// Add us to the global list of known printer definitions
|
|
sPrinters.AddItem(this);
|
|
|
|
ResetJobStatus();
|
|
}
|
|
|
|
|
|
Printer::~Printer()
|
|
{
|
|
((PrintServerApp*)be_app)->NotifyPrinterDeletion(this);
|
|
}
|
|
|
|
|
|
void
|
|
Printer::MessageReceived(BMessage* msg)
|
|
{
|
|
switch(msg->what) {
|
|
case B_GET_PROPERTY:
|
|
case B_SET_PROPERTY:
|
|
case B_CREATE_PROPERTY:
|
|
case B_DELETE_PROPERTY:
|
|
case B_COUNT_PROPERTIES:
|
|
case B_EXECUTE_PROPERTY:
|
|
HandleScriptingCommand(msg);
|
|
break;
|
|
|
|
default:
|
|
Inherited::MessageReceived(msg);
|
|
}
|
|
}
|
|
|
|
|
|
// Remove printer spooler directory
|
|
status_t
|
|
Printer::Remove()
|
|
{
|
|
status_t rc = B_OK;
|
|
BPath path;
|
|
|
|
if ((rc = ::find_directory(B_USER_PRINTERS_DIRECTORY, &path)) == B_OK) {
|
|
path.Append(Name());
|
|
rc = rmdir(path.Path());
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
status_t
|
|
Printer::FindPathToDriver(const char* driverName, BPath* path)
|
|
{
|
|
return PrintAddOnServer::FindPathToDriver(driverName, path);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// ConfigurePrinter
|
|
//
|
|
// Handles calling the printer addon's add_printer function.
|
|
//
|
|
// Parameters:
|
|
// driverName - the name of the printer driver add-on
|
|
// printerName - the name of the printer spool folder
|
|
//
|
|
// Returns:
|
|
// B_OK if successful or errorcode otherwise.
|
|
// ---------------------------------------------------------------
|
|
status_t
|
|
Printer::ConfigurePrinter(const char* driverName,
|
|
const char* printerName)
|
|
{
|
|
PrintAddOnServer addOn(driverName);
|
|
return addOn.AddPrinter(printerName);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// ConfigurePage
|
|
//
|
|
// Handles calling the printer addon's config_page function.
|
|
//
|
|
// Parameters:
|
|
// settings - Page settings to display. The contents of this
|
|
// message will be replaced with the new settings
|
|
// if the function returns success.
|
|
//
|
|
// Returns:
|
|
// B_OK if successful or errorcode otherwise.
|
|
// ---------------------------------------------------------------
|
|
status_t
|
|
Printer::ConfigurePage(BMessage& settings)
|
|
{
|
|
BString driver;
|
|
status_t result = GetDriverName(&driver);
|
|
if (result != B_OK)
|
|
return result;
|
|
|
|
PrintAddOnServer addOn(driver.String());
|
|
result = addOn.ConfigPage(SpoolDir(), &settings);
|
|
if (result == B_OK) {
|
|
AddCurrentPrinter(settings);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// ConfigureJob
|
|
//
|
|
// Handles calling the printer addon's config_job function.
|
|
//
|
|
// Parameters:
|
|
// settings - Job settings to display. The contents of this
|
|
// message will be replaced with the new settings
|
|
// if the function returns success.
|
|
//
|
|
// Returns:
|
|
// B_OK if successful or errorcode otherwise.
|
|
// ---------------------------------------------------------------
|
|
status_t
|
|
Printer::ConfigureJob(BMessage& settings)
|
|
{
|
|
BString driver;
|
|
status_t result = GetDriverName(&driver);
|
|
if (result != B_OK)
|
|
return result;
|
|
|
|
PrintAddOnServer addOn(driver.String());
|
|
result = addOn.ConfigJob(SpoolDir(), &settings);
|
|
if (result == B_OK)
|
|
AddCurrentPrinter(settings);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// HandleSpooledJobs
|
|
//
|
|
// Print spooled jobs in a new thread.
|
|
// ---------------------------------------------------------------
|
|
void
|
|
Printer::HandleSpooledJob()
|
|
{
|
|
BAutolock lock(gLock);
|
|
if (lock.IsLocked()
|
|
&& (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) {
|
|
StartPrintThread();
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// GetDefaultSettings
|
|
//
|
|
// Retrieve the default configuration message from printer add-on
|
|
//
|
|
// Parameters:
|
|
// settings, output paramter.
|
|
//
|
|
// Returns:
|
|
// B_OK if successful or errorcode otherwise.
|
|
// ---------------------------------------------------------------
|
|
status_t
|
|
Printer::GetDefaultSettings(BMessage& settings)
|
|
{
|
|
BString driver;
|
|
status_t result = GetDriverName(&driver);
|
|
if (result != B_OK)
|
|
return result;
|
|
|
|
PrintAddOnServer addOn(driver.String());
|
|
result = addOn.DefaultSettings(SpoolDir(), &settings);
|
|
if (result == B_OK)
|
|
AddCurrentPrinter(settings);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void
|
|
Printer::AbortPrintThread()
|
|
{
|
|
fAbort = true;
|
|
}
|
|
|
|
|
|
status_t
|
|
Printer::GetDriverName(BString* name)
|
|
{
|
|
return SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_DRV_NAME, name);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// AddCurrentPrinter
|
|
//
|
|
// Add printer name to message.
|
|
//
|
|
// Parameters:
|
|
// msg - message.
|
|
// ---------------------------------------------------------------
|
|
void
|
|
Printer::AddCurrentPrinter(BMessage& message)
|
|
{
|
|
BString name;
|
|
GetName(name);
|
|
|
|
message.RemoveName(PSRV_FIELD_CURRENT_PRINTER);
|
|
message.AddString(PSRV_FIELD_CURRENT_PRINTER, name.String());
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// GetName
|
|
//
|
|
// Get the name from spool directory.
|
|
//
|
|
// Parameters:
|
|
// name - the name of the printer.
|
|
// ---------------------------------------------------------------
|
|
void
|
|
Printer::GetName(BString& name)
|
|
{
|
|
if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) != B_OK)
|
|
name = "Unknown Printer";
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// ResetJobStatus
|
|
//
|
|
// Reset status of "processing" jobs to "waiting" at print_server start.
|
|
// ---------------------------------------------------------------
|
|
void
|
|
Printer::ResetJobStatus()
|
|
{
|
|
if (fPrinter.Lock()) {
|
|
const int32 n = fPrinter.CountJobs();
|
|
for (int32 i = 0; i < n; i ++) {
|
|
Job* job = fPrinter.JobAt(i);
|
|
if (job->Status() == kProcessing)
|
|
job->SetStatus(kWaiting);
|
|
}
|
|
fPrinter.Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// HasCurrentPrinter
|
|
//
|
|
// Try to read the printer name from job file.
|
|
//
|
|
// Parameters:
|
|
// name - the printer name.
|
|
//
|
|
// Returns:
|
|
// true if successful.
|
|
// ---------------------------------------------------------------
|
|
bool
|
|
Printer::HasCurrentPrinter(BString& name)
|
|
{
|
|
BMessage settings;
|
|
// read settings from spool file and get printer name
|
|
BFile jobFile(&fJob->EntryRef(), B_READ_WRITE);
|
|
return jobFile.InitCheck() == B_OK
|
|
&& jobFile.Seek(sizeof(print_file_header), SEEK_SET) == sizeof(print_file_header)
|
|
&& settings.Unflatten(&jobFile) == B_OK
|
|
&& settings.FindString(PSRV_FIELD_CURRENT_PRINTER, &name) == B_OK;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// MoveJob
|
|
//
|
|
// Try to move job to another printer folder.
|
|
//
|
|
// Parameters:
|
|
// name - the printer folder name.
|
|
//
|
|
// Returns:
|
|
// true if successful.
|
|
// ---------------------------------------------------------------
|
|
bool
|
|
Printer::MoveJob(const BString& name)
|
|
{
|
|
BPath file(&fJob->EntryRef());
|
|
BPath path;
|
|
file.GetParent(&path);
|
|
path.Append("..");
|
|
path.Append(name.String());
|
|
BDirectory dir(path.Path());
|
|
BEntry entry(&fJob->EntryRef());
|
|
// try to move job file to proper directory
|
|
return entry.MoveTo(&dir) == B_OK;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// FindSpooledJob
|
|
//
|
|
// Looks if there is a job waiting to be processed and moves
|
|
// jobs to the proper printer folder.
|
|
//
|
|
// Note:
|
|
// Our implementation of BPrintJob moves jobs to the
|
|
// proper printer folder.
|
|
//
|
|
//
|
|
// Returns:
|
|
// true if there is a job present in fJob.
|
|
// ---------------------------------------------------------------
|
|
bool
|
|
Printer::FindSpooledJob()
|
|
{
|
|
BString name2;
|
|
GetName(name2);
|
|
do {
|
|
fJob = fPrinter.GetNextJob();
|
|
if (fJob) {
|
|
BString name;
|
|
if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) {
|
|
// job in wrong printer folder moved to apropriate one
|
|
fJob->SetStatus(kUnknown, false); // so that fPrinter.GetNextJob skips it
|
|
fJob->Release();
|
|
} else {
|
|
// job found
|
|
fJob->SetPrinter(this);
|
|
return true;
|
|
}
|
|
}
|
|
} while (fJob != NULL);
|
|
return false;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// PrintSpooledJob
|
|
//
|
|
// Loads the printer add-on and calls its take_job function with
|
|
// the spool file as argument.
|
|
//
|
|
// Parameters:
|
|
// spoolFile - the path to the spool file.
|
|
//
|
|
// Returns:
|
|
// B_OK if successful.
|
|
// ---------------------------------------------------------------
|
|
status_t
|
|
Printer::PrintSpooledJob(const char* spoolFile)
|
|
{
|
|
BString driver;
|
|
status_t result = GetDriverName(&driver);
|
|
if (result != B_OK)
|
|
return result;
|
|
|
|
PrintAddOnServer addOn(driver.String());
|
|
return addOn.TakeJob(spoolFile, SpoolDir());
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// PrintThread
|
|
//
|
|
// Loads the printer add-on and calls its take_job function with
|
|
// the spool file as argument.
|
|
//
|
|
// Parameters:
|
|
// job - the spool job.
|
|
// ---------------------------------------------------------------
|
|
void
|
|
Printer::PrintThread(Job* job)
|
|
{
|
|
// Wait until resource is available
|
|
fResource->Lock();
|
|
bool failed = true;
|
|
// Can we continue?
|
|
if (!fAbort) {
|
|
BPath path;
|
|
bool canOpenFile;
|
|
{
|
|
BEntry entry(&job->EntryRef());
|
|
path.SetTo(&entry);
|
|
BFile jobFile(path.Path(), B_READ_WRITE);
|
|
canOpenFile = jobFile.InitCheck() == B_OK;
|
|
}
|
|
// Tell the printer to print the spooled job
|
|
if (canOpenFile && PrintSpooledJob(path.Path()) == B_OK) {
|
|
// Remove spool file if printing was successful.
|
|
job->Remove(); failed = false;
|
|
}
|
|
}
|
|
// Set status of spooled job on error
|
|
if (failed)
|
|
job->SetStatus(kFailed);
|
|
fResource->Unlock();
|
|
job->Release();
|
|
atomic_add(&fProcessing, -1);
|
|
Release();
|
|
// Notify print_server to process next spooled job
|
|
be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// print_thread
|
|
//
|
|
// Print thread entry, calls PrintThread with spool job.
|
|
//
|
|
// Parameters:
|
|
// data - spool job.
|
|
//
|
|
// Returns:
|
|
// 0 always.
|
|
// ---------------------------------------------------------------
|
|
status_t
|
|
Printer::print_thread(void* data)
|
|
{
|
|
Job* job = static_cast<Job*>(data);
|
|
job->GetPrinter()->PrintThread(job);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------
|
|
// StartPrintThread
|
|
//
|
|
// Sets the status of the current spool job to "processing" and
|
|
// starts the print_thread.
|
|
// ---------------------------------------------------------------
|
|
void
|
|
Printer::StartPrintThread()
|
|
{
|
|
Acquire();
|
|
thread_id tid = spawn_thread(print_thread, "print", B_NORMAL_PRIORITY, (void*)fJob);
|
|
if (tid > 0) {
|
|
fJob->SetStatus(kProcessing);
|
|
atomic_add(&fProcessing, 1);
|
|
resume_thread(tid);
|
|
} else {
|
|
fJob->Release();
|
|
Release();
|
|
}
|
|
}
|
|
|