1/*
2 * Copyright 2001-2014 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Marc Flerackers, mflerackers@androme.be
7 */
8
9
10// Records a series of drawing instructions that can be "replayed" later.
11
12
13#include <Picture.h>
14
15#include <new>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20//#define DEBUG 1
21#include <ByteOrder.h>
22#include <Debug.h>
23#include <List.h>
24#include <Message.h>
25
26#include <AppServerLink.h>
27#include <Autolock.h>
28#include <ObjectList.h>
29#include <PicturePlayer.h>
30#include <ServerProtocol.h>
31
32#include "PicturePrivate.h"
33
34
35static BObjectList<BPicture> sPictureList;
36static BLocker sPictureListLock;
37
38
39void
40reconnect_pictures_to_app_server()
41{
42	BAutolock _(sPictureListLock);
43	for (int32 i = 0; i < sPictureList.CountItems(); i++) {
44		BPicture::Private picture(sPictureList.ItemAt(i));
45		picture.ReconnectToAppServer();
46	}
47}
48
49
50BPicture::Private::Private(BPicture* picture)
51	:
52	fPicture(picture)
53{
54}
55
56
57void
58BPicture::Private::ReconnectToAppServer()
59{
60	fPicture->_Upload();
61}
62
63
64struct _BPictureExtent_ {
65							_BPictureExtent_(int32 size = 0);
66							~_BPictureExtent_();
67
68			const void*		Data() const { return fNewData; }
69			status_t		ImportData(const void* data, int32 size);
70
71			status_t		Flatten(BDataIO* stream);
72			status_t		Unflatten(BDataIO* stream);
73
74			int32			Size() const { return fNewSize; }
75			status_t		SetSize(int32 size);
76
77			bool			AddPicture(BPicture* picture)
78								{ return fPictures.AddItem(picture); }
79			void			DeletePicture(int32 index)
80								{ delete static_cast<BPicture*>
81									(fPictures.RemoveItem(index)); }
82
83			BList*			Pictures() { return &fPictures; }
84			BPicture*		PictureAt(int32 index)
85								{ return static_cast<BPicture*>
86									(fPictures.ItemAt(index)); }
87
88			int32			CountPictures() const
89								{ return fPictures.CountItems(); }
90
91private:
92			void*	fNewData;
93			int32	fNewSize;
94
95			BList	fPictures;
96				// In R5 this is a BArray<BPicture*>
97				// which is completely inline.
98};
99
100
101struct picture_header {
102	int32 magic1; // version ?
103	int32 magic2; // endianess ?
104};
105
106
107BPicture::BPicture()
108	:
109	fToken(-1),
110	fExtent(NULL),
111	fUsurped(NULL)
112{
113	_InitData();
114}
115
116
117BPicture::BPicture(const BPicture& otherPicture)
118	:
119	fToken(-1),
120	fExtent(NULL),
121	fUsurped(NULL)
122{
123	_InitData();
124
125	if (otherPicture.fToken != -1) {
126		BPrivate::AppServerLink link;
127		link.StartMessage(AS_CLONE_PICTURE);
128		link.Attach<int32>(otherPicture.fToken);
129
130		status_t status = B_ERROR;
131		if (link.FlushWithReply(status) == B_OK && status == B_OK)
132			link.Read<int32>(&fToken);
133
134		if (status < B_OK)
135			return;
136	}
137
138	if (otherPicture.fExtent->Size() > 0) {
139		fExtent->ImportData(otherPicture.fExtent->Data(),
140			otherPicture.fExtent->Size());
141
142		for (int32 i = 0; i < otherPicture.fExtent->CountPictures(); i++) {
143			BPicture* picture
144				= new BPicture(*otherPicture.fExtent->PictureAt(i));
145			fExtent->AddPicture(picture);
146		}
147	}
148}
149
150
151BPicture::BPicture(BMessage* data)
152	:
153	fToken(-1),
154	fExtent(NULL),
155	fUsurped(NULL)
156{
157	_InitData();
158
159	int32 version;
160	if (data->FindInt32("_ver", &version) != B_OK)
161		version = 0;
162
163	int8 endian;
164	if (data->FindInt8("_endian", &endian) != B_OK)
165		endian = 0;
166
167	const void* pictureData;
168	ssize_t size;
169	if (data->FindData("_data", B_RAW_TYPE, &pictureData, &size) != B_OK)
170		return;
171
172	// Load sub pictures
173	BMessage pictureMessage;
174	int32 i = 0;
175	while (data->FindMessage("piclib", i++, &pictureMessage) == B_OK) {
176		BPicture* picture = new BPicture(&pictureMessage);
177		fExtent->AddPicture(picture);
178	}
179
180	if (version == 0) {
181		// TODO: For now. We'll see if it's worth to support old style data
182		debugger("old style BPicture data is not supported");
183	} else if (version == 1) {
184		fExtent->ImportData(pictureData, size);
185
186//		swap_data(fExtent->fNewData, fExtent->fNewSize);
187
188		if (fExtent->Size() > 0)
189			_AssertServerCopy();
190	}
191
192	// Do we just free the data now?
193	if (fExtent->Size() > 0)
194		fExtent->SetSize(0);
195
196	// What with the sub pictures?
197	for (i = fExtent->CountPictures() - 1; i >= 0; i--)
198		fExtent->DeletePicture(i);
199}
200
201
202BPicture::BPicture(const void* data, int32 size)
203{
204	_InitData();
205	// TODO: For now. We'll see if it's worth to support old style data
206	debugger("old style BPicture data is not supported");
207}
208
209
210void
211BPicture::_InitData()
212{
213	fToken = -1;
214	fUsurped = NULL;
215
216	fExtent = new (std::nothrow) _BPictureExtent_;
217
218	BAutolock _(sPictureListLock);
219	sPictureList.AddItem(this);
220}
221
222
223BPicture::~BPicture()
224{
225	BAutolock _(sPictureListLock);
226	sPictureList.RemoveItem(this, false);
227	_DisposeData();
228}
229
230
231void
232BPicture::_DisposeData()
233{
234	if (fToken != -1) {
235		BPrivate::AppServerLink link;
236
237		link.StartMessage(AS_DELETE_PICTURE);
238		link.Attach<int32>(fToken);
239		link.Flush();
240		SetToken(-1);
241	}
242
243	delete fExtent;
244	fExtent = NULL;
245}
246
247
248BArchivable*
249BPicture::Instantiate(BMessage* data)
250{
251	if (validate_instantiation(data, "BPicture"))
252		return new BPicture(data);
253
254	return NULL;
255}
256
257
258status_t
259BPicture::Archive(BMessage* data, bool deep) const
260{
261	if (!const_cast<BPicture*>(this)->_AssertLocalCopy())
262		return B_ERROR;
263
264	status_t err = BArchivable::Archive(data, deep);
265	if (err != B_OK)
266		return err;
267
268	err = data->AddInt32("_ver", 1);
269	if (err != B_OK)
270		return err;
271
272	err = data->AddInt8("_endian", B_HOST_IS_BENDIAN);
273	if (err != B_OK)
274		return err;
275
276	err = data->AddData("_data", B_RAW_TYPE, fExtent->Data(), fExtent->Size());
277	if (err != B_OK)
278		return err;
279
280	for (int32 i = 0; i < fExtent->CountPictures(); i++) {
281		BMessage pictureMessage;
282
283		err = fExtent->PictureAt(i)->Archive(&pictureMessage, deep);
284		if (err != B_OK)
285			break;
286
287		err = data->AddMessage("piclib", &pictureMessage);
288		if (err != B_OK)
289			break;
290	}
291
292	return err;
293}
294
295
296status_t
297BPicture::Perform(perform_code code, void* arg)
298{
299	return BArchivable::Perform(code, arg);
300}
301
302
303status_t
304BPicture::Play(void** callBackTable, int32 tableEntries, void* user)
305{
306	if (!_AssertLocalCopy())
307		return B_ERROR;
308
309	BPrivate::PicturePlayer player(fExtent->Data(), fExtent->Size(),
310		fExtent->Pictures());
311
312	return player.Play(callBackTable, tableEntries, user);
313}
314
315
316status_t
317BPicture::Flatten(BDataIO* stream)
318{
319	// TODO: what about endianess?
320
321	if (!_AssertLocalCopy())
322		return B_ERROR;
323
324	const picture_header header = { 2, 0 };
325	ssize_t bytesWritten = stream->Write(&header, sizeof(header));
326	if (bytesWritten < B_OK)
327		return bytesWritten;
328
329	if (bytesWritten != (ssize_t)sizeof(header))
330		return B_IO_ERROR;
331
332	return fExtent->Flatten(stream);
333}
334
335
336status_t
337BPicture::Unflatten(BDataIO* stream)
338{
339	// TODO: clear current picture data?
340
341	picture_header header;
342	ssize_t bytesRead = stream->Read(&header, sizeof(header));
343	if (bytesRead < B_OK)
344		return bytesRead;
345
346	if (bytesRead != (ssize_t)sizeof(header)
347		|| header.magic1 != 2 || header.magic2 != 0)
348		return B_BAD_TYPE;
349
350	status_t status = fExtent->Unflatten(stream);
351	if (status < B_OK)
352		return status;
353
354//	swap_data(fExtent->fNewData, fExtent->fNewSize);
355
356	if (!_AssertServerCopy())
357		return B_ERROR;
358
359	// Data is now kept server side, remove the local copy
360	if (fExtent->Data() != NULL)
361		fExtent->SetSize(0);
362
363	return status;
364}
365
366
367void
368BPicture::_ImportOldData(const void* data, int32 size)
369{
370	// TODO: We don't support old data for now
371}
372
373
374void
375BPicture::SetToken(int32 token)
376{
377	fToken = token;
378}
379
380
381int32
382BPicture::Token() const
383{
384	return fToken;
385}
386
387
388bool
389BPicture::_AssertLocalCopy()
390{
391	if (fExtent->Data() != NULL)
392		return true;
393
394	if (fToken == -1)
395		return false;
396
397	return _Download() == B_OK;
398}
399
400
401bool
402BPicture::_AssertOldLocalCopy()
403{
404	// TODO: We don't support old data for now
405
406	return false;
407}
408
409
410bool
411BPicture::_AssertServerCopy()
412{
413	if (fToken != -1)
414		return true;
415
416	if (fExtent->Data() == NULL)
417		return false;
418
419	for (int32 i = 0; i < fExtent->CountPictures(); i++) {
420		if (!fExtent->PictureAt(i)->_AssertServerCopy())
421			return false;
422	}
423
424	return _Upload() == B_OK;
425}
426
427
428status_t
429BPicture::_Upload()
430{
431	if (fExtent == NULL || fExtent->Data() == NULL)
432		return B_BAD_VALUE;
433
434	BPrivate::AppServerLink link;
435
436	link.StartMessage(AS_CREATE_PICTURE);
437	link.Attach<int32>(fExtent->CountPictures());
438
439	for (int32 i = 0; i < fExtent->CountPictures(); i++) {
440		BPicture* picture = fExtent->PictureAt(i);
441		if (picture != NULL)
442			link.Attach<int32>(picture->fToken);
443		else
444			link.Attach<int32>(-1);
445	}
446	link.Attach<int32>(fExtent->Size());
447	link.Attach(fExtent->Data(), fExtent->Size());
448
449	status_t status = B_ERROR;
450	if (link.FlushWithReply(status) == B_OK
451		&& status == B_OK) {
452		link.Read<int32>(&fToken);
453	}
454
455	return status;
456}
457
458
459status_t
460BPicture::_Download()
461{
462	ASSERT(fExtent->Data() == NULL);
463	ASSERT(fToken != -1);
464
465	BPrivate::AppServerLink link;
466
467	link.StartMessage(AS_DOWNLOAD_PICTURE);
468	link.Attach<int32>(fToken);
469
470	status_t status = B_ERROR;
471	if (link.FlushWithReply(status) == B_OK && status == B_OK) {
472		int32 count = 0;
473		link.Read<int32>(&count);
474
475		// Read sub picture tokens
476		for (int32 i = 0; i < count; i++) {
477			BPicture* picture = new BPicture;
478			link.Read<int32>(&picture->fToken);
479			fExtent->AddPicture(picture);
480		}
481
482		int32 size;
483		link.Read<int32>(&size);
484		status = fExtent->SetSize(size);
485		if (status == B_OK)
486			link.Read(const_cast<void*>(fExtent->Data()), size);
487	}
488
489	return status;
490}
491
492
493const void*
494BPicture::Data() const
495{
496	if (fExtent->Data() == NULL)
497		const_cast<BPicture*>(this)->_AssertLocalCopy();
498
499	return fExtent->Data();
500}
501
502
503int32
504BPicture::DataSize() const
505{
506	if (fExtent->Data() == NULL)
507		const_cast<BPicture*>(this)->_AssertLocalCopy();
508
509	return fExtent->Size();
510}
511
512
513void
514BPicture::Usurp(BPicture* lameDuck)
515{
516	_DisposeData();
517
518	// Reinitializes the BPicture
519	_InitData();
520
521	// Do the Usurping
522	fUsurped = lameDuck;
523}
524
525
526BPicture*
527BPicture::StepDown()
528{
529	BPicture* lameDuck = fUsurped;
530	fUsurped = NULL;
531
532	return lameDuck;
533}
534
535
536void BPicture::_ReservedPicture1() {}
537void BPicture::_ReservedPicture2() {}
538void BPicture::_ReservedPicture3() {}
539
540
541BPicture&
542BPicture::operator=(const BPicture&)
543{
544	return* this;
545}
546
547
548// _BPictureExtent_
549_BPictureExtent_::_BPictureExtent_(int32 size)
550	:
551	fNewData(NULL),
552	fNewSize(0)
553{
554	SetSize(size);
555}
556
557
558_BPictureExtent_::~_BPictureExtent_()
559{
560	free(fNewData);
561	for (int32 i = 0; i < fPictures.CountItems(); i++)
562		delete static_cast<BPicture*>(fPictures.ItemAtFast(i));
563}
564
565
566status_t
567_BPictureExtent_::ImportData(const void* data, int32 size)
568{
569	if (data == NULL)
570		return B_BAD_VALUE;
571
572	status_t status = B_OK;
573	if (Size() != size)
574		status = SetSize(size);
575
576	if (status == B_OK)
577		memcpy(fNewData, data, size);
578
579	return status;
580}
581
582
583status_t
584_BPictureExtent_::Unflatten(BDataIO* stream)
585{
586	if (stream == NULL)
587		return B_BAD_VALUE;
588
589	int32 count = 0;
590	ssize_t bytesRead = stream->Read(&count, sizeof(count));
591	if (bytesRead < B_OK)
592		return bytesRead;
593	if (bytesRead != (ssize_t)sizeof(count))
594		return B_BAD_DATA;
595
596	for (int32 i = 0; i < count; i++) {
597		BPicture* picture = new BPicture;
598		status_t status = picture->Unflatten(stream);
599		if (status < B_OK) {
600			delete picture;
601			return status;
602		}
603
604		AddPicture(picture);
605	}
606
607	int32 size;
608	bytesRead = stream->Read(&size, sizeof(size));
609	if (bytesRead < B_OK)
610		return bytesRead;
611
612	if (bytesRead != (ssize_t)sizeof(size))
613		return B_IO_ERROR;
614
615	status_t status = B_OK;
616	if (Size() != size)
617		status = SetSize(size);
618
619	if (status < B_OK)
620		return status;
621
622	bytesRead = stream->Read(fNewData, size);
623	if (bytesRead < B_OK)
624		return bytesRead;
625
626	if (bytesRead != (ssize_t)size)
627		return B_IO_ERROR;
628
629	return B_OK;
630}
631
632
633status_t
634_BPictureExtent_::Flatten(BDataIO* stream)
635{
636	int32 count = fPictures.CountItems();
637	ssize_t bytesWritten = stream->Write(&count, sizeof(count));
638	if (bytesWritten < B_OK)
639		return bytesWritten;
640
641	if (bytesWritten != (ssize_t)sizeof(count))
642		return B_IO_ERROR;
643
644	for (int32 i = 0; i < count; i++) {
645		status_t status = PictureAt(i)->Flatten(stream);
646		if (status < B_OK)
647			return status;
648	}
649
650	bytesWritten = stream->Write(&fNewSize, sizeof(fNewSize));
651	if (bytesWritten < B_OK)
652		return bytesWritten;
653
654	if (bytesWritten != (ssize_t)sizeof(fNewSize))
655		return B_IO_ERROR;
656
657	bytesWritten = stream->Write(fNewData, fNewSize);
658	if (bytesWritten < B_OK)
659		return bytesWritten;
660
661	if (bytesWritten != fNewSize)
662		return B_IO_ERROR;
663
664	return B_OK;
665}
666
667
668status_t
669_BPictureExtent_::SetSize(int32 size)
670{
671	if (size < 0)
672		return B_BAD_VALUE;
673
674	if (size == fNewSize)
675		return B_OK;
676
677	if (size == 0) {
678		free(fNewData);
679		fNewData = NULL;
680	} else {
681		void* data = realloc(fNewData, size);
682		if (data == NULL)
683			return B_NO_MEMORY;
684
685		fNewData = data;
686	}
687
688	fNewSize = size;
689	return B_OK;
690}
691