xref: /haiku/src/apps/mandelbrot/Mandelbrot.cpp (revision 830f67ef991407f287dbc1238aa5f5906d90c991)
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