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