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