xref: /haiku/src/add-ons/screen_savers/spider/SpiderSaver.cpp (revision 632e56d8e514ba6ac41f582ce580e51a3cd8922e)
1 /*
2  * Copyright 2007-2009, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Stephan Aßmus <superstippi@gmx.de>
7  */
8 #include "SpiderSaver.h"
9 
10 #include <math.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <Bitmap.h>
16 #include <Catalog.h>
17 #include <Message.h>
18 #include <Menu.h>
19 #include <MenuField.h>
20 #include <MenuItem.h>
21 #include <Slider.h>
22 #include <StringView.h>
23 
24 #include "Polygon.h"
25 #include "PolygonQueue.h"
26 
27 #undef B_TRANSLATION_CONTEXT
28 #define B_TRANSLATION_CONTEXT "Screensaver Spider"
29 
30 enum {
31 	MSG_QUEUE_NUMBER			= 'qunm',
32 	MSG_POLY_NUMBER				= 'plnm',
33 	MSG_QUEUE_DEPTH				= 'qudp',
34 	MSG_COLOR					= 'colr',
35 };
36 
37 #define MIN_POLY_POINTS 3
38 #define MAX_POLY_POINTS 10
39 #define MIN_QUEUE_DEPTH 40
40 #define MAX_QUEUE_DEPTH 160
41 #define MAX_QUEUE_NUMBER 40
42 
43 enum {
44 	RED							= 1,
45 	GREEN						= 2,
46 	BLUE						= 3,
47 	YELLOW						= 4,
48 	PURPLE						= 5,
49 	CYAN						= 6,
50 	GRAY						= 7,
51 };
52 
53 // MAIN INSTANTIATION FUNCTION
54 extern "C" _EXPORT BScreenSaver*
55 instantiate_screen_saver(BMessage *message, image_id image)
56 {
57 	return new SpiderSaver(message, image);
58 }
59 
60 // constructor
61 SpiderSaver::SpiderSaver(BMessage *message, image_id id)
62 	: BScreenSaver(message, id),
63 	  fBackBitmap(NULL),
64 	  fBackView(NULL),
65 	  fQueues(new PolygonQueue*[MAX_QUEUE_NUMBER]),
66 	  fQueueNumber(20),
67 	  fMaxPolyPoints(MAX_POLY_POINTS),
68 	  fMaxQueueDepth(MAX_QUEUE_DEPTH),
69 	  fColor(RED),
70 	  fPreview(false)
71 {
72 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++)
73 		fQueues[i] = NULL;
74 	if (message) {
75 		int32 value;
76 		if (message->FindInt32("queue number", &value) == B_OK)
77 			fQueueNumber = value;
78 		if (message->FindInt32("poly points", &value) == B_OK)
79 			fMaxPolyPoints = value;
80 		if (message->FindInt32("queue depth", &value) == B_OK)
81 			fMaxQueueDepth = value;
82 		if (message->FindInt32("color", &value) == B_OK)
83 			fColor = value;
84 	}
85 	srand48((long int)system_time());
86 }
87 
88 // destructor
89 SpiderSaver::~SpiderSaver()
90 {
91 	_Cleanup();
92 	delete[] fQueues;
93 }
94 
95 // StartConfig
96 void
97 SpiderSaver::StartConfig(BView *view)
98 {
99 	SpiderView* configView = new SpiderView(view->Bounds(), this,
100 		fQueueNumber, fMaxPolyPoints, fMaxQueueDepth, fColor);
101 	view->AddChild(configView);
102 }
103 
104 // StartSaver
105 status_t
106 SpiderSaver::StartSaver(BView *v, bool preview)
107 {
108 	SetTickSize(50000);
109 
110 	fPreview = preview;
111 	fBounds = v->Bounds();
112 	_Init(fBounds);
113 
114 	return B_OK;
115 }
116 
117 // StopSaver
118 void
119 SpiderSaver::StopSaver()
120 {
121 	_Cleanup();
122 }
123 
124 // Draw
125 void
126 SpiderSaver::Draw(BView *view, int32 frame)
127 {
128 	fLocker.Lock();
129 	for (uint32 i = 0; i < fQueueNumber; i++) {
130 		if (fQueues[i])
131 			fQueues[i]->Step();
132 	}
133 	if (fBackView) {
134 		if (fBackBitmap->Lock()) {
135 			_DrawInto(fBackView);
136 			fBackView->Sync();
137 			fBackBitmap->Unlock();
138 		}
139 		view->DrawBitmap(fBackBitmap, BPoint(0.0, 0.0));
140 	}
141 	fLocker.Unlock();
142 }
143 
144 // SaveState
145 status_t
146 SpiderSaver::SaveState(BMessage* into) const
147 {
148 	if (into) {
149 		into->AddInt32("queue number", (int32)fQueueNumber);
150 		into->AddInt32("poly points", (int32)fMaxPolyPoints);
151 		into->AddInt32("queue depth", (int32)fMaxQueueDepth);
152 		into->AddInt32("color", (int32)fColor);
153 		return B_OK;
154 	}
155 	return B_BAD_VALUE;
156 }
157 
158 // SetQueueNumber
159 void
160 SpiderSaver::SetQueueNumber(uint32 number)
161 {
162 	fLocker.Lock();
163 	_Cleanup();
164 	fQueueNumber = number;
165 	_Init(fBounds);
166 	fLocker.Unlock();
167 }
168 
169 // SetQueueDepth
170 void
171 SpiderSaver::SetQueueDepth(uint32 maxDepth)
172 {
173 	fLocker.Lock();
174 	_Cleanup();
175 	fMaxQueueDepth = maxDepth;
176 	_Init(fBounds);
177 	fLocker.Unlock();
178 }
179 
180 // SetPolyPoints
181 void
182 SpiderSaver::SetPolyPoints(uint32 maxPoints)
183 {
184 	fLocker.Lock();
185 	_Cleanup();
186 	fMaxPolyPoints = maxPoints;
187 	_Init(fBounds);
188 	fLocker.Unlock();
189 }
190 
191 // SetColor
192 void
193 SpiderSaver::SetColor(uint32 color)
194 {
195 	fLocker.Lock();
196 	_Cleanup();
197 	fColor = color;
198 	_Init(fBounds);
199 	fLocker.Unlock();
200 }
201 
202 // _Init
203 void
204 SpiderSaver::_Init(BRect bounds)
205 {
206 	_AllocBackBitmap(bounds.Width(), bounds.Height());
207 	uint32 minPoints = fMaxPolyPoints / 2;
208 	uint32 maxPoints = fMaxPolyPoints;
209 	uint32 minQueueDepth = fMaxQueueDepth / 2;
210 	uint32 maxQueueDepth = fMaxQueueDepth;
211 	if (fPreview) {
212 		minQueueDepth /= 4;
213 		maxQueueDepth /= 4;
214 	}
215 	for (uint32 i = 0; i < fQueueNumber; i++)
216 		fQueues[i] = new PolygonQueue(new Polygon(bounds,
217 			minPoints + lrand48() % (maxPoints - minPoints)),
218 			minQueueDepth + lrand48() % (maxQueueDepth - minQueueDepth));
219 }
220 
221 
222 // _Cleanup
223 void
224 SpiderSaver::_Cleanup()
225 {
226 	_FreeBackBitmap();
227 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++) {
228 		delete fQueues[i];
229 		fQueues[i] = NULL;
230 	}
231 }
232 
233 
234 // _AllocBackBitmap
235 void
236 SpiderSaver::_AllocBackBitmap(float width, float height)
237 {
238 	// sanity check
239 	if (width <= 0.0 || height <= 0.0)
240 		return;
241 
242 	BRect b(0.0, 0.0, width, height);
243 	fBackBitmap = new(std::nothrow) BBitmap(b, B_RGB32, true);
244 	if (!fBackBitmap)
245 		return;
246 
247 	if (fBackBitmap->IsValid()) {
248 		fBackView = new(std::nothrow) BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
249 		if (fBackView == NULL) {
250 			_FreeBackBitmap();
251 			fprintf(stderr,
252 				"SpiderSaver::_AllocBackBitmap(): view allocation failed\n");
253 			return;
254 		}
255 		fBackBitmap->AddChild(fBackView);
256 		memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
257 	} else {
258 		_FreeBackBitmap();
259 		fprintf(stderr, "SpiderSaver::_AllocBackBitmap(): bitmap invalid\n");
260 	}
261 }
262 
263 
264 // _FreeBackBitmap
265 void
266 SpiderSaver::_FreeBackBitmap()
267 {
268 	if (fBackBitmap) {
269 		delete fBackBitmap;
270 		fBackBitmap = NULL;
271 		fBackView = NULL;
272 	}
273 }
274 
275 
276 // _DrawInto
277 void
278 SpiderSaver::_DrawInto(BView *view)
279 {
280 	for (uint32 i = 0; i < fQueueNumber; i++) {
281 		switch (fColor) {
282 			case GREEN:
283 				view->SetHighColor(1, 2, 1, 255);
284 				break;
285 			case BLUE:
286 				view->SetHighColor(1, 1, 2, 255);
287 				break;
288 			case YELLOW:
289 				view->SetHighColor(2, 2, 1, 255);
290 				break;
291 			case PURPLE:
292 				view->SetHighColor(2, 1, 2, 255);
293 				break;
294 			case CYAN:
295 				view->SetHighColor(1, 2, 2, 255);
296 				break;
297 			case GRAY:
298 				view->SetHighColor(2, 2, 2, 255);
299 				break;
300 			case RED:
301 			default:
302 				view->SetHighColor(2, 1, 1, 255);
303 				break;
304 		}
305 
306 		if (fQueues[i] == NULL)
307 			continue;
308 
309 		if (Polygon* p = fQueues[i]->Head()) {
310 			view->SetDrawingMode(B_OP_ADD);
311 			_DrawPolygon(p, view);
312 		}
313 		if (Polygon* p = fQueues[i]->Tail()) {
314 			view->SetDrawingMode(B_OP_SUBTRACT);
315 			_DrawPolygon(p, view);
316 		}
317 	}
318 }
319 
320 // _DrawPolygon
321 void
322 SpiderSaver::_DrawPolygon(Polygon* polygon, BView *view)
323 {
324 	int32 pointCount = polygon->CountPoints();
325 	if (pointCount > 1) {
326 		BPoint p = polygon->PointAt(0);
327 		view->MovePenTo(p);
328 		for (int32 i = 1; i < pointCount; i++)
329 			view->StrokeLine(polygon->PointAt(i));
330 		view->StrokeLine(p);
331 	}
332 }
333 
334 // constructor
335 SpiderView::SpiderView(BRect frame, SpiderSaver* saver,
336 					   uint32 queueNumber, uint32 maxPolyPoints,
337 					   uint32 maxQueueDepth, uint32 color)
338 	: BView(frame, "spider view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
339 	  fSaver(saver)
340 {
341 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
342 
343 	frame.OffsetTo(0.0, 0.0);
344 	frame.InsetBy(10.0, 5.0);
345 
346 	float viewHeight = floorf(frame.Height() / 5.0);
347 
348 	// title stuff
349 	font_height fh;
350 	be_bold_font->GetHeight(&fh);
351 	float fontHeight = fh.ascent + fh.descent + 5.0;
352 	frame.bottom = frame.top + fontHeight;
353 	BStringView* title = new BStringView(frame, B_EMPTY_STRING,
354 		B_TRANSLATE("Spider by stippi"));
355 	title->SetFont(be_bold_font);
356 	AddChild(title);
357 
358 	be_plain_font->GetHeight(&fh);
359 	fontHeight = fh.ascent + fh.descent + 5.0;
360 	frame.top = frame.bottom;
361 	frame.bottom = frame.top + fontHeight;
362 	title = new BStringView(frame, B_EMPTY_STRING, B_TRANSLATE("for  bonefish"));
363 	BFont font(be_plain_font);
364 	font.SetShear(110.0);
365 	title->SetFont(&font);
366 	title->SetAlignment(B_ALIGN_CENTER);
367 	AddChild(title);
368 
369 	// controls
370 	frame.top = 10.0;
371 	frame.bottom = frame.top + viewHeight;
372 	frame.OffsetBy(0.0, viewHeight);
373 	fQueueNumberS = new BSlider(frame, "queue number",
374 		B_TRANSLATE("Max. polygon count"), new BMessage(MSG_QUEUE_NUMBER),
375 		1, MAX_QUEUE_NUMBER);
376 	fQueueNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
377 	fQueueNumberS->SetHashMarkCount((MAX_QUEUE_NUMBER - 1) / 2 + 1);
378 	fQueueNumberS->SetValue(queueNumber);
379 	AddChild(fQueueNumberS);
380 	frame.OffsetBy(0.0, viewHeight);
381 	fPolyNumberS = new BSlider(frame, "poly points",
382 		B_TRANSLATE("Max. points per polygon"), new BMessage(MSG_POLY_NUMBER),
383 		MIN_POLY_POINTS, MAX_POLY_POINTS);
384 	fPolyNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
385 	fPolyNumberS->SetHashMarkCount(MAX_POLY_POINTS - MIN_POLY_POINTS + 1);
386 	fPolyNumberS->SetValue(maxPolyPoints);
387 	AddChild(fPolyNumberS);
388 	frame.OffsetBy(0.0, viewHeight);
389 	fQueueDepthS = new BSlider(frame, "queue depth", B_TRANSLATE("Trail depth"),
390 		new BMessage(MSG_QUEUE_DEPTH), MIN_QUEUE_DEPTH, MAX_QUEUE_DEPTH);
391 	fQueueDepthS->SetHashMarks(B_HASH_MARKS_BOTTOM);
392 	fQueueDepthS->SetHashMarkCount((MAX_QUEUE_DEPTH - MIN_QUEUE_DEPTH) / 4 + 1);
393 	fQueueDepthS->SetValue(maxQueueDepth);
394 	AddChild(fQueueDepthS);
395 
396 	BMenu* menu = new BMenu(B_TRANSLATE("Color"));
397 	BMessage* message = new BMessage(MSG_COLOR);
398 	message->AddInt32("color", RED);
399 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Red"), message);
400 	if (color == RED)
401 		item->SetMarked(true);
402 	menu->AddItem(item);
403 	message = new BMessage(MSG_COLOR);
404 	message->AddInt32("color", GREEN);
405 	item = new BMenuItem(B_TRANSLATE("Green"), message);
406 	if (color == GREEN)
407 		item->SetMarked(true);
408 	menu->AddItem(item);
409 	message = new BMessage(MSG_COLOR);
410 	message->AddInt32("color", BLUE);
411 	item = new BMenuItem(B_TRANSLATE("Blue"), message);
412 	if (color == BLUE)
413 		item->SetMarked(true);
414 	menu->AddItem(item);
415 	message = new BMessage(MSG_COLOR);
416 	message->AddInt32("color", YELLOW);
417 	item = new BMenuItem(B_TRANSLATE("Yellow"), message);
418 	if (color == YELLOW)
419 		item->SetMarked(true);
420 	menu->AddItem(item);
421 	message = new BMessage(MSG_COLOR);
422 	message->AddInt32("color", PURPLE);
423 	item = new BMenuItem(B_TRANSLATE("Purple"), message);
424 	if (color == PURPLE)
425 		item->SetMarked(true);
426 	menu->AddItem(item);
427 	message = new BMessage(MSG_COLOR);
428 	message->AddInt32("color", CYAN);
429 	item = new BMenuItem(B_TRANSLATE("Cyan"), message);
430 	if (color == CYAN)
431 		item->SetMarked(true);
432 	menu->AddItem(item);
433 	message = new BMessage(MSG_COLOR);
434 	message->AddInt32("color", GRAY);
435 	item = new BMenuItem(B_TRANSLATE("Gray"), message);
436 	if (color == GRAY)
437 		item->SetMarked(true);
438 	menu->AddItem(item);
439 
440 	menu->SetLabelFromMarked(true);
441 	menu->SetRadioMode(true);
442 
443 	frame.OffsetBy(0.0, viewHeight);
444 	fColorMF = new BMenuField(frame, "color", B_TRANSLATE("Color"), menu);
445 	fColorMF->SetDivider(fColorMF->StringWidth(B_TRANSLATE("Color")) + 5.0);
446 	AddChild(fColorMF);
447 }
448 
449 // destructor
450 SpiderView::~SpiderView()
451 {
452 }
453 
454 // AttachedToWindow
455 void
456 SpiderView::AttachedToWindow()
457 {
458 	fQueueNumberS->SetTarget(this);
459 	fPolyNumberS->SetTarget(this);
460 	fQueueDepthS->SetTarget(this);
461 	fColorMF->Menu()->SetTargetForItems(this);
462 }
463 
464 // MessageReceived
465 void
466 SpiderView::MessageReceived(BMessage* message)
467 {
468 	switch (message->what) {
469 		case MSG_QUEUE_NUMBER:
470 			fSaver->SetQueueNumber(fQueueNumberS->Value());
471 			break;
472 		case MSG_POLY_NUMBER:
473 			fSaver->SetPolyPoints(fPolyNumberS->Value());
474 			break;
475 		case MSG_QUEUE_DEPTH:
476 			fSaver->SetQueueDepth(fQueueDepthS->Value());
477 			break;
478 		case MSG_COLOR: {
479 			uint32 color;
480 			if (message->FindInt32("color", (int32*)&color) == B_OK)
481 				fSaver->SetColor(color);
482 			break;
483 		}
484 		default:
485 			BView::MessageReceived(message);
486 			break;
487 	}
488 }
489 
490