2 * Copyright 2001-2009, Haiku.
3 * Distributed under the terms of the MIT license.
7 * Stefano Ceccherini (burton666@libero.it)
9 * julun <host.haiku@gmx.de>
20 #include <Application.h>
25 #include <FindDirectory.h>
26 #include <Messenger.h>
32 #include <SystemCatalog.h>
35 #include <AutoDeleter.h>
36 #include <pr_server.h>
37 #include <ViewPrivate.h>
39 using BPrivate::gSystemCatalog
;
41 #undef B_TRANSLATION_CONTEXT
42 #define B_TRANSLATION_CONTEXT "PrintJob"
45 #define B_TRANSLATE(str) \
46 gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "PrintJob")
49 /*! Summary of spool file:
51 |-----------------------------------|
53 |-----------------------------------|
54 | BMessage print_job_settings |
55 |-----------------------------------|
57 | ********** (first page) ********* |
60 | * ----------------------------- * |
61 | * |---------------------------| * |
62 | * | BPoint where | * |
63 | * | BRect bounds | * |
64 | * | BPicture pic | * |
65 | * |---------------------------| * |
66 | * |---------------------------| * |
67 | * | BPoint where | * |
68 | * | BRect bounds | * |
69 | * | BPicture pic | * |
70 | * |---------------------------| * |
71 | ********************************* |
73 | ********* (second page) ********* |
76 | * ----------------------------- * |
77 | * |---------------------------| * |
78 | * | BPoint where | * |
79 | * | BRect bounds | * |
80 | * | BPicture pic | * |
81 | * |---------------------------| * |
82 | ********************************* |
83 |-----------------------------------|
85 BeOS R5 print_file_header.version is 1 << 16
86 BeOS R5 print_file_header.first_page is -1
88 each page can consist of a collection of picture structures
89 remaining pages start at _page_header_.next_page of previous _page_header_
91 See also: "How to Write a BeOS R5 Printer Driver" for description of spool
92 file format: http://haiku-os.org/documents/dev/how_to_write_a_printer_driver
96 struct _page_header_
{
97 int32 number_of_pictures
;
104 ShowError(const char* message
)
106 BAlert
* alert
= new BAlert(B_TRANSLATE("Error"), message
, B_TRANSLATE("OK"));
107 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
112 // #pragma mark -- PrintServerMessenger
118 class PrintServerMessenger
{
120 PrintServerMessenger(uint32 what
, BMessage
* input
);
121 ~PrintServerMessenger();
124 status_t
SendRequest();
126 void SetResult(BMessage
* result
);
127 BMessage
* Result() const { return fResult
; }
129 static status_t
GetPrintServerMessenger(BMessenger
& messenger
);
132 void RejectUserInput();
133 void AllowUserInput();
134 void DeleteSemaphore();
135 static status_t
MessengerThread(void* data
);
141 sem_id fThreadCompleted
;
142 BAlert
* fHiddenApplicationModalWindow
;
146 } // namespace BPrivate
149 using namespace BPrivate
;
152 // #pragma mark -- BPrintJob
155 BPrintJob::BPrintJob(const char* jobName
)
161 fDefaultSetupMessage(NULL
),
163 fCurrentPageHeader(NULL
)
165 memset(&fSpoolFileHeader
, 0, sizeof(print_file_header
));
167 if (jobName
!= NULL
&& jobName
[0])
168 fPrintJobName
= strdup(jobName
);
170 fCurrentPageHeader
= new _page_header_
;
171 if (fCurrentPageHeader
!= NULL
)
172 memset(fCurrentPageHeader
, 0, sizeof(_page_header_
));
176 BPrintJob::~BPrintJob()
181 delete fSetupMessage
;
182 delete fDefaultSetupMessage
;
183 delete fCurrentPageHeader
;
188 BPrintJob::ConfigPage()
190 PrintServerMessenger
messenger(PSRV_SHOW_PAGE_SETUP
, fSetupMessage
);
191 status_t status
= messenger
.SendRequest();
195 delete fSetupMessage
;
196 fSetupMessage
= messenger
.Result();
197 _HandlePageSetup(fSetupMessage
);
204 BPrintJob::ConfigJob()
206 PrintServerMessenger
messenger(PSRV_SHOW_PRINT_SETUP
, fSetupMessage
);
207 status_t status
= messenger
.SendRequest();
211 delete fSetupMessage
;
212 fSetupMessage
= messenger
.Result();
213 if (!_HandlePrintSetup(fSetupMessage
))
222 BPrintJob::BeginJob()
226 // can not start a new job until it has been commited or cancelled
227 if (fSpoolFile
!= NULL
|| fCurrentPageHeader
== NULL
)
230 // TODO show alert, setup message is required
231 if (fSetupMessage
== NULL
)
236 status_t status
= find_directory(B_USER_PRINTERS_DIRECTORY
, &path
);
240 char *printer
= _GetCurrentPrinterName();
243 MemoryDeleter
_(printer
);
245 path
.Append(printer
);
247 char mangledName
[B_FILE_NAME_LENGTH
];
248 _GetMangledName(mangledName
, B_FILE_NAME_LENGTH
);
250 path
.Append(mangledName
);
251 if (path
.InitCheck() != B_OK
)
254 // TODO: fSpoolFileName should store the name only (not path which can be
256 strlcpy(fSpoolFileName
, path
.Path(), sizeof(fSpoolFileName
));
257 fSpoolFile
= new BFile(fSpoolFileName
, B_READ_WRITE
| B_CREATE_FILE
);
259 if (fSpoolFile
->InitCheck() != B_OK
) {
264 // add print_file_header
265 // page_count is updated in CommitJob()
266 // on BeOS R5 the offset to the first page was always -1
267 fSpoolFileHeader
.version
= 1 << 16;
268 fSpoolFileHeader
.page_count
= 0;
269 fSpoolFileHeader
.first_page
= (off_t
)-1;
271 if (fSpoolFile
->Write(&fSpoolFileHeader
, sizeof(print_file_header
))
272 != sizeof(print_file_header
)) {
277 // add printer settings message
278 if (!fSetupMessage
->HasString(PSRV_FIELD_CURRENT_PRINTER
))
279 fSetupMessage
->AddString(PSRV_FIELD_CURRENT_PRINTER
, printer
);
291 BPrintJob::CommitJob()
293 if (fSpoolFile
== NULL
)
296 if (fSpoolFileHeader
.page_count
== 0) {
297 ShowError(B_TRANSLATE("No pages to print!"));
305 // write spool file header
306 fSpoolFile
->Seek(0, SEEK_SET
);
307 fSpoolFile
->Write(&fSpoolFileHeader
, sizeof(print_file_header
));
309 // set file attributes
311 be_app
->GetAppInfo(&appInfo
);
312 const char* printerName
= "";
313 fSetupMessage
->FindString(PSRV_FIELD_CURRENT_PRINTER
, &printerName
);
315 BNodeInfo
info(fSpoolFile
);
316 info
.SetType(PSRV_SPOOL_FILETYPE
);
318 fSpoolFile
->WriteAttr(PSRV_SPOOL_ATTR_PAGECOUNT
, B_INT32_TYPE
, 0,
319 &fSpoolFileHeader
.page_count
, sizeof(int32
));
320 fSpoolFile
->WriteAttr(PSRV_SPOOL_ATTR_DESCRIPTION
, B_STRING_TYPE
, 0,
321 fPrintJobName
, strlen(fPrintJobName
) + 1);
322 fSpoolFile
->WriteAttr(PSRV_SPOOL_ATTR_PRINTER
, B_STRING_TYPE
, 0,
323 printerName
, strlen(printerName
) + 1);
324 fSpoolFile
->WriteAttr(PSRV_SPOOL_ATTR_STATUS
, B_STRING_TYPE
, 0,
325 PSRV_JOB_STATUS_WAITING
, strlen(PSRV_JOB_STATUS_WAITING
) + 1);
326 fSpoolFile
->WriteAttr(PSRV_SPOOL_ATTR_MIMETYPE
, B_STRING_TYPE
, 0,
327 appInfo
.signature
, strlen(appInfo
.signature
) + 1);
333 // notify print server
334 BMessenger printServer
;
335 if (PrintServerMessenger::GetPrintServerMessenger(printServer
) != B_OK
)
338 BMessage
request(PSRV_PRINT_SPOOLED_JOB
);
339 request
.AddString("JobName", fPrintJobName
);
340 request
.AddString("Spool File", fSpoolFileName
);
343 printServer
.SendMessage(&request
, &reply
);
348 BPrintJob::CancelJob()
350 if (fSpoolFile
== NULL
)
354 BEntry(fSpoolFileName
).Remove();
361 BPrintJob::SpoolPage()
363 if (fSpoolFile
== NULL
)
366 if (fCurrentPageHeader
->number_of_pictures
== 0)
369 fSpoolFileHeader
.page_count
++;
370 fSpoolFile
->Seek(0, SEEK_END
);
371 if (fCurrentPageHeaderOffset
) {
372 // update last written page_header
373 fCurrentPageHeader
->next_page
= fSpoolFile
->Position();
374 fSpoolFile
->Seek(fCurrentPageHeaderOffset
, SEEK_SET
);
375 fSpoolFile
->Write(fCurrentPageHeader
, sizeof(_page_header_
));
376 fSpoolFile
->Seek(fCurrentPageHeader
->next_page
, SEEK_SET
);
384 BPrintJob::CanContinue()
386 // Check if our local error storage is still B_OK
387 return fError
== B_OK
&& !fAbort
;
392 BPrintJob::DrawView(BView
* view
, BRect rect
, BPoint where
)
394 if (fSpoolFile
== NULL
)
400 if (view
->LockLooper()) {
402 _RecurseView(view
, B_ORIGIN
- rect
.LeftTop(), &picture
, rect
);
403 _AddPicture(picture
, rect
, where
);
404 view
->UnlockLooper();
410 BPrintJob::Settings()
412 if (fSetupMessage
== NULL
)
415 return new BMessage(*fSetupMessage
);
420 BPrintJob::SetSettings(BMessage
* message
)
423 _HandlePrintSetup(message
);
425 delete fSetupMessage
;
426 fSetupMessage
= message
;
431 BPrintJob::IsSettingsMessageValid(BMessage
* message
) const
433 char* printerName
= _GetCurrentPrinterName();
434 if (printerName
== NULL
)
437 const char* name
= NULL
;
438 // The passed message is valid if it contains the right printer name.
439 bool valid
= message
!= NULL
440 && message
->FindString("printer_name", &name
) == B_OK
441 && strcmp(printerName
, name
) == 0;
448 // Either SetSettings() or ConfigPage() has to be called prior
449 // to any of the getters otherwise they return undefined values.
451 BPrintJob::PaperRect()
453 if (fDefaultSetupMessage
== NULL
)
454 _LoadDefaultSettings();
461 BPrintJob::PrintableRect()
463 if (fDefaultSetupMessage
== NULL
)
464 _LoadDefaultSettings();
471 BPrintJob::GetResolution(int32
* xdpi
, int32
* ydpi
)
473 if (fDefaultSetupMessage
== NULL
)
474 _LoadDefaultSettings();
477 *xdpi
= fXResolution
;
480 *ydpi
= fYResolution
;
485 BPrintJob::FirstPage()
492 BPrintJob::LastPage()
499 BPrintJob::PrinterType(void*) const
501 BMessenger printServer
;
502 if (PrintServerMessenger::GetPrintServerMessenger(printServer
) != B_OK
)
503 return B_COLOR_PRINTER
; // default
506 BMessage
message(PSRV_GET_ACTIVE_PRINTER
);
507 printServer
.SendMessage(&message
, &reply
);
510 if (reply
.FindInt32("color", &type
) != B_OK
)
511 return B_COLOR_PRINTER
; // default
517 // #pragma mark - private
521 BPrintJob::_RecurseView(BView
* view
, BPoint origin
, BPicture
* picture
,
524 ASSERT(picture
!= NULL
);
527 region
.Set(BRect(rect
.left
, rect
.top
, rect
.right
, rect
.bottom
));
528 view
->fState
->print_rect
= rect
;
530 view
->AppendToPicture(picture
);
532 view
->SetOrigin(origin
);
533 view
->ConstrainClippingRegion(®ion
);
535 if (view
->ViewColor() != B_TRANSPARENT_COLOR
) {
536 rgb_color highColor
= view
->HighColor();
537 view
->SetHighColor(view
->ViewColor());
538 view
->FillRect(rect
);
539 view
->SetHighColor(highColor
);
542 if ((view
->Flags() & B_WILL_DRAW
) != 0) {
543 view
->fIsPrinting
= true;
545 view
->fIsPrinting
= false;
551 BView
* child
= view
->ChildAt(0);
552 while (child
!= NULL
) {
553 if (!child
->IsHidden()) {
554 BPoint
leftTop(view
->Bounds().LeftTop() + child
->Frame().LeftTop());
555 BRect
printRect(rect
.OffsetToCopy(rect
.LeftTop() - leftTop
)
557 if (printRect
.IsValid())
558 _RecurseView(child
, origin
+ leftTop
, picture
, printRect
);
560 child
= child
->NextSibling();
563 if ((view
->Flags() & B_DRAW_ON_CHILDREN
) != 0) {
564 view
->AppendToPicture(picture
);
566 view
->SetOrigin(origin
);
567 view
->ConstrainClippingRegion(®ion
);
568 view
->fIsPrinting
= true;
569 view
->DrawAfterChildren(rect
);
570 view
->fIsPrinting
= false;
578 BPrintJob::_GetMangledName(char* buffer
, size_t bufferSize
) const
580 snprintf(buffer
, bufferSize
, "%s@%" B_PRId64
, fPrintJobName
,
581 system_time() / 1000);
586 BPrintJob::_HandlePageSetup(BMessage
* setup
)
588 setup
->FindRect(PSRV_FIELD_PRINTABLE_RECT
, &fUsableSize
);
589 setup
->FindRect(PSRV_FIELD_PAPER_RECT
, &fPaperSize
);
591 // TODO verify data type (taken from libprint)
593 if (setup
->FindInt64(PSRV_FIELD_XRES
, &valueInt64
) == B_OK
)
594 fXResolution
= (short)valueInt64
;
596 if (setup
->FindInt64(PSRV_FIELD_YRES
, &valueInt64
) == B_OK
)
597 fYResolution
= (short)valueInt64
;
602 BPrintJob::_HandlePrintSetup(BMessage
* message
)
604 _HandlePageSetup(message
);
607 if (message
->FindInt32(PSRV_FIELD_FIRST_PAGE
, &fFirstPage
) != B_OK
)
610 if (message
->FindInt32(PSRV_FIELD_LAST_PAGE
, &fLastPage
) != B_OK
)
618 BPrintJob::_NewPage()
620 // init, write new page_header
621 fCurrentPageHeader
->next_page
= 0;
622 fCurrentPageHeader
->number_of_pictures
= 0;
623 fCurrentPageHeaderOffset
= fSpoolFile
->Position();
624 fSpoolFile
->Write(fCurrentPageHeader
, sizeof(_page_header_
));
629 BPrintJob::_EndLastPage()
634 if (fCurrentPageHeader
->number_of_pictures
== 0)
637 fSpoolFileHeader
.page_count
++;
638 fSpoolFile
->Seek(0, SEEK_END
);
639 if (fCurrentPageHeaderOffset
) {
640 fCurrentPageHeader
->next_page
= 0;
641 fSpoolFile
->Seek(fCurrentPageHeaderOffset
, SEEK_SET
);
642 fSpoolFile
->Write(fCurrentPageHeader
, sizeof(_page_header_
));
643 fSpoolFile
->Seek(0, SEEK_END
);
649 BPrintJob::_AddSetupSpec()
651 fSetupMessage
->Flatten(fSpoolFile
);
656 BPrintJob::_AddPicture(BPicture
& picture
, BRect
& rect
, BPoint
& where
)
658 ASSERT(fSpoolFile
!= NULL
);
660 fCurrentPageHeader
->number_of_pictures
++;
661 fSpoolFile
->Write(&where
, sizeof(BRect
));
662 fSpoolFile
->Write(&rect
, sizeof(BPoint
));
663 picture
.Flatten(fSpoolFile
);
667 /*! Returns a copy of the applications default printer name or NULL if it
668 could not be obtained. Caller is responsible to free the string using
672 BPrintJob::_GetCurrentPrinterName() const
674 BMessenger printServer
;
675 if (PrintServerMessenger::GetPrintServerMessenger(printServer
) != B_OK
)
678 const char* printerName
= NULL
;
681 BMessage
message(PSRV_GET_ACTIVE_PRINTER
);
682 if (printServer
.SendMessage(&message
, &reply
) == B_OK
)
683 reply
.FindString("printer_name", &printerName
);
685 if (printerName
== NULL
)
688 return strdup(printerName
);
693 BPrintJob::_LoadDefaultSettings()
695 BMessenger printServer
;
696 if (PrintServerMessenger::GetPrintServerMessenger(printServer
) != B_OK
)
699 BMessage
message(PSRV_GET_DEFAULT_SETTINGS
);
700 BMessage
* reply
= new BMessage
;
702 printServer
.SendMessage(&message
, reply
);
704 // Only override our settings if we don't have any settings yet
705 if (fSetupMessage
== NULL
)
706 _HandlePrintSetup(reply
);
708 delete fDefaultSetupMessage
;
709 fDefaultSetupMessage
= reply
;
713 void BPrintJob::_ReservedPrintJob1() {}
714 void BPrintJob::_ReservedPrintJob2() {}
715 void BPrintJob::_ReservedPrintJob3() {}
716 void BPrintJob::_ReservedPrintJob4() {}
719 // #pragma mark -- PrintServerMessenger
725 PrintServerMessenger::PrintServerMessenger(uint32 what
, BMessage
*input
)
731 fThreadCompleted(-1),
732 fHiddenApplicationModalWindow(NULL
)
738 PrintServerMessenger::~PrintServerMessenger()
741 // in case SendRequest could not start the thread
742 delete fRequest
; fRequest
= NULL
;
748 PrintServerMessenger::RejectUserInput()
750 fHiddenApplicationModalWindow
= new BAlert("bogus", "app_modal", "OK");
751 fHiddenApplicationModalWindow
->DefaultButton()->SetEnabled(false);
752 fHiddenApplicationModalWindow
->SetDefaultButton(NULL
);
753 fHiddenApplicationModalWindow
->SetFlags(fHiddenApplicationModalWindow
->Flags() | B_CLOSE_ON_ESCAPE
);
754 fHiddenApplicationModalWindow
->MoveTo(-65000, -65000);
755 fHiddenApplicationModalWindow
->Go(NULL
);
760 PrintServerMessenger::AllowUserInput()
762 fHiddenApplicationModalWindow
->Lock();
763 fHiddenApplicationModalWindow
->Quit();
768 PrintServerMessenger::DeleteSemaphore()
770 if (fThreadCompleted
>= B_OK
) {
771 sem_id id
= fThreadCompleted
;
772 fThreadCompleted
= -1;
779 PrintServerMessenger::SendRequest()
781 fThreadCompleted
= create_sem(0, "print_server_messenger_sem");
782 if (fThreadCompleted
< B_OK
)
785 thread_id id
= spawn_thread(MessengerThread
, "async_request",
786 B_NORMAL_PRIORITY
, this);
787 if (id
<= 0 || resume_thread(id
) != B_OK
)
790 // Get the originating window, if it exists
791 BWindow
* window
= dynamic_cast<BWindow
*>(
792 BLooper::LooperForThread(find_thread(NULL
)));
793 if (window
!= NULL
) {
797 err
= acquire_sem_etc(fThreadCompleted
, 1, B_RELATIVE_TIMEOUT
,
799 // We've (probably) had our time slice taken away from us
800 } while (err
== B_INTERRUPTED
);
802 // Semaphore was finally nuked in SetResult(BMessage *)
803 if (err
== B_BAD_SEM_ID
)
805 window
->UpdateIfNeeded();
808 // No window to update, so just hang out until we're done.
809 while (acquire_sem(fThreadCompleted
) == B_INTERRUPTED
);
813 wait_for_thread(id
, &status
);
815 return Result() != NULL
? B_OK
: B_ERROR
;
820 PrintServerMessenger::Request()
822 if (fRequest
!= NULL
)
825 if (fInput
!= NULL
) {
826 fRequest
= new BMessage(*fInput
);
827 fRequest
->what
= fWhat
;
829 fRequest
= new BMessage(fWhat
);
836 PrintServerMessenger::SetResult(BMessage
* result
)
840 // terminate loop in thread spawned by SendRequest
845 PrintServerMessenger::GetPrintServerMessenger(BMessenger
& messenger
)
847 messenger
= BMessenger(PSRV_SIGNATURE_TYPE
);
848 return messenger
.IsValid() ? B_OK
: B_ERROR
;
853 PrintServerMessenger::MessengerThread(void* data
)
855 PrintServerMessenger
* messenger
= static_cast<PrintServerMessenger
*>(data
);
857 BMessenger printServer
;
858 if (messenger
->GetPrintServerMessenger(printServer
) != B_OK
) {
859 ShowError(B_TRANSLATE("Print Server is not responding."));
860 messenger
->SetResult(NULL
);
864 BMessage
* request
= messenger
->Request();
865 if (request
== NULL
) {
866 messenger
->SetResult(NULL
);
872 if (printServer
.SendMessage(request
, &reply
) != B_OK
873 || reply
.what
!= 'okok' ) {
874 messenger
->SetResult(NULL
);
878 messenger
->SetResult(new BMessage(reply
));
883 } // namespace BPrivate