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