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