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