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
52class FractalView : public BView {
53public:
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
88private:
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
108FractalView::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
124FractalView::~FractalView()
125{
126	delete fDisplayBitmap;
127}
128
129
130void FractalView::ResetPosition()
131{
132	fLocationX = 0;
133	fLocationY = 0;
134	fSize = 0.005;
135}
136
137
138void FractalView::AttachedToWindow()
139{
140	fFractalEngine = new FractalEngine(this, Window());
141	fFractalEngine->Run();
142	TRACE("Attached to window\n");
143}
144
145
146void FractalView::FrameResized(float, float)
147{
148	TRACE("Frame Resize\n");
149	UpdateSize();
150}
151
152
153void 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
172void 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
182BRect 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
205void FractalView::MouseDown(BPoint where)
206{
207	fSelecting = true;
208	fSelectStart = where;
209	fMouseButtons = 0;
210	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&fMouseButtons);
211}
212
213
214void FractalView::MouseMoved(BPoint where, uint32 mode, const BMessage*)
215{
216	if (fSelecting) {
217		fSelectEnd = where;
218		Invalidate();
219	}
220}
221
222
223void 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
233void 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
244void 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
260void 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
285void 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
342void FractalView::Pulse()
343{
344	ImportBitsAndInvalidate();
345}
346
347
348void 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
361void 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
372void FractalView::Draw(BRect updateRect)
373{
374	DrawBitmap(fDisplayBitmap, updateRect, updateRect);
375	if (fSelecting)
376		StrokeRect(GetDragFrame());
377}
378
379
380void 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
396void 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
423void FractalView::EndSave()
424{
425	fSaving = false;
426	ImportBitsAndInvalidate();
427}
428
429
430// #pragma mark - MandelbrotWindow
431
432
433class MandelbrotWindow : public BWindow
434{
435public:
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
483private:
484		FractalView* fFractalView;
485};
486
487
488MandelbrotWindow::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
567void
568MandelbrotWindow::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
631void
632MandelbrotWindow::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
660void
661MandelbrotWindow::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
737bool
738MandelbrotWindow::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
751class MandelbrotApp : public BApplication
752{
753public:
754				MandelbrotApp()
755					: BApplication("application/x-vnd.Haiku-Mandelbrot") {}
756
757		void	ReadyToRun();
758		bool	QuitRequested() { return true; }
759};
760
761
762void
763MandelbrotApp::ReadyToRun()
764{
765	MandelbrotWindow* wind = new MandelbrotWindow(BRect(0, 0, 640, 480));
766	wind->CenterOnScreen();
767	wind->Show();
768}
769
770
771int
772main(int argc, char* argv[])
773{
774	MandelbrotApp().Run();
775	return 0;
776}
777