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