* 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"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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)
{
}
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;
Printer*
Printer::Find(const BString& name)
{
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;
for (int32 idx = 0; idx < sPrinters.CountItems(); idx++) {
Printer* printer = sPrinters.ItemAt(idx);
printer->SpoolDir()->GetNodeRef(&n);
if (n == *node)
return printer;
}
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::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;
if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) == B_OK)
SetName(name.String());
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);
}
}
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);
}
status_t
Printer::ConfigurePrinter(const char* driverName,
const char* printerName)
{
PrintAddOnServer addOn(driverName);
return addOn.AddPrinter(printerName);
}
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;
}
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;
}
void
Printer::HandleSpooledJob()
{
BAutolock lock(gLock);
if (lock.IsLocked()
&& (!fSinglePrintThread || fProcessing == 0) && FindSpooledJob()) {
StartPrintThread();
}
}
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);
}
void
Printer::AddCurrentPrinter(BMessage& message)
{
BString name;
GetName(name);
message.RemoveName(PSRV_FIELD_CURRENT_PRINTER);
message.AddString(PSRV_FIELD_CURRENT_PRINTER, name.String());
}
void
Printer::GetName(BString& name)
{
if (SpoolDir()->ReadAttrString(PSRV_PRINTER_ATTR_PRT_NAME, &name) != B_OK)
name = "Unknown Printer";
}
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();
}
}
bool
Printer::HasCurrentPrinter(BString& name)
{
BMessage settings;
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;
}
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());
return entry.MoveTo(&dir) == B_OK;
}
bool
Printer::FindSpooledJob()
{
BString name2;
GetName(name2);
do {
fJob = fPrinter.GetNextJob();
if (fJob) {
BString name;
if (HasCurrentPrinter(name) && name != name2 && MoveJob(name)) {
fJob->SetStatus(kUnknown, false);
fJob->Release();
} else {
fJob->SetPrinter(this);
return true;
}
}
} while (fJob != NULL);
return false;
}
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());
}
void
Printer::PrintThread(Job* job)
{
fResource->Lock();
bool failed = true;
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;
}
if (canOpenFile && PrintSpooledJob(path.Path()) == B_OK) {
job->Remove(); failed = false;
}
}
if (failed)
job->SetStatus(kFailed);
fResource->Unlock();
job->Release();
atomic_add(&fProcessing, -1);
Release();
be_app_messenger.SendMessage(PSRV_PRINT_SPOOLED_JOB);
}
status_t
Printer::print_thread(void* data)
{
Job* job = static_cast<Job*>(data);
job->GetPrinter()->PrintThread(job);
return 0;
}
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();
}
}