xref: /haiku/src/add-ons/screen_savers/spider/SpiderSaver.cpp (revision 77320941256637f4b494c57104ff5a45f86a89ba)
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, minPoints
217 															+ lrand48()
218 															% (maxPoints
219 															- minPoints)),
220 									  minQueueDepth + lrand48() % (maxQueueDepth
221 									  - minQueueDepth));
222 }
223 
224 // _Cleanup
225 void
226 SpiderSaver::_Cleanup()
227 {
228 	_FreeBackBitmap();
229 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++) {
230 		delete fQueues[i];
231 		fQueues[i] = NULL;
232 	}
233 }
234 
235 // _AllocBackBitmap
236 void
237 SpiderSaver::_AllocBackBitmap(float width, float height)
238 {
239 	// sanity check
240 	if (width <= 0.0 || height <= 0.0)
241 		return;
242 
243 	BRect b(0.0, 0.0, width, height);
244 	fBackBitmap = new BBitmap(b, B_RGB32, true);
245 	if (!fBackBitmap)
246 		return;
247 	if (fBackBitmap->IsValid()) {
248 		fBackView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
249 		fBackBitmap->AddChild(fBackView);
250 		memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
251 	} else {
252 		_FreeBackBitmap();
253 		fprintf(stderr, "SpiderSaver::_AllocBackBitmap(): bitmap invalid\n");
254 	}
255 }
256 
257 // _FreeBackBitmap
258 void
259 SpiderSaver::_FreeBackBitmap()
260 {
261 	if (fBackBitmap) {
262 		delete fBackBitmap;
263 		fBackBitmap = NULL;
264 		fBackView = NULL;
265 	}
266 }
267 
268 // _DrawInto
269 void
270 SpiderSaver::_DrawInto(BView *view)
271 {
272 	for (uint32 i = 0; i < fQueueNumber; i++) {
273 		switch (fColor) {
274 			case GREEN:
275 				view->SetHighColor(1, 2, 1, 255);
276 				break;
277 			case BLUE:
278 				view->SetHighColor(1, 1, 2, 255);
279 				break;
280 			case YELLOW:
281 				view->SetHighColor(2, 2, 1, 255);
282 				break;
283 			case PURPLE:
284 				view->SetHighColor(2, 1, 2, 255);
285 				break;
286 			case CYAN:
287 				view->SetHighColor(1, 2, 2, 255);
288 				break;
289 			case GRAY:
290 				view->SetHighColor(2, 2, 2, 255);
291 				break;
292 			case RED:
293 			default:
294 				view->SetHighColor(2, 1, 1, 255);
295 				break;
296 		}
297 
298 		if (fQueues[i] == NULL)
299 			continue;
300 
301 		if (Polygon* p = fQueues[i]->Head()) {
302 			view->SetDrawingMode(B_OP_ADD);
303 			_DrawPolygon(p, view);
304 		}
305 		if (Polygon* p = fQueues[i]->Tail()) {
306 			view->SetDrawingMode(B_OP_SUBTRACT);
307 			_DrawPolygon(p, view);
308 		}
309 	}
310 }
311 
312 // _DrawPolygon
313 void
314 SpiderSaver::_DrawPolygon(Polygon* polygon, BView *view)
315 {
316 	int32 pointCount = polygon->CountPoints();
317 	if (pointCount > 1) {
318 		BPoint p = polygon->PointAt(0);
319 		view->MovePenTo(p);
320 		for (int32 i = 1; i < pointCount; i++)
321 			view->StrokeLine(polygon->PointAt(i));
322 		view->StrokeLine(p);
323 	}
324 }
325 
326 // constructor
327 SpiderView::SpiderView(BRect frame, SpiderSaver* saver,
328 					   uint32 queueNumber, uint32 maxPolyPoints,
329 					   uint32 maxQueueDepth, uint32 color)
330 	: BView(frame, "spider view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
331 	  fSaver(saver)
332 {
333 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
334 
335 	frame.OffsetTo(0.0, 0.0);
336 	frame.InsetBy(10.0, 5.0);
337 
338 	float viewHeight = floorf(frame.Height() / 5.0);
339 
340 	// title stuff
341 	font_height fh;
342 	be_bold_font->GetHeight(&fh);
343 	float fontHeight = fh.ascent + fh.descent + 5.0;
344 	frame.bottom = frame.top + fontHeight;
345 	BStringView* title = new BStringView(frame, B_EMPTY_STRING,
346 		B_TRANSLATE("Spider by stippi"));
347 	title->SetFont(be_bold_font);
348 	AddChild(title);
349 
350 	be_plain_font->GetHeight(&fh);
351 	fontHeight = fh.ascent + fh.descent + 5.0;
352 	frame.top = frame.bottom;
353 	frame.bottom = frame.top + fontHeight;
354 	title = new BStringView(frame, B_EMPTY_STRING, B_TRANSLATE("for  bonefish"));
355 	BFont font(be_plain_font);
356 	font.SetShear(110.0);
357 	title->SetFont(&font);
358 	title->SetAlignment(B_ALIGN_CENTER);
359 	AddChild(title);
360 
361 	// controls
362 	frame.top = 10.0;
363 	frame.bottom = frame.top + viewHeight;
364 	frame.OffsetBy(0.0, viewHeight);
365 	fQueueNumberS = new BSlider(frame, "queue number",
366 		B_TRANSLATE("Max. polygon count"), new BMessage(MSG_QUEUE_NUMBER),
367 		1, MAX_QUEUE_NUMBER);
368 	fQueueNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
369 	fQueueNumberS->SetHashMarkCount((MAX_QUEUE_NUMBER - 1) / 2 + 1);
370 	fQueueNumberS->SetValue(queueNumber);
371 	AddChild(fQueueNumberS);
372 	frame.OffsetBy(0.0, viewHeight);
373 	fPolyNumberS = new BSlider(frame, "poly points",
374 		B_TRANSLATE("Max. points per polygon"), new BMessage(MSG_POLY_NUMBER),
375 		MIN_POLY_POINTS, MAX_POLY_POINTS);
376 	fPolyNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
377 	fPolyNumberS->SetHashMarkCount(MAX_POLY_POINTS - MIN_POLY_POINTS + 1);
378 	fPolyNumberS->SetValue(maxPolyPoints);
379 	AddChild(fPolyNumberS);
380 	frame.OffsetBy(0.0, viewHeight);
381 	fQueueDepthS = new BSlider(frame, "queue depth", B_TRANSLATE("Trail depth"),
382 		new BMessage(MSG_QUEUE_DEPTH), MIN_QUEUE_DEPTH, MAX_QUEUE_DEPTH);
383 	fQueueDepthS->SetHashMarks(B_HASH_MARKS_BOTTOM);
384 	fQueueDepthS->SetHashMarkCount((MAX_QUEUE_DEPTH - MIN_QUEUE_DEPTH) / 4 + 1);
385 	fQueueDepthS->SetValue(maxQueueDepth);
386 	AddChild(fQueueDepthS);
387 
388 	BMenu* menu = new BMenu(B_TRANSLATE("Color"));
389 	BMessage* message = new BMessage(MSG_COLOR);
390 	message->AddInt32("color", RED);
391 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Red"), message);
392 	if (color == RED)
393 		item->SetMarked(true);
394 	menu->AddItem(item);
395 	message = new BMessage(MSG_COLOR);
396 	message->AddInt32("color", GREEN);
397 	item = new BMenuItem(B_TRANSLATE("Green"), message);
398 	if (color == GREEN)
399 		item->SetMarked(true);
400 	menu->AddItem(item);
401 	message = new BMessage(MSG_COLOR);
402 	message->AddInt32("color", BLUE);
403 	item = new BMenuItem(B_TRANSLATE("Blue"), message);
404 	if (color == BLUE)
405 		item->SetMarked(true);
406 	menu->AddItem(item);
407 	message = new BMessage(MSG_COLOR);
408 	message->AddInt32("color", YELLOW);
409 	item = new BMenuItem(B_TRANSLATE("Yellow"), message);
410 	if (color == YELLOW)
411 		item->SetMarked(true);
412 	menu->AddItem(item);
413 	message = new BMessage(MSG_COLOR);
414 	message->AddInt32("color", PURPLE);
415 	item = new BMenuItem(B_TRANSLATE("Purple"), message);
416 	if (color == PURPLE)
417 		item->SetMarked(true);
418 	menu->AddItem(item);
419 	message = new BMessage(MSG_COLOR);
420 	message->AddInt32("color", CYAN);
421 	item = new BMenuItem(B_TRANSLATE("Cyan"), message);
422 	if (color == CYAN)
423 		item->SetMarked(true);
424 	menu->AddItem(item);
425 	message = new BMessage(MSG_COLOR);
426 	message->AddInt32("color", GRAY);
427 	item = new BMenuItem(B_TRANSLATE("Gray"), message);
428 	if (color == GRAY)
429 		item->SetMarked(true);
430 	menu->AddItem(item);
431 
432 	menu->SetLabelFromMarked(true);
433 	menu->SetRadioMode(true);
434 
435 	frame.OffsetBy(0.0, viewHeight);
436 	fColorMF = new BMenuField(frame, "color", B_TRANSLATE("Color"), menu);
437 	fColorMF->SetDivider(fColorMF->StringWidth(B_TRANSLATE("Color")) + 5.0);
438 	AddChild(fColorMF);
439 }
440 
441 // destructor
442 SpiderView::~SpiderView()
443 {
444 }
445 
446 // AttachedToWindow
447 void
448 SpiderView::AttachedToWindow()
449 {
450 	fQueueNumberS->SetTarget(this);
451 	fPolyNumberS->SetTarget(this);
452 	fQueueDepthS->SetTarget(this);
453 	fColorMF->Menu()->SetTargetForItems(this);
454 }
455 
456 // MessageReceived
457 void
458 SpiderView::MessageReceived(BMessage* message)
459 {
460 	switch (message->what) {
461 		case MSG_QUEUE_NUMBER:
462 			fSaver->SetQueueNumber(fQueueNumberS->Value());
463 			break;
464 		case MSG_POLY_NUMBER:
465 			fSaver->SetPolyPoints(fPolyNumberS->Value());
466 			break;
467 		case MSG_QUEUE_DEPTH:
468 			fSaver->SetQueueDepth(fQueueDepthS->Value());
469 			break;
470 		case MSG_COLOR: {
471 			uint32 color;
472 			if (message->FindInt32("color", (int32*)&color) == B_OK)
473 				fSaver->SetColor(color);
474 			break;
475 		}
476 		default:
477 			BView::MessageReceived(message);
478 			break;
479 	}
480 }
481 
482