xref: /haiku/src/kits/interface/PrintJob.cpp (revision 40e20c10768c3dcfc54074b8886e3e91455332e1)
1 /*
2  * Copyright 2001-2009, Haiku.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		I.R. Adema
7  *		Stefano Ceccherini (burton666@libero.it)
8  *		Michael Pfeiffer
9  *		julun <host.haiku@gmx.de>
10  */
11 
12 
13 #include <PrintJob.h>
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <Alert.h>
20 #include <Application.h>
21 #include <Button.h>
22 #include <Debug.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <FindDirectory.h>
26 #include <Messenger.h>
27 #include <NodeInfo.h>
28 #include <OS.h>
29 #include <Path.h>
30 #include <Region.h>
31 #include <Roster.h>
32 #include <SystemCatalog.h>
33 #include <View.h>
34 
35 #include <pr_server.h>
36 #include <ViewPrivate.h>
37 
38 using BPrivate::gSystemCatalog;
39 
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "PrintJob"
42 
43 #undef B_TRANSLATE
44 #define B_TRANSLATE(str) \
45 	gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "PrintJob")
46 
47 
48 /*!	Summary of spool file:
49 
50 		|-----------------------------------|
51 		|         print_file_header         |
52 		|-----------------------------------|
53 		|    BMessage print_job_settings    |
54 		|-----------------------------------|
55 		|                                   |
56 		| ********** (first page) ********* |
57 		| *                               * |
58 		| *         _page_header_         * |
59 		| * ----------------------------- * |
60 		| * |---------------------------| * |
61 		| * |       BPoint where        | * |
62 		| * |       BRect bounds        | * |
63 		| * |       BPicture pic        | * |
64 		| * |---------------------------| * |
65 		| * |---------------------------| * |
66 		| * |       BPoint where        | * |
67 		| * |       BRect bounds        | * |
68 		| * |       BPicture pic        | * |
69 		| * |---------------------------| * |
70 		| ********************************* |
71 		|                                   |
72 		| ********* (second page) ********* |
73 		| *                               * |
74 		| *         _page_header_         * |
75 		| * ----------------------------- * |
76 		| * |---------------------------| * |
77 		| * |       BPoint where        | * |
78 		| * |       BRect bounds        | * |
79 		| * |       BPicture pic        | * |
80 		| * |---------------------------| * |
81 		| ********************************* |
82 		|-----------------------------------|
83 
84 	BeOS R5 print_file_header.version is 1 << 16
85 	BeOS R5 print_file_header.first_page is -1
86 
87 	each page can consist of a collection of picture structures
88 	remaining pages start at _page_header_.next_page of previous _page_header_
89 
90 	See also: "How to Write a BeOS R5 Printer Driver" for description of spool
91 	file format: http://haiku-os.org/documents/dev/how_to_write_a_printer_driver
92 */
93 
94 
95 struct _page_header_ {
96 	int32 number_of_pictures;
97 	off_t next_page;
98 	int32 reserved[10];
99 };
100 
101 
102 static void
103 ShowError(const char* message)
104 {
105 	BAlert* alert = new BAlert(B_TRANSLATE("Error"), message, B_TRANSLATE("OK"));
106 	alert->Go();
107 }
108 
109 
110 // #pragma mark -- PrintServerMessenger
111 
112 
113 namespace BPrivate {
114 
115 
116 class PrintServerMessenger {
117 public:
118 							PrintServerMessenger(uint32 what, BMessage* input);
119 							~PrintServerMessenger();
120 
121 			BMessage*		Request();
122 			status_t		SendRequest();
123 
124 			void			SetResult(BMessage* result);
125 			BMessage*		Result() const { return fResult; }
126 
127 	static	status_t		GetPrintServerMessenger(BMessenger& messenger);
128 
129 private:
130 			void			RejectUserInput();
131 			void			AllowUserInput();
132 			void			DeleteSemaphore();
133 	static	status_t		MessengerThread(void* data);
134 
135 			uint32			fWhat;
136 			BMessage*		fInput;
137 			BMessage*		fRequest;
138 			BMessage*		fResult;
139 			sem_id			fThreadCompleted;
140 			BAlert*			fHiddenApplicationModalWindow;
141 };
142 
143 
144 }	// namespace BPrivate
145 
146 
147 using namespace BPrivate;
148 
149 
150 // #pragma mark -- BPrintJob
151 
152 
153 BPrintJob::BPrintJob(const char* jobName)
154 	:
155 	fPrintJobName(NULL),
156 	fSpoolFile(NULL),
157 	fError(B_NO_INIT),
158 	fSetupMessage(NULL),
159 	fDefaultSetupMessage(NULL),
160 	fAbort(0),
161 	fCurrentPageHeader(NULL)
162 {
163 	memset(&fSpoolFileHeader, 0, sizeof(print_file_header));
164 
165 	if (jobName != NULL && jobName[0])
166 		fPrintJobName = strdup(jobName);
167 
168 	fCurrentPageHeader = new _page_header_;
169 	if (fCurrentPageHeader != NULL)
170 		memset(fCurrentPageHeader, 0, sizeof(_page_header_));
171 }
172 
173 
174 BPrintJob::~BPrintJob()
175 {
176 	CancelJob();
177 
178 	free(fPrintJobName);
179 	delete fSetupMessage;
180 	delete fDefaultSetupMessage;
181 	delete fCurrentPageHeader;
182 }
183 
184 
185 status_t
186 BPrintJob::ConfigPage()
187 {
188 	PrintServerMessenger messenger(PSRV_SHOW_PAGE_SETUP, fSetupMessage);
189 	status_t status = messenger.SendRequest();
190 	if (status != B_OK)
191 		return status;
192 
193 	delete fSetupMessage;
194 	fSetupMessage = messenger.Result();
195 	_HandlePageSetup(fSetupMessage);
196 
197 	return B_OK;
198 }
199 
200 
201 status_t
202 BPrintJob::ConfigJob()
203 {
204 	PrintServerMessenger messenger(PSRV_SHOW_PRINT_SETUP, fSetupMessage);
205 	status_t status = messenger.SendRequest();
206 	if (status != B_OK)
207 		return status;
208 
209 	delete fSetupMessage;
210 	fSetupMessage = messenger.Result();
211 	if (!_HandlePrintSetup(fSetupMessage))
212 		return B_ERROR;
213 
214 	fError = B_OK;
215 	return B_OK;
216 }
217 
218 
219 void
220 BPrintJob::BeginJob()
221 {
222 	fError = B_ERROR;
223 
224 	// can not start a new job until it has been commited or cancelled
225 	if (fSpoolFile != NULL || fCurrentPageHeader == NULL)
226 		return;
227 
228 	// TODO show alert, setup message is required
229 	if (fSetupMessage == NULL)
230 		return;
231 
232 	// create spool file
233 	BPath path;
234 	status_t status = find_directory(B_USER_PRINTERS_DIRECTORY, &path);
235 	if (status != B_OK)
236 		return;
237 
238 	char *printer = _GetCurrentPrinterName();
239 	if (printer == NULL)
240 		return;
241 
242 	path.Append(printer);
243 	free(printer);
244 
245 	char mangledName[B_FILE_NAME_LENGTH];
246 	_GetMangledName(mangledName, B_FILE_NAME_LENGTH);
247 
248 	path.Append(mangledName);
249 	if (path.InitCheck() != B_OK)
250 		return;
251 
252 	// TODO: fSpoolFileName should store the name only (not path which can be
253 	// 1024 bytes long)
254 	strlcpy(fSpoolFileName, path.Path(), sizeof(fSpoolFileName));
255 	fSpoolFile = new BFile(fSpoolFileName, B_READ_WRITE | B_CREATE_FILE);
256 
257 	if (fSpoolFile->InitCheck() != B_OK) {
258 		CancelJob();
259 		return;
260 	}
261 
262 	// add print_file_header
263 	// page_count is updated in CommitJob()
264 	// on BeOS R5 the offset to the first page was always -1
265 	fSpoolFileHeader.version = 1 << 16;
266 	fSpoolFileHeader.page_count = 0;
267 	fSpoolFileHeader.first_page = (off_t)-1;
268 
269 	if (fSpoolFile->Write(&fSpoolFileHeader, sizeof(print_file_header))
270 			!= sizeof(print_file_header)) {
271 		CancelJob();
272 		return;
273 	}
274 
275 	// add printer settings message
276 	if (!fSetupMessage->HasString(PSRV_FIELD_CURRENT_PRINTER))
277 		fSetupMessage->AddString(PSRV_FIELD_CURRENT_PRINTER, printer);
278 
279 	_AddSetupSpec();
280 	_NewPage();
281 
282 	// state variables
283 	fAbort = 0;
284 	fError = B_OK;
285 }
286 
287 
288 void
289 BPrintJob::CommitJob()
290 {
291 	if (fSpoolFile == NULL)
292 		return;
293 
294 	if (fSpoolFileHeader.page_count == 0) {
295 		ShowError(B_TRANSLATE("No Pages to print!"));
296 		CancelJob();
297 		return;
298 	}
299 
300 	// update spool file
301 	_EndLastPage();
302 
303 	// write spool file header
304 	fSpoolFile->Seek(0, SEEK_SET);
305 	fSpoolFile->Write(&fSpoolFileHeader, sizeof(print_file_header));
306 
307 	// set file attributes
308 	app_info appInfo;
309 	be_app->GetAppInfo(&appInfo);
310 	const char* printerName = "";
311 	fSetupMessage->FindString(PSRV_FIELD_CURRENT_PRINTER, &printerName);
312 
313 	BNodeInfo info(fSpoolFile);
314 	info.SetType(PSRV_SPOOL_FILETYPE);
315 
316 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PAGECOUNT, B_INT32_TYPE, 0,
317 		&fSpoolFileHeader.page_count, sizeof(int32));
318 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_DESCRIPTION, B_STRING_TYPE, 0,
319 		fPrintJobName, strlen(fPrintJobName) + 1);
320 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_PRINTER, B_STRING_TYPE, 0,
321 		printerName, strlen(printerName) + 1);
322 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_STATUS, B_STRING_TYPE, 0,
323 		PSRV_JOB_STATUS_WAITING, strlen(PSRV_JOB_STATUS_WAITING) + 1);
324 	fSpoolFile->WriteAttr(PSRV_SPOOL_ATTR_MIMETYPE, B_STRING_TYPE, 0,
325 		appInfo.signature, strlen(appInfo.signature) + 1);
326 
327 	delete fSpoolFile;
328 	fSpoolFile = NULL;
329 	fError = B_ERROR;
330 
331 	// notify print server
332 	BMessenger printServer;
333 	if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
334 		return;
335 
336 	BMessage request(PSRV_PRINT_SPOOLED_JOB);
337 	request.AddString("JobName", fPrintJobName);
338 	request.AddString("Spool File", fSpoolFileName);
339 
340 	BMessage reply;
341 	printServer.SendMessage(&request, &reply);
342 }
343 
344 
345 void
346 BPrintJob::CancelJob()
347 {
348 	if (fSpoolFile == NULL)
349 		return;
350 
351 	fAbort = 1;
352 	BEntry(fSpoolFileName).Remove();
353 	delete fSpoolFile;
354 	fSpoolFile = NULL;
355 }
356 
357 
358 void
359 BPrintJob::SpoolPage()
360 {
361 	if (fSpoolFile == NULL)
362 		return;
363 
364 	if (fCurrentPageHeader->number_of_pictures == 0)
365 		return;
366 
367 	fSpoolFileHeader.page_count++;
368 	fSpoolFile->Seek(0, SEEK_END);
369 	if (fCurrentPageHeaderOffset) {
370 		// update last written page_header
371 		fCurrentPageHeader->next_page = fSpoolFile->Position();
372 		fSpoolFile->Seek(fCurrentPageHeaderOffset, SEEK_SET);
373 		fSpoolFile->Write(fCurrentPageHeader, sizeof(_page_header_));
374 		fSpoolFile->Seek(fCurrentPageHeader->next_page, SEEK_SET);
375 	}
376 
377 	_NewPage();
378 }
379 
380 
381 bool
382 BPrintJob::CanContinue()
383 {
384 	// Check if our local error storage is still B_OK
385 	return fError == B_OK && !fAbort;
386 }
387 
388 
389 void
390 BPrintJob::DrawView(BView* view, BRect rect, BPoint where)
391 {
392 	if (fSpoolFile == NULL)
393 		return;
394 
395 	if (view == NULL)
396 		return;
397 
398 	if (view->LockLooper()) {
399 		BPicture picture;
400 		_RecurseView(view, B_ORIGIN - rect.LeftTop(), &picture, rect);
401 		_AddPicture(picture, rect, where);
402 		view->UnlockLooper();
403 	}
404 }
405 
406 
407 BMessage*
408 BPrintJob::Settings()
409 {
410 	if (fSetupMessage == NULL)
411 		return NULL;
412 
413 	return new BMessage(*fSetupMessage);
414 }
415 
416 
417 void
418 BPrintJob::SetSettings(BMessage* message)
419 {
420 	if (message != NULL)
421 		_HandlePrintSetup(message);
422 
423 	delete fSetupMessage;
424 	fSetupMessage = message;
425 }
426 
427 
428 bool
429 BPrintJob::IsSettingsMessageValid(BMessage* message) const
430 {
431 	char* printerName = _GetCurrentPrinterName();
432 	if (printerName == NULL)
433 		return false;
434 
435 	const char* name = NULL;
436 	// The passed message is valid if it contains the right printer name.
437 	bool valid = message != NULL
438 		&& message->FindString("printer_name", &name) == B_OK
439 		&& strcmp(printerName, name) == 0;
440 
441 	free(printerName);
442 	return valid;
443 }
444 
445 
446 // Either SetSettings() or ConfigPage() has to be called prior
447 // to any of the getters otherwise they return undefined values.
448 BRect
449 BPrintJob::PaperRect()
450 {
451 	if (fDefaultSetupMessage == NULL)
452 		_LoadDefaultSettings();
453 
454 	return fPaperSize;
455 }
456 
457 
458 BRect
459 BPrintJob::PrintableRect()
460 {
461 	if (fDefaultSetupMessage == NULL)
462 		_LoadDefaultSettings();
463 
464 	return fUsableSize;
465 }
466 
467 
468 void
469 BPrintJob::GetResolution(int32* xdpi, int32* ydpi)
470 {
471 	if (fDefaultSetupMessage == NULL)
472 		_LoadDefaultSettings();
473 
474 	if (xdpi != NULL)
475 		*xdpi = fXResolution;
476 
477 	if (ydpi != NULL)
478 		*ydpi = fYResolution;
479 }
480 
481 
482 int32
483 BPrintJob::FirstPage()
484 {
485 	return fFirstPage;
486 }
487 
488 
489 int32
490 BPrintJob::LastPage()
491 {
492 	return fLastPage;
493 }
494 
495 
496 int32
497 BPrintJob::PrinterType(void*) const
498 {
499 	BMessenger printServer;
500 	if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
501 		return B_COLOR_PRINTER; // default
502 
503 	BMessage reply;
504 	BMessage message(PSRV_GET_ACTIVE_PRINTER);
505 	printServer.SendMessage(&message, &reply);
506 
507 	int32 type;
508 	if (reply.FindInt32("color", &type) != B_OK)
509 		return B_COLOR_PRINTER; // default
510 
511 	return type;
512 }
513 
514 
515 // #pragma mark - private
516 
517 
518 void
519 BPrintJob::_RecurseView(BView* view, BPoint origin, BPicture* picture,
520 	BRect rect)
521 {
522 	ASSERT(picture != NULL);
523 
524 	BRegion region;
525 	region.Set(BRect(rect.left, rect.top, rect.right, rect.bottom));
526 	view->fState->print_rect = rect;
527 
528 	view->AppendToPicture(picture);
529 	view->PushState();
530 	view->SetOrigin(origin);
531 	view->ConstrainClippingRegion(&region);
532 
533 	if (view->ViewColor() != B_TRANSPARENT_COLOR) {
534 		rgb_color highColor = view->HighColor();
535 		view->SetHighColor(view->ViewColor());
536 		view->FillRect(rect);
537 		view->SetHighColor(highColor);
538 	}
539 
540 	if ((view->Flags() & B_WILL_DRAW) != 0) {
541 		view->fIsPrinting = true;
542 		view->Draw(rect);
543 		view->fIsPrinting = false;
544 	}
545 
546 	view->PopState();
547 	view->EndPicture();
548 
549 	BView* child = view->ChildAt(0);
550 	while (child != NULL) {
551 		if (!child->IsHidden()) {
552 			BPoint leftTop(view->Bounds().LeftTop() + child->Frame().LeftTop());
553 			BRect printRect(rect.OffsetToCopy(rect.LeftTop() - leftTop)
554 				& child->Bounds());
555 			if (printRect.IsValid())
556 				_RecurseView(child, origin + leftTop, picture, printRect);
557 		}
558 		child = child->NextSibling();
559 	}
560 
561 	if ((view->Flags() & B_DRAW_ON_CHILDREN) != 0) {
562 		view->AppendToPicture(picture);
563 		view->PushState();
564 		view->SetOrigin(origin);
565 		view->ConstrainClippingRegion(&region);
566 		view->fIsPrinting = true;
567 		view->DrawAfterChildren(rect);
568 		view->fIsPrinting = false;
569 		view->PopState();
570 		view->EndPicture();
571 	}
572 }
573 
574 
575 void
576 BPrintJob::_GetMangledName(char* buffer, size_t bufferSize) const
577 {
578 	snprintf(buffer, bufferSize, "%s@%lld", fPrintJobName,
579 		system_time() / 1000);
580 }
581 
582 
583 void
584 BPrintJob::_HandlePageSetup(BMessage* setup)
585 {
586 	setup->FindRect(PSRV_FIELD_PRINTABLE_RECT, &fUsableSize);
587 	setup->FindRect(PSRV_FIELD_PAPER_RECT, &fPaperSize);
588 
589 	// TODO verify data type (taken from libprint)
590 	int64 valueInt64;
591 	if (setup->FindInt64(PSRV_FIELD_XRES, &valueInt64) == B_OK)
592 		fXResolution = (short)valueInt64;
593 
594 	if (setup->FindInt64(PSRV_FIELD_YRES, &valueInt64) == B_OK)
595 		fYResolution = (short)valueInt64;
596 }
597 
598 
599 bool
600 BPrintJob::_HandlePrintSetup(BMessage* message)
601 {
602 	_HandlePageSetup(message);
603 
604 	bool valid = true;
605 	if (message->FindInt32(PSRV_FIELD_FIRST_PAGE, &fFirstPage) != B_OK)
606 		valid = false;
607 
608 	if (message->FindInt32(PSRV_FIELD_LAST_PAGE, &fLastPage) != B_OK)
609 		valid = false;
610 
611 	return valid;
612 }
613 
614 
615 void
616 BPrintJob::_NewPage()
617 {
618 	// init, write new page_header
619 	fCurrentPageHeader->next_page = 0;
620 	fCurrentPageHeader->number_of_pictures = 0;
621 	fCurrentPageHeaderOffset = fSpoolFile->Position();
622 	fSpoolFile->Write(fCurrentPageHeader, sizeof(_page_header_));
623 }
624 
625 
626 void
627 BPrintJob::_EndLastPage()
628 {
629 	if (!fSpoolFile)
630 		return;
631 
632 	if (fCurrentPageHeader->number_of_pictures == 0)
633 		return;
634 
635 	fSpoolFileHeader.page_count++;
636 	fSpoolFile->Seek(0, SEEK_END);
637 	if (fCurrentPageHeaderOffset) {
638 		fCurrentPageHeader->next_page = 0;
639 		fSpoolFile->Seek(fCurrentPageHeaderOffset, SEEK_SET);
640 		fSpoolFile->Write(fCurrentPageHeader, sizeof(_page_header_));
641 		fSpoolFile->Seek(0, SEEK_END);
642 	}
643 }
644 
645 
646 void
647 BPrintJob::_AddSetupSpec()
648 {
649 	fSetupMessage->Flatten(fSpoolFile);
650 }
651 
652 
653 void
654 BPrintJob::_AddPicture(BPicture& picture, BRect& rect, BPoint& where)
655 {
656 	ASSERT(fSpoolFile != NULL);
657 
658 	fCurrentPageHeader->number_of_pictures++;
659 	fSpoolFile->Write(&where, sizeof(BRect));
660 	fSpoolFile->Write(&rect, sizeof(BPoint));
661 	picture.Flatten(fSpoolFile);
662 }
663 
664 
665 /*!	Returns a copy of the applications default printer name or NULL if it
666 	could not be obtained. Caller is responsible to free the string using
667 	free().
668 */
669 char*
670 BPrintJob::_GetCurrentPrinterName() const
671 {
672 	BMessenger printServer;
673 	if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
674 		return NULL;
675 
676 	const char* printerName = NULL;
677 
678 	BMessage reply;
679 	BMessage message(PSRV_GET_ACTIVE_PRINTER);
680 	if (printServer.SendMessage(&message, &reply) == B_OK)
681 		reply.FindString("printer_name", &printerName);
682 
683 	if (printerName == NULL)
684 		return NULL;
685 
686 	return strdup(printerName);
687 }
688 
689 
690 void
691 BPrintJob::_LoadDefaultSettings()
692 {
693 	BMessenger printServer;
694 	if (PrintServerMessenger::GetPrintServerMessenger(printServer) != B_OK)
695 		return;
696 
697 	BMessage message(PSRV_GET_DEFAULT_SETTINGS);
698 	BMessage* reply = new BMessage;
699 
700 	printServer.SendMessage(&message, reply);
701 
702 	// Only override our settings if we don't have any settings yet
703 	if (fSetupMessage == NULL)
704 		_HandlePrintSetup(reply);
705 
706 	delete fDefaultSetupMessage;
707 	fDefaultSetupMessage = reply;
708 }
709 
710 
711 void BPrintJob::_ReservedPrintJob1() {}
712 void BPrintJob::_ReservedPrintJob2() {}
713 void BPrintJob::_ReservedPrintJob3() {}
714 void BPrintJob::_ReservedPrintJob4() {}
715 
716 
717 // #pragma mark -- PrintServerMessenger
718 
719 
720 namespace BPrivate {
721 
722 
723 PrintServerMessenger::PrintServerMessenger(uint32 what, BMessage *input)
724 	:
725 	fWhat(what),
726 	fInput(input),
727 	fRequest(NULL),
728 	fResult(NULL),
729 	fThreadCompleted(-1),
730 	fHiddenApplicationModalWindow(NULL)
731 {
732 	RejectUserInput();
733 }
734 
735 
736 PrintServerMessenger::~PrintServerMessenger()
737 {
738 	DeleteSemaphore();
739 		// in case SendRequest could not start the thread
740 	delete fRequest; fRequest = NULL;
741 	AllowUserInput();
742 }
743 
744 
745 void
746 PrintServerMessenger::RejectUserInput()
747 {
748 	fHiddenApplicationModalWindow = new BAlert("bogus", "app_modal", "OK");
749 	fHiddenApplicationModalWindow->DefaultButton()->SetEnabled(false);
750 	fHiddenApplicationModalWindow->SetDefaultButton(NULL);
751 	fHiddenApplicationModalWindow->MoveTo(-65000, -65000);
752 	fHiddenApplicationModalWindow->Go(NULL);
753 }
754 
755 
756 void
757 PrintServerMessenger::AllowUserInput()
758 {
759 	fHiddenApplicationModalWindow->Lock();
760 	fHiddenApplicationModalWindow->Quit();
761 }
762 
763 
764 void
765 PrintServerMessenger::DeleteSemaphore()
766 {
767 	if (fThreadCompleted >= B_OK) {
768 		sem_id id = fThreadCompleted;
769 		fThreadCompleted = -1;
770 		delete_sem(id);
771 	}
772 }
773 
774 
775 status_t
776 PrintServerMessenger::SendRequest()
777 {
778 	fThreadCompleted = create_sem(0, "print_server_messenger_sem");
779 	if (fThreadCompleted < B_OK)
780 		return B_ERROR;
781 
782 	thread_id id = spawn_thread(MessengerThread, "async_request",
783 		B_NORMAL_PRIORITY, this);
784 	if (id <= 0 || resume_thread(id) != B_OK)
785 		return B_ERROR;
786 
787 	// Get the originating window, if it exists
788 	BWindow* window = dynamic_cast<BWindow*>(
789 		BLooper::LooperForThread(find_thread(NULL)));
790 	if (window != NULL) {
791 		status_t err;
792 		while (true) {
793 			do {
794 				err = acquire_sem_etc(fThreadCompleted, 1, B_RELATIVE_TIMEOUT,
795 					50000);
796 			// We've (probably) had our time slice taken away from us
797 			} while (err == B_INTERRUPTED);
798 
799 			// Semaphore was finally nuked in SetResult(BMessage *)
800 			if (err == B_BAD_SEM_ID)
801 				break;
802 			window->UpdateIfNeeded();
803 		}
804 	} else {
805 		// No window to update, so just hang out until we're done.
806 		while (acquire_sem(fThreadCompleted) == B_INTERRUPTED);
807 	}
808 
809 	status_t status;
810 	wait_for_thread(id, &status);
811 
812 	return Result() != NULL ? B_OK : B_ERROR;
813 }
814 
815 
816 BMessage*
817 PrintServerMessenger::Request()
818 {
819 	if (fRequest != NULL)
820 		return fRequest;
821 
822 	if (fInput != NULL) {
823 		fRequest = new BMessage(*fInput);
824 		fRequest->what = fWhat;
825 	} else
826 		fRequest = new BMessage(fWhat);
827 
828 	return fRequest;
829 }
830 
831 
832 void
833 PrintServerMessenger::SetResult(BMessage* result)
834 {
835 	fResult = result;
836 	DeleteSemaphore();
837 	// terminate loop in thread spawned by SendRequest
838 }
839 
840 
841 status_t
842 PrintServerMessenger::GetPrintServerMessenger(BMessenger& messenger)
843 {
844 	messenger = BMessenger(PSRV_SIGNATURE_TYPE);
845 	return messenger.IsValid() ? B_OK : B_ERROR;
846 }
847 
848 
849 status_t
850 PrintServerMessenger::MessengerThread(void* data)
851 {
852 	PrintServerMessenger* messenger = static_cast<PrintServerMessenger*>(data);
853 
854 	BMessenger printServer;
855 	if (messenger->GetPrintServerMessenger(printServer) != B_OK) {
856 		ShowError(B_TRANSLATE("Print Server is not responding."));
857 		messenger->SetResult(NULL);
858 		return B_ERROR;
859 	}
860 
861 	BMessage* request = messenger->Request();
862 	if (request == NULL) {
863 		messenger->SetResult(NULL);
864 		return B_ERROR;
865 	}
866 
867 
868 	BMessage reply;
869 	if (printServer.SendMessage(request, &reply) != B_OK
870 		|| reply.what != 'okok' ) {
871 		messenger->SetResult(NULL);
872 		return B_ERROR;
873 	}
874 
875 	messenger->SetResult(new BMessage(reply));
876 	return B_OK;
877 }
878 
879 
880 }	// namespace BPrivate
881