/* * 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 #include #include #include // BeOS API #include #include #include #include #include #include #include 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::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(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(); } }