xref: /haiku/src/add-ons/screen_savers/spider/SpiderSaver.cpp (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
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 {
71 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++)
72 		fQueues[i] = NULL;
73 	if (message) {
74 		int32 value;
75 		if (message->FindInt32("queue number", &value) == B_OK)
76 			fQueueNumber = value;
77 		if (message->FindInt32("poly points", &value) == B_OK)
78 			fMaxPolyPoints = value;
79 		if (message->FindInt32("queue depth", &value) == B_OK)
80 			fMaxQueueDepth = value;
81 		if (message->FindInt32("color", &value) == B_OK)
82 			fColor = value;
83 	}
84 	srand48((long int)system_time());
85 }
86 
87 // destructor
88 SpiderSaver::~SpiderSaver()
89 {
90 	_Cleanup();
91 	delete[] fQueues;
92 }
93 
94 // StartConfig
95 void
96 SpiderSaver::StartConfig(BView *view)
97 {
98 	SpiderView* configView = new SpiderView(view->Bounds(), this,
99 		fQueueNumber, fMaxPolyPoints, fMaxQueueDepth, fColor);
100 	view->AddChild(configView);
101 }
102 
103 // StartSaver
104 status_t
105 SpiderSaver::StartSaver(BView *v, bool preview)
106 {
107 	SetTickSize(50000);
108 
109 	fPreview = preview;
110 	fBounds = v->Bounds();
111 	_Init(fBounds);
112 
113 	return B_OK;
114 }
115 
116 // StopSaver
117 void
118 SpiderSaver::StopSaver()
119 {
120 	_Cleanup();
121 }
122 
123 // Draw
124 void
125 SpiderSaver::Draw(BView *view, int32 frame)
126 {
127 	fLocker.Lock();
128 	for (uint32 i = 0; i < fQueueNumber; i++) {
129 		if (fQueues[i])
130 			fQueues[i]->Step();
131 	}
132 	if (fBackView) {
133 		if (fBackBitmap->Lock()) {
134 			_DrawInto(fBackView);
135 			fBackView->Sync();
136 			fBackBitmap->Unlock();
137 		}
138 		view->DrawBitmap(fBackBitmap, BPoint(0.0, 0.0));
139 	}
140 	fLocker.Unlock();
141 }
142 
143 // SaveState
144 status_t
145 SpiderSaver::SaveState(BMessage* into) const
146 {
147 	if (into) {
148 		into->AddInt32("queue number", (int32)fQueueNumber);
149 		into->AddInt32("poly points", (int32)fMaxPolyPoints);
150 		into->AddInt32("queue depth", (int32)fMaxQueueDepth);
151 		into->AddInt32("color", (int32)fColor);
152 		return B_OK;
153 	}
154 	return B_BAD_VALUE;
155 }
156 
157 // SetQueueNumber
158 void
159 SpiderSaver::SetQueueNumber(uint32 number)
160 {
161 	fLocker.Lock();
162 	_Cleanup();
163 	fQueueNumber = number;
164 	_Init(fBounds);
165 	fLocker.Unlock();
166 }
167 
168 // SetQueueDepth
169 void
170 SpiderSaver::SetQueueDepth(uint32 maxDepth)
171 {
172 	fLocker.Lock();
173 	_Cleanup();
174 	fMaxQueueDepth = maxDepth;
175 	_Init(fBounds);
176 	fLocker.Unlock();
177 }
178 
179 // SetPolyPoints
180 void
181 SpiderSaver::SetPolyPoints(uint32 maxPoints)
182 {
183 	fLocker.Lock();
184 	_Cleanup();
185 	fMaxPolyPoints = maxPoints;
186 	_Init(fBounds);
187 	fLocker.Unlock();
188 }
189 
190 // SetColor
191 void
192 SpiderSaver::SetColor(uint32 color)
193 {
194 	fLocker.Lock();
195 	_Cleanup();
196 	fColor = color;
197 	_Init(fBounds);
198 	fLocker.Unlock();
199 }
200 
201 // _Init
202 void
203 SpiderSaver::_Init(BRect bounds)
204 {
205 	_AllocBackBitmap(bounds.Width(), bounds.Height());
206 	uint32 minPoints = fMaxPolyPoints / 2;
207 	uint32 maxPoints = fMaxPolyPoints;
208 	uint32 minQueueDepth = fMaxQueueDepth / 2;
209 	uint32 maxQueueDepth = fMaxQueueDepth;
210 	if (fPreview) {
211 		minQueueDepth /= 4;
212 		maxQueueDepth /= 4;
213 	}
214 	for (uint32 i = 0; i < fQueueNumber; i++)
215 		fQueues[i] = new PolygonQueue(new Polygon(bounds, minPoints
216 															+ lrand48()
217 															% (maxPoints
218 															- minPoints)),
219 									  minQueueDepth + lrand48() % (maxQueueDepth
220 									  - minQueueDepth));
221 }
222 
223 // _Cleanup
224 void
225 SpiderSaver::_Cleanup()
226 {
227 	_FreeBackBitmap();
228 	for (int32 i = 0; i < MAX_QUEUE_NUMBER; i++) {
229 		delete fQueues[i];
230 		fQueues[i] = NULL;
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 BBitmap(b, B_RGB32, true);
244 	if (!fBackBitmap)
245 		return;
246 	if (fBackBitmap->IsValid()) {
247 		fBackView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
248 		fBackBitmap->AddChild(fBackView);
249 		memset(fBackBitmap->Bits(), 0, fBackBitmap->BitsLength());
250 	} else {
251 		_FreeBackBitmap();
252 		fprintf(stderr, "SpiderSaver::_AllocBackBitmap(): bitmap invalid\n");
253 	}
254 }
255 
256 // _FreeBackBitmap
257 void
258 SpiderSaver::_FreeBackBitmap()
259 {
260 	if (fBackBitmap) {
261 		delete fBackBitmap;
262 		fBackBitmap = NULL;
263 		fBackView = NULL;
264 	}
265 }
266 
267 // _DrawInto
268 void
269 SpiderSaver::_DrawInto(BView *view)
270 {
271 	for (uint32 i = 0; i < fQueueNumber; i++) {
272 		switch (fColor) {
273 			case GREEN:
274 				view->SetHighColor(1, 2, 1, 255);
275 				break;
276 			case BLUE:
277 				view->SetHighColor(1, 1, 2, 255);
278 				break;
279 			case YELLOW:
280 				view->SetHighColor(2, 2, 1, 255);
281 				break;
282 			case PURPLE:
283 				view->SetHighColor(2, 1, 2, 255);
284 				break;
285 			case CYAN:
286 				view->SetHighColor(1, 2, 2, 255);
287 				break;
288 			case GRAY:
289 				view->SetHighColor(2, 2, 2, 255);
290 				break;
291 			case RED:
292 			default:
293 				view->SetHighColor(2, 1, 1, 255);
294 				break;
295 		}
296 		if (Polygon* p = fQueues[i]->Head()) {
297 			view->SetDrawingMode(B_OP_ADD);
298 			_DrawPolygon(p, view);
299 		}
300 		if (Polygon* p = fQueues[i]->Tail()) {
301 			view->SetDrawingMode(B_OP_SUBTRACT);
302 			_DrawPolygon(p, view);
303 		}
304 	}
305 }
306 
307 // _DrawPolygon
308 void
309 SpiderSaver::_DrawPolygon(Polygon* polygon, BView *view)
310 {
311 	int32 pointCount = polygon->CountPoints();
312 	if (pointCount > 1) {
313 		BPoint p = polygon->PointAt(0);
314 		view->MovePenTo(p);
315 		for (int32 i = 1; i < pointCount; i++)
316 			view->StrokeLine(polygon->PointAt(i));
317 		view->StrokeLine(p);
318 	}
319 }
320 
321 // constructor
322 SpiderView::SpiderView(BRect frame, SpiderSaver* saver,
323 					   uint32 queueNumber, uint32 maxPolyPoints,
324 					   uint32 maxQueueDepth, uint32 color)
325 	: BView(frame, "spider view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
326 	  fSaver(saver)
327 {
328 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
329 
330 	frame.OffsetTo(0.0, 0.0);
331 	frame.InsetBy(10.0, 5.0);
332 
333 	float viewHeight = floorf(frame.Height() / 5.0);
334 
335 	// title stuff
336 	font_height fh;
337 	be_bold_font->GetHeight(&fh);
338 	float fontHeight = fh.ascent + fh.descent + 5.0;
339 	frame.bottom = frame.top + fontHeight;
340 	BStringView* title = new BStringView(frame, B_EMPTY_STRING,
341 		B_TRANSLATE("Spider by stippi"));
342 	title->SetFont(be_bold_font);
343 	AddChild(title);
344 
345 	be_plain_font->GetHeight(&fh);
346 	fontHeight = fh.ascent + fh.descent + 5.0;
347 	frame.top = frame.bottom;
348 	frame.bottom = frame.top + fontHeight;
349 	title = new BStringView(frame, B_EMPTY_STRING, B_TRANSLATE("for  bonefish"));
350 	BFont font(be_plain_font);
351 	font.SetShear(110.0);
352 	title->SetFont(&font);
353 	title->SetAlignment(B_ALIGN_CENTER);
354 	AddChild(title);
355 
356 	// controls
357 	frame.top = 10.0;
358 	frame.bottom = frame.top + viewHeight;
359 	frame.OffsetBy(0.0, viewHeight);
360 	fQueueNumberS = new BSlider(frame, "queue number",
361 		B_TRANSLATE("Max. polygon count"), new BMessage(MSG_QUEUE_NUMBER),
362 		1, MAX_QUEUE_NUMBER);
363 	fQueueNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
364 	fQueueNumberS->SetHashMarkCount((MAX_QUEUE_NUMBER - 1) / 2 + 1);
365 	fQueueNumberS->SetValue(queueNumber);
366 	AddChild(fQueueNumberS);
367 	frame.OffsetBy(0.0, viewHeight);
368 	fPolyNumberS = new BSlider(frame, "poly points",
369 		B_TRANSLATE("Max. points per polygon"), new BMessage(MSG_POLY_NUMBER),
370 		MIN_POLY_POINTS, MAX_POLY_POINTS);
371 	fPolyNumberS->SetHashMarks(B_HASH_MARKS_BOTTOM);
372 	fPolyNumberS->SetHashMarkCount(MAX_POLY_POINTS - MIN_POLY_POINTS + 1);
373 	fPolyNumberS->SetValue(maxPolyPoints);
374 	AddChild(fPolyNumberS);
375 	frame.OffsetBy(0.0, viewHeight);
376 	fQueueDepthS = new BSlider(frame, "queue depth", B_TRANSLATE("Trail depth"),
377 		new BMessage(MSG_QUEUE_DEPTH), MIN_QUEUE_DEPTH, MAX_QUEUE_DEPTH);
378 	fQueueDepthS->SetHashMarks(B_HASH_MARKS_BOTTOM);
379 	fQueueDepthS->SetHashMarkCount((MAX_QUEUE_DEPTH - MIN_QUEUE_DEPTH) / 4 + 1);
380 	fQueueDepthS->SetValue(maxQueueDepth);
381 	AddChild(fQueueDepthS);
382 
383 	BMenu* menu = new BMenu(B_TRANSLATE("Color"));
384 	BMessage* message = new BMessage(MSG_COLOR);
385 	message->AddInt32("color", RED);
386 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Red"), message);
387 	if (color == RED)
388 		item->SetMarked(true);
389 	menu->AddItem(item);
390 	message = new BMessage(MSG_COLOR);
391 	message->AddInt32("color", GREEN);
392 	item = new BMenuItem(B_TRANSLATE("Green"), message);
393 	if (color == GREEN)
394 		item->SetMarked(true);
395 	menu->AddItem(item);
396 	message = new BMessage(MSG_COLOR);
397 	message->AddInt32("color", BLUE);
398 	item = new BMenuItem(B_TRANSLATE("Blue"), message);
399 	if (color == BLUE)
400 		item->SetMarked(true);
401 	menu->AddItem(item);
402 	message = new BMessage(MSG_COLOR);
403 	message->AddInt32("color", YELLOW);
404 	item = new BMenuItem(B_TRANSLATE("Yellow"), message);
405 	if (color == YELLOW)
406 		item->SetMarked(true);
407 	menu->AddItem(item);
408 	message = new BMessage(MSG_COLOR);
409 	message->AddInt32("color", PURPLE);
410 	item = new BMenuItem(B_TRANSLATE("Purple"), message);
411 	if (color == PURPLE)
412 		item->SetMarked(true);
413 	menu->AddItem(item);
414 	message = new BMessage(MSG_COLOR);
415 	message->AddInt32("color", CYAN);
416 	item = new BMenuItem(B_TRANSLATE("Cyan"), message);
417 	if (color == CYAN)
418 		item->SetMarked(true);
419 	menu->AddItem(item);
420 	message = new BMessage(MSG_COLOR);
421 	message->AddInt32("color", GRAY);
422 	item = new BMenuItem(B_TRANSLATE("Gray"), message);
423 	if (color == GRAY)
424 		item->SetMarked(true);
425 	menu->AddItem(item);
426 
427 	menu->SetLabelFromMarked(true);
428 	menu->SetRadioMode(true);
429 
430 	frame.OffsetBy(0.0, viewHeight);
431 	fColorMF = new BMenuField(frame, "color", B_TRANSLATE("Color"), menu);
432 	fColorMF->SetDivider(fColorMF->StringWidth(B_TRANSLATE("Color")) + 5.0);
433 	AddChild(fColorMF);
434 }
435 
436 // destructor
437 SpiderView::~SpiderView()
438 {
439 }
440 
441 // AttachedToWindow
442 void
443 SpiderView::AttachedToWindow()
444 {
445 	fQueueNumberS->SetTarget(this);
446 	fPolyNumberS->SetTarget(this);
447 	fQueueDepthS->SetTarget(this);
448 	fColorMF->Menu()->SetTargetForItems(this);
449 }
450 
451 // MessageReceived
452 void
453 SpiderView::MessageReceived(BMessage* message)
454 {
455 	switch (message->what) {
456 		case MSG_QUEUE_NUMBER:
457 			fSaver->SetQueueNumber(fQueueNumberS->Value());
458 			break;
459 		case MSG_POLY_NUMBER:
460 			fSaver->SetPolyPoints(fPolyNumberS->Value());
461 			break;
462 		case MSG_QUEUE_DEPTH:
463 			fSaver->SetQueueDepth(fQueueDepthS->Value());
464 			break;
465 		case MSG_COLOR: {
466 			uint32 color;
467 			if (message->FindInt32("color", (int32*)&color) == B_OK)
468 				fSaver->SetColor(color);
469 			break;
470 		}
471 		default:
472 			BView::MessageReceived(message);
473 			break;
474 	}
475 }
476 
477