1 /* 2 * Copyright 2016, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * Augustin Cavalier <waddlesplash> 7 * kerwizzy 8 */ 9 10 11 #include <AboutWindow.h> 12 #include <Application.h> 13 #include <Bitmap.h> 14 #include <BitmapStream.h> 15 #include <String.h> 16 #include <Catalog.h> 17 #include <Directory.h> 18 #include <File.h> 19 #include <FilePanel.h> 20 #include <FindDirectory.h> 21 #include <MenuBar.h> 22 #include <NodeInfo.h> 23 #include <Path.h> 24 #include <TranslationUtils.h> 25 #include <TranslatorRoster.h> 26 #include <LayoutBuilder.h> 27 #include <View.h> 28 #include <Window.h> 29 #include <Screen.h> 30 #include <ScrollView.h> 31 32 #include <algorithm> 33 34 #include "FractalEngine.h" 35 36 #undef B_TRANSLATION_CONTEXT 37 #define B_TRANSLATION_CONTEXT "MandelbrotWindow" 38 39 #define MANDELBROT_VIEW_REFRESH_FPS 10 40 41 // #pragma mark - FractalView 42 43 //#define TRACE_MANDELBROT_VIEW 44 #ifdef TRACE_MANDELBROT_VIEW 45 # include <stdio.h> 46 # define TRACE(x...) printf(x) 47 #else 48 # define TRACE(x...) 49 #endif 50 51 52 class FractalView : public BView { 53 public: 54 FractalView(); 55 ~FractalView(); 56 57 virtual void AttachedToWindow(); 58 virtual void FrameResized(float, float); 59 virtual void Pulse(); 60 61 virtual void MouseDown(BPoint where); 62 virtual void MouseMoved(BPoint where, uint32 mode, const BMessage*); 63 virtual void MouseUp(BPoint where); 64 65 virtual void MessageReceived(BMessage* msg); 66 virtual void Draw(BRect updateRect); 67 68 void ResetPosition(); 69 void SetLocationFromFrame(double frameX, double frameY); 70 void ZoomFractal(double originX, double originY, double zoomFactor); 71 void ZoomFractalFromFrame(double frameOriginX, double frameOriginY, 72 double zoomFactor); 73 void ImportBitsAndInvalidate(); 74 void RedrawFractal(); 75 void UpdateSize(); 76 void CreateDisplayBitmap(uint16 width, uint16 height); 77 78 void StartSave(); 79 void WriteImage(entry_ref*, char*); 80 void EndSave(); 81 82 FractalEngine* fFractalEngine; 83 enum { 84 MSG_START_SAVE, 85 MSG_WRITE_IMAGE 86 }; 87 88 private: 89 BRect GetDragFrame(); 90 91 BPoint fSelectStart; 92 BPoint fSelectEnd; 93 bool fSelecting; 94 uint32 fMouseButtons; 95 96 BBitmap* fDisplayBitmap; 97 98 double fLocationX; 99 double fLocationY; 100 double fSize; 101 102 BFilePanel* fSavePanel; 103 104 bool fSaving; 105 }; 106 107 108 FractalView::FractalView() 109 : 110 BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS | B_PULSE_NEEDED), 111 fFractalEngine(NULL), 112 fSelecting(false), 113 fDisplayBitmap(NULL), 114 fLocationX(0), 115 fLocationY(0), 116 fSize(0.005), 117 fSavePanel(NULL), 118 fSaving(false) 119 { 120 SetHighColor(make_color(255, 255, 255, 255)); 121 } 122 123 124 FractalView::~FractalView() 125 { 126 delete fDisplayBitmap; 127 } 128 129 130 void FractalView::ResetPosition() 131 { 132 fLocationX = 0; 133 fLocationY = 0; 134 fSize = 0.005; 135 } 136 137 138 void FractalView::AttachedToWindow() 139 { 140 fFractalEngine = new FractalEngine(this, Window()); 141 fFractalEngine->Run(); 142 TRACE("Attached to window\n"); 143 } 144 145 146 void FractalView::FrameResized(float, float) 147 { 148 TRACE("Frame Resize\n"); 149 UpdateSize(); 150 } 151 152 153 void FractalView::UpdateSize() 154 { 155 TRACE("Update Size\n"); 156 BMessage msg(FractalEngine::MSG_RESIZE); 157 158 uint16 width = (uint16)Frame().Width(); 159 uint16 height = (uint16)Frame().Height(); 160 161 msg.AddUInt16("width", width); 162 msg.AddUInt16("height", height); 163 164 CreateDisplayBitmap(width, height); 165 166 msg.AddPointer("bitmap", fDisplayBitmap); 167 168 fFractalEngine->PostMessage(&msg); // Create the new buffer 169 } 170 171 172 void FractalView::CreateDisplayBitmap(uint16 width,uint16 height) 173 { 174 delete fDisplayBitmap; 175 fDisplayBitmap = NULL; 176 TRACE("width %u height %u\n",width,height); 177 BRect rect(0, 0, width, height); 178 fDisplayBitmap = new BBitmap(rect, B_RGB24); 179 } 180 181 182 BRect FractalView::GetDragFrame() 183 { 184 BRect dragZone = BRect(std::min(fSelectStart.x, fSelectEnd.x), 185 std::min(fSelectStart.y, fSelectEnd.y), 186 std::max(fSelectStart.x, fSelectEnd.x), 187 std::max(fSelectStart.y, fSelectEnd.y)), 188 frame = Frame(); 189 float width = dragZone.Width(), 190 height = width * (frame.Height() / frame.Width()); 191 192 float x1 = fSelectStart.x, y1 = fSelectStart.y, x2, y2; 193 if (fSelectStart.x < fSelectEnd.x) 194 x2 = x1 + width; 195 else 196 x2 = x1 - width; 197 if (fSelectStart.y < fSelectEnd.y) 198 y2 = y1 + height; 199 else 200 y2 = y1 - height; 201 return BRect(x1, y1, x2, y2); 202 } 203 204 205 void FractalView::MouseDown(BPoint where) 206 { 207 fSelecting = true; 208 fSelectStart = where; 209 fMouseButtons = 0; 210 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&fMouseButtons); 211 } 212 213 214 void FractalView::MouseMoved(BPoint where, uint32 mode, const BMessage*) 215 { 216 if (fSelecting) { 217 fSelectEnd = where; 218 Invalidate(); 219 } 220 } 221 222 223 void FractalView::SetLocationFromFrame(double frameX,double frameY) 224 { 225 BRect frame = Frame(); 226 227 fLocationX = ((frameX - frame.Width() / 2) * fSize + fLocationX); 228 fLocationY = ((frameY - frame.Height() / 2) * -fSize + fLocationY); 229 // -fSize because is in raster coordinates (y swapped) 230 } 231 232 233 void FractalView::ZoomFractalFromFrame(double frameOriginX, double frameOriginY, 234 double zoomFactor) 235 { 236 BRect frame = Frame(); 237 238 ZoomFractal((frameOriginX - frame.Width() / 2) * fSize + fLocationX, 239 (frameOriginY - frame.Height() / 2) * -fSize + fLocationY, 240 zoomFactor); 241 } 242 243 244 void FractalView::ZoomFractal(double originX, double originY, double zoomFactor) 245 { 246 double deltaX = originX - fLocationX; 247 double deltaY = originY - fLocationY; 248 249 TRACE("oX %g oY %g zoom %g\n", originX, originY, zoomFactor); 250 251 deltaX /= zoomFactor; 252 deltaY /= zoomFactor; 253 254 fLocationX = originX - deltaX; 255 fLocationY = originY - deltaY; 256 fSize /= zoomFactor; 257 } 258 259 260 void FractalView::MouseUp(BPoint where) 261 { 262 BRect frame = Frame(); 263 fSelecting = false; 264 if (fabs(fSelectStart.x - where.x) > 4) { 265 fSelectEnd = where; 266 BRect dragFrame = GetDragFrame(); 267 BPoint lt = dragFrame.LeftTop(); 268 float centerX = lt.x + dragFrame.Width() / 2, 269 centerY = lt.y + dragFrame.Height() / 2; 270 271 SetLocationFromFrame(centerX, centerY); 272 fSize = std::fabs((dragFrame.Width() * fSize) / frame.Width()); 273 } else { 274 if (fMouseButtons & B_PRIMARY_MOUSE_BUTTON) { 275 SetLocationFromFrame(where.x, where.y); 276 ZoomFractal(fLocationX, fLocationY, 2); 277 } else { 278 ZoomFractal(fLocationX, fLocationY, 0.5); 279 } 280 } 281 RedrawFractal(); 282 } 283 284 285 void FractalView::MessageReceived(BMessage* msg) 286 { 287 switch (msg->what) { 288 case B_MOUSE_WHEEL_CHANGED: { 289 float change = msg->FindFloat("be:wheel_delta_y"); 290 BPoint where; 291 GetMouse(&where, NULL); 292 double zoomFactor; 293 if (change < 0) 294 zoomFactor = 3.0/2.0; 295 else 296 zoomFactor = 2.0/3.0; 297 ZoomFractalFromFrame(where.x, where.y, zoomFactor); 298 299 RedrawFractal(); 300 break; 301 } 302 303 case FractalEngine::MSG_BUFFER_CREATED: 304 TRACE("Got buffer created msg.\n"); 305 306 ImportBitsAndInvalidate(); 307 RedrawFractal(); 308 break; 309 310 case FractalEngine::MSG_RENDER_COMPLETE: 311 TRACE("Got render complete msg.\n"); 312 313 Window()->SetPulseRate(0); 314 ImportBitsAndInvalidate(); 315 break; 316 317 case MSG_WRITE_IMAGE: { 318 delete fSavePanel; 319 fSavePanel = NULL; 320 321 entry_ref dirRef; 322 char* name; 323 msg->FindRef("directory", &dirRef); 324 msg->FindString((const char*)"name", (const char**) &name); 325 326 WriteImage(&dirRef, name); 327 break; 328 } 329 330 case B_CANCEL: 331 // image is frozen before the FilePanel is shown 332 EndSave(); 333 break; 334 335 default: 336 BView::MessageReceived(msg); 337 break; 338 } 339 } 340 341 342 void FractalView::Pulse() 343 { 344 ImportBitsAndInvalidate(); 345 } 346 347 348 void FractalView::ImportBitsAndInvalidate() 349 { 350 if (fSaving) { 351 TRACE("Not importing bits because saving.\n"); 352 return; 353 } 354 TRACE("Importing bits...\n"); 355 356 fFractalEngine->WriteToBitmap(fDisplayBitmap); 357 Invalidate(); 358 } 359 360 361 void FractalView::RedrawFractal() 362 { 363 Window()->SetPulseRate(1000000 / MANDELBROT_VIEW_REFRESH_FPS); 364 BMessage message(FractalEngine::MSG_RENDER); 365 message.AddDouble("locationX", fLocationX); 366 message.AddDouble("locationY", fLocationY); 367 message.AddDouble("size", fSize); 368 fFractalEngine->PostMessage(&message); 369 } 370 371 372 void FractalView::Draw(BRect updateRect) 373 { 374 DrawBitmap(fDisplayBitmap, updateRect, updateRect); 375 if (fSelecting) 376 StrokeRect(GetDragFrame()); 377 } 378 379 380 void FractalView::StartSave() { 381 TRACE("Got to start save\n"); 382 fSaving = true; 383 384 BMessenger messenger(this); 385 BMessage message(MSG_WRITE_IMAGE); 386 fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger, 0, 0, false, 387 &message); 388 BString* filename = new BString(); 389 filename->SetToFormat("%g-%g-%g.png", fLocationX, fLocationY, fSize); 390 391 fSavePanel->SetSaveText(filename->String()); 392 fSavePanel->Show(); 393 } 394 395 396 void FractalView::WriteImage(entry_ref* dirRef, char* name) 397 { 398 TRACE("Got to write save handler\n"); 399 400 BFile file; 401 BDirectory parentDir(dirRef); 402 parentDir.CreateFile(name, &file); 403 404 // Write the screenshot bitmap to the file 405 BBitmapStream stream(fDisplayBitmap); 406 BTranslatorRoster* roster = BTranslatorRoster::Default(); 407 roster->Translate(&stream, NULL, NULL, &file, B_PNG_FORMAT, 408 B_TRANSLATOR_BITMAP); 409 410 BNodeInfo info(&file); 411 if (info.InitCheck() == B_OK) 412 info.SetType("image/png"); 413 414 BBitmap* bitmap; 415 stream.DetachBitmap(&bitmap); 416 // The stream takes over ownership of the bitmap 417 418 // unfreeze the image, image was frozen before invoke of FilePanel 419 EndSave(); 420 } 421 422 423 void FractalView::EndSave() 424 { 425 fSaving = false; 426 ImportBitsAndInvalidate(); 427 } 428 429 430 // #pragma mark - MandelbrotWindow 431 432 433 class MandelbrotWindow : public BWindow 434 { 435 public: 436 enum { 437 MSG_MANDELBROT_SET = 'MndW', 438 MSG_BURNINGSHIP_SET, 439 MSG_TRICORN_SET, 440 MSG_JULIA_SET, 441 MSG_ORBITTRAP_SET, 442 MSG_MULTIBROT_SET, 443 444 MSG_ROYAL_PALETTE, 445 MSG_DEEPFROST_PALETTE, 446 MSG_FROST_PALETTE, 447 MSG_FIRE_PALETTE, 448 MSG_MIDNIGHT_PALETTE, 449 MSG_GRASSLAND_PALETTE, 450 MSG_LIGHTNING_PALETTE, 451 MSG_SPRING_PALETTE, 452 MSG_HIGHCONTRAST_PALETTE, 453 454 MSG_ITER_128, 455 MSG_ITER_512, 456 MSG_ITER_1024, 457 MSG_ITER_4096, 458 MSG_ITER_8192, 459 MSG_ITER_12288, 460 MSG_ITER_16384, 461 462 MSG_SUBSAMPLING_1, 463 MSG_SUBSAMPLING_2, 464 MSG_SUBSAMPLING_3, 465 MSG_SUBSAMPLING_4, 466 467 MSG_TOGGLE_FULLSCREEN 468 }; 469 MandelbrotWindow(BRect frame); 470 ~MandelbrotWindow() {} 471 472 void ToggleFullscreen(); 473 474 virtual void DispatchMessage(BMessage* message, BHandler* target); 475 virtual void MessageReceived(BMessage* msg); 476 virtual bool QuitRequested(); 477 478 bool fFullScreen; 479 480 BMenuBar* fMenuBar; 481 BRect fWindowFrame; 482 483 private: 484 FractalView* fFractalView; 485 }; 486 487 488 MandelbrotWindow::MandelbrotWindow(BRect frame) 489 : 490 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Mandelbrot"), B_TITLED_WINDOW_LOOK, 491 B_NORMAL_WINDOW_FEEL, 0L), 492 fFractalView(new FractalView) 493 { 494 fFullScreen = false; 495 fMenuBar = new BMenuBar("MenuBar"); 496 BMenu* setMenu; 497 BMenu* paletteMenu; 498 BMenu* iterMenu; 499 BMenu* subsamplingMenu; 500 BLayoutBuilder::Menu<>(fMenuBar) 501 .AddMenu(B_TRANSLATE("File")) 502 .AddItem(B_TRANSLATE("Save as image" B_UTF8_ELLIPSIS), 503 FractalView::MSG_START_SAVE, 'S') 504 .AddSeparator() 505 .AddItem(B_TRANSLATE("About"), B_ABOUT_REQUESTED) 506 .AddItem(B_TRANSLATE("Quit"), B_QUIT_REQUESTED, 'Q') 507 .End() 508 .AddMenu(B_TRANSLATE("View")) 509 .AddItem(B_TRANSLATE("Full screen"), MSG_TOGGLE_FULLSCREEN, 510 B_RETURN) 511 .End() 512 .AddMenu(B_TRANSLATE("Set")) 513 .GetMenu(setMenu) 514 .AddItem(B_TRANSLATE("Mandelbrot"), MSG_MANDELBROT_SET) 515 .AddItem(B_TRANSLATE("Burning Ship"), MSG_BURNINGSHIP_SET) 516 .AddItem(B_TRANSLATE("Tricorn"), MSG_TRICORN_SET) 517 .AddItem(B_TRANSLATE("Julia"), MSG_JULIA_SET) 518 .AddItem(B_TRANSLATE("Orbit Trap"), MSG_ORBITTRAP_SET) 519 .AddItem(B_TRANSLATE("Multibrot"), MSG_MULTIBROT_SET) 520 .End() 521 .AddMenu(B_TRANSLATE("Palette")) 522 .GetMenu(paletteMenu) 523 .AddItem(B_TRANSLATE("Royal"), MSG_ROYAL_PALETTE) 524 .AddItem(B_TRANSLATE("Deepfrost"), MSG_DEEPFROST_PALETTE) 525 .AddItem(B_TRANSLATE("Frost"), MSG_FROST_PALETTE) 526 .AddItem(B_TRANSLATE("Fire"), MSG_FIRE_PALETTE) 527 .AddItem(B_TRANSLATE("Midnight"), MSG_MIDNIGHT_PALETTE) 528 .AddItem(B_TRANSLATE("Grassland"), MSG_GRASSLAND_PALETTE) 529 .AddItem(B_TRANSLATE("Lightning"), MSG_LIGHTNING_PALETTE) 530 .AddItem(B_TRANSLATE("Spring"), MSG_SPRING_PALETTE) 531 .AddItem(B_TRANSLATE("High contrast"), MSG_HIGHCONTRAST_PALETTE) 532 .End() 533 .AddMenu(B_TRANSLATE("Iterations")) 534 .GetMenu(iterMenu) 535 .AddItem("128", MSG_ITER_128) 536 .AddItem("512", MSG_ITER_512) 537 .AddItem("1024", MSG_ITER_1024) 538 .AddItem("4096", MSG_ITER_4096) 539 .AddItem("8192", MSG_ITER_8192) 540 .AddItem("12288", MSG_ITER_12288) 541 .AddItem("16384", MSG_ITER_16384) 542 .End() 543 .AddMenu(B_TRANSLATE("Subsampling")) 544 .GetMenu(subsamplingMenu) 545 .AddItem(B_TRANSLATE("1 (none)"), MSG_SUBSAMPLING_1) 546 .AddItem("4", MSG_SUBSAMPLING_2) 547 .AddItem("9", MSG_SUBSAMPLING_3) 548 .AddItem("16", MSG_SUBSAMPLING_4) 549 .End() 550 .End(); 551 setMenu->SetRadioMode(true); 552 setMenu->FindItem(MSG_MANDELBROT_SET)->SetMarked(true); 553 paletteMenu->SetRadioMode(true); 554 paletteMenu->FindItem(MSG_ROYAL_PALETTE)->SetMarked(true); 555 iterMenu->SetRadioMode(true); 556 iterMenu->FindItem(MSG_ITER_1024)->SetMarked(true); 557 subsamplingMenu->SetRadioMode(true); 558 subsamplingMenu->FindItem(MSG_SUBSAMPLING_2)->SetMarked(true); 559 560 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 561 .SetInsets(0) 562 .Add(fMenuBar) 563 .Add(fFractalView) 564 .End(); 565 } 566 567 void 568 MandelbrotWindow::ToggleFullscreen() { 569 BRect frame; 570 fFullScreen = !fFullScreen; 571 if (fFullScreen) { 572 TRACE("Enabling fullscreen\n"); 573 BScreen screen; 574 fWindowFrame = Frame(); 575 frame = screen.Frame(); 576 frame.top -= fMenuBar->Bounds().Height() + 1; 577 578 SetFlags(Flags() | B_NOT_RESIZABLE | B_NOT_MOVABLE); 579 580 Activate(); 581 // make the window frontmost 582 } else { 583 TRACE("Disabling fullscreen\n"); 584 frame = fWindowFrame; 585 586 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_MOVABLE)); 587 } 588 589 MoveTo(frame.left, frame.top); 590 ResizeTo(frame.Width(), frame.Height()); 591 592 Layout(false); 593 } 594 595 596 #define HANDLE_SET(uiwhat, id) \ 597 case uiwhat: { \ 598 BMessage msg(FractalEngine::MSG_CHANGE_SET); \ 599 msg.AddUInt8("set", id); \ 600 fFractalView->fFractalEngine->PostMessage(&msg); \ 601 fFractalView->ResetPosition(); \ 602 fFractalView->RedrawFractal(); \ 603 break; \ 604 } 605 #define HANDLE_PALETTE(uiwhat, id) \ 606 case uiwhat: { \ 607 BMessage msg(FractalEngine::MSG_SET_PALETTE); \ 608 msg.AddUInt8("palette", id); \ 609 fFractalView->fFractalEngine->PostMessage(&msg); \ 610 fFractalView->RedrawFractal(); \ 611 break; \ 612 } 613 #define HANDLE_ITER(uiwhat, id) \ 614 case uiwhat: { \ 615 BMessage msg(FractalEngine::MSG_SET_ITERATIONS); \ 616 msg.AddUInt16("iterations", id); \ 617 fFractalView->fFractalEngine->PostMessage(&msg); \ 618 fFractalView->RedrawFractal(); \ 619 break; \ 620 } 621 #define HANDLE_SUBSAMPLING(uiwhat, id) \ 622 case uiwhat: { \ 623 BMessage msg(FractalEngine::MSG_SET_SUBSAMPLING); \ 624 msg.AddUInt8("subsampling", id); \ 625 fFractalView->fFractalEngine->PostMessage(&msg); \ 626 fFractalView->RedrawFractal(); \ 627 break; \ 628 } 629 630 631 void 632 MandelbrotWindow::DispatchMessage(BMessage* message, BHandler* target) 633 { 634 const char* bytes; 635 int32 modifierKeys; 636 if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN) 637 && message->FindString("bytes", &bytes) == B_OK 638 && message->FindInt32("modifiers", &modifierKeys) == B_OK) { 639 if (bytes[0] == B_FUNCTION_KEY) { 640 // Matches WebPositive fullscreen key (F11) 641 int32 key; 642 if (message->FindInt32("key", &key) == B_OK) { 643 switch (key) { 644 case B_F11_KEY: { 645 ToggleFullscreen(); 646 break; 647 } 648 649 default: 650 break; 651 } 652 } 653 } 654 } 655 656 BWindow::DispatchMessage(message, target); 657 } 658 659 660 void 661 MandelbrotWindow::MessageReceived(BMessage* msg) 662 { 663 switch (msg->what) { 664 HANDLE_SET(MSG_MANDELBROT_SET, 0) 665 HANDLE_SET(MSG_BURNINGSHIP_SET, 1) 666 HANDLE_SET(MSG_TRICORN_SET, 2) 667 HANDLE_SET(MSG_JULIA_SET, 3) 668 HANDLE_SET(MSG_ORBITTRAP_SET, 4) 669 HANDLE_SET(MSG_MULTIBROT_SET, 5) 670 671 HANDLE_PALETTE(MSG_ROYAL_PALETTE, 0) 672 HANDLE_PALETTE(MSG_DEEPFROST_PALETTE, 1) 673 HANDLE_PALETTE(MSG_FROST_PALETTE, 2) 674 HANDLE_PALETTE(MSG_FIRE_PALETTE, 3) 675 HANDLE_PALETTE(MSG_MIDNIGHT_PALETTE, 4) 676 HANDLE_PALETTE(MSG_GRASSLAND_PALETTE, 5) 677 HANDLE_PALETTE(MSG_LIGHTNING_PALETTE, 6) 678 HANDLE_PALETTE(MSG_SPRING_PALETTE, 7) 679 HANDLE_PALETTE(MSG_HIGHCONTRAST_PALETTE, 8) 680 681 HANDLE_ITER(MSG_ITER_128, 128) 682 HANDLE_ITER(MSG_ITER_512, 512) 683 HANDLE_ITER(MSG_ITER_1024, 1024) 684 HANDLE_ITER(MSG_ITER_4096, 4096) 685 HANDLE_ITER(MSG_ITER_8192, 8192) 686 HANDLE_ITER(MSG_ITER_12288, 12288) 687 HANDLE_ITER(MSG_ITER_16384, 16384) 688 689 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_1, 1) 690 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_2, 2) 691 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_3, 3) 692 HANDLE_SUBSAMPLING(MSG_SUBSAMPLING_4, 4) 693 694 case FractalView::MSG_START_SAVE: { 695 fFractalView->StartSave(); 696 break; 697 } 698 699 case MSG_TOGGLE_FULLSCREEN: 700 ToggleFullscreen(); 701 break; 702 703 case B_ABOUT_REQUESTED: { 704 BAboutWindow* wind = new BAboutWindow("Mandelbrot", 705 "application/x-vnd.Haiku-Mandelbrot"); 706 707 const char* authors[] = { 708 "Augustin Cavalier <waddlesplash>", 709 "kerwizzy", 710 NULL 711 }; 712 wind->AddCopyright(2016, "Haiku, Inc."); 713 wind->AddAuthors(authors); 714 wind->Show(); 715 break; 716 } 717 718 case B_KEY_DOWN: { 719 int8 val; 720 if (msg->FindInt8("byte", &val) == B_OK && val == B_ESCAPE 721 && fFullScreen) 722 ToggleFullscreen(); 723 break; 724 } 725 726 default: 727 BWindow::MessageReceived(msg); 728 break; 729 } 730 } 731 #undef HANDLE_SET 732 #undef HANDLE_PALETTE 733 #undef HANDLE_ITER 734 #undef HANDLE_SUBSAMPLING 735 736 737 bool 738 MandelbrotWindow::QuitRequested() 739 { 740 if (BWindow::QuitRequested()) { 741 be_app->PostMessage(B_QUIT_REQUESTED); 742 return true; 743 } 744 return false; 745 } 746 747 748 // #pragma mark - MandelbrotApp 749 750 751 class MandelbrotApp : public BApplication 752 { 753 public: 754 MandelbrotApp() 755 : BApplication("application/x-vnd.Haiku-Mandelbrot") {} 756 757 void ReadyToRun(); 758 bool QuitRequested() { return true; } 759 }; 760 761 762 void 763 MandelbrotApp::ReadyToRun() 764 { 765 MandelbrotWindow* wind = new MandelbrotWindow(BRect(0, 0, 640, 480)); 766 wind->CenterOnScreen(); 767 wind->Show(); 768 } 769 770 771 int 772 main(int argc, char* argv[]) 773 { 774 MandelbrotApp().Run(); 775 return 0; 776 } 777