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