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