1 #include <Application.h> 2 #include <WindowScreen.h> 3 #include <Screen.h> 4 #include <string.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <math.h> 8 #include <SupportDefs.h> // min_c() and max_c() 9 10 #ifdef DEBUGGING 11 #define PRINT(x) printf x 12 #else 13 #define PRINT(x) 14 #endif 15 16 // macros 17 #define set_pixel(x,y,color) (frame_buffer[x + (line_length*y)] = color) 18 #define get_pixel(x,y) (frame_buffer[x + (line_length*y)]) 19 20 class NApplication : public BApplication { 21 public: 22 NApplication(); 23 bool is_quitting; // So that the WindowScreen knows what 24 // to do when disconnected. 25 private: 26 bool QuitRequested(); 27 void ReadyToRun(); 28 }; 29 30 class NWindowScreen : public BWindowScreen { 31 public: 32 NWindowScreen(status_t*); 33 private: 34 void ScreenConnected(bool); 35 long MyCode(); 36 static long Entry(void*); 37 // handy stuff 38 void set_frame_rate(float fps) {frame_pause = (bigtime_t)((1000 * 1000)/fps);} 39 // special for demos 40 enum { 41 // used for star->last_draw 42 INVALID = 0x7fffffff 43 }; 44 typedef struct { 45 float init_velocity; 46 float gravity; 47 double cos_z_theta; 48 int32 y; 49 int32 x; 50 int32 timeval; 51 uint32 last_draw; 52 int32 lx,ly; 53 } particle; 54 uint32 particle_count; 55 particle *particle_list; 56 // simple raster functions 57 void draw_line(int x1, int y1, int x2, int y2, int color); 58 void draw_rect(int x, int y, int w, int h, int color); 59 void fill_rect(int x, int y, int w, int h, int color); 60 void draw_ellipse(int cx, int cy, int wide, int deep, int color); 61 void fill_ellipse(int x, int y, int xradius, int yradius, int color); 62 void ellipse_points(int x, int y, int x_offset, int y_offset, int color); 63 void ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color); 64 thread_id tid; 65 sem_id sem; 66 area_id area; 67 uint8* save_buffer; 68 uint8* frame_buffer; 69 ulong line_length; 70 bigtime_t frame_pause; // time between frames 71 int width,height; 72 int COLORS; 73 bool thread_is_locked; // small hack to allow to quit the 74 // app from ScreenConnected() 75 }; 76 77 main() 78 { 79 NApplication app; 80 } 81 82 NApplication::NApplication() 83 :BApplication("application/x-vnd.Prok-DemoTemplate") 84 { 85 Run(); // see you in ReadyToRun() 86 } 87 88 void NApplication::ReadyToRun() 89 { 90 PRINT(("ReadyToRun()\n")); 91 status_t ret = B_ERROR; 92 is_quitting = false; 93 NWindowScreen *ws = new NWindowScreen(&ret); 94 PRINT(("WindowScreen ctor returned. ret = %s\n", strerror(ret))); 95 // exit if constructing the WindowScreen failed. 96 if((ws == NULL) || (ret < B_OK)) 97 { 98 //printf("the window screen was NULL, or there was an error\n"); 99 PostMessage(B_QUIT_REQUESTED); 100 } 101 else 102 PRINT(("everything's just peachy. done with ReadyToRun().\n")); 103 } 104 105 bool NApplication::QuitRequested() 106 { 107 PRINT(("QuitRequested()\n")); 108 status_t ret; 109 is_quitting = true; 110 wait_for_thread(find_thread("rendering thread"), &ret); // wait for the render thread to finish 111 return true; 112 } 113 114 NWindowScreen::NWindowScreen(status_t *ret) 115 : BWindowScreen("Example", B_8_BIT_640x480, ret), width(640), height(480), COLORS(256) 116 { 117 PRINT(("WindowScreen ctor.\n")); 118 thread_is_locked = true; 119 tid = 0; 120 if(*ret == B_OK) 121 { 122 PRINT(("creating blocking sem and save_buffer area.\n")); 123 // this semaphore controls the access to the WindowScreen 124 sem = create_sem(0,"WindowScreen Access"); 125 // this area is used to save the whole framebuffer when 126 // switching workspaces. (better than malloc()). 127 area = create_area("save", (void**)&save_buffer, B_ANY_ADDRESS, 640*480, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA); 128 // exit if an error occured. 129 if((sem < B_OK) || (area < B_OK)) 130 { 131 PRINT(("create_area() or create_sem() failed\n")); 132 *ret = B_ERROR; 133 } 134 else 135 { 136 PRINT(("calling Show().\n")); 137 Show(); // let's go. See you in ScreenConnected. 138 } 139 } 140 else 141 { 142 PRINT(("BWindowScreen base class ctor returned failure\n")); 143 be_app->PostMessage(B_QUIT_REQUESTED); 144 } 145 // set the frame rate 146 set_frame_rate(30.); 147 } 148 149 void NWindowScreen::ScreenConnected(bool connected) 150 { 151 PRINT(("ScreenConnected()\n")); 152 fflush(stdout); 153 if(connected) 154 { 155 if(SetSpace(B_8_BIT_640x480) < B_OK) 156 { 157 SetFrameBuffer(640,480); 158 PRINT(("SetSpace() failed\n")); 159 // properly set the framebuffer. exit if an error occurs. 160 be_app->PostMessage(B_QUIT_REQUESTED); 161 return; 162 } 163 // get the framebuffer-related info, each time the 164 // WindowScreen is connected (multiple monitor) 165 frame_buffer = (uint8*)(CardInfo()->frame_buffer); 166 line_length = FrameBufferInfo()->bytes_per_row; 167 if(tid == 0) 168 { 169 // clean the framebuffer 170 PRINT(("zeroing the framebuffer\n")); 171 memset(frame_buffer,0,480*line_length); 172 // spawn the rendering thread. exit if an error occurs. 173 PRINT(("spawning the render thread.\n")); 174 tid = spawn_thread(Entry,"rendering thread", B_URGENT_DISPLAY_PRIORITY,this); 175 if(resume_thread(tid) < B_OK) 176 { 177 be_app->PostMessage(B_QUIT_REQUESTED); 178 return; 179 } 180 } 181 else 182 { 183 for(int y=0;y<480;y++) 184 { 185 // restore the framebuffer when switching back from 186 // another workspace. 187 memcpy(frame_buffer+y*line_length,save_buffer+640*y,640); 188 } 189 } 190 // set our color list. 191 rgb_color palette[256]; 192 rgb_color c1; 193 for(int i=0,j=0;i<256;i++,j++) 194 { 195 if(i<64) 196 { 197 c1.red = j*4; // greys 198 c1.green = j*4; 199 c1.blue = j*4; 200 c1.alpha = 255; 201 } 202 if((i>=64) && (i<128)) 203 { 204 c1.red = j*4; // reds 205 c1.green = 0; 206 c1.blue = 0; 207 c1.alpha = 255; 208 } 209 if((i>=128) && (i<192)) 210 { 211 c1.red = 0; // greens 212 c1.green = j*4; 213 c1.blue = 0; 214 c1.alpha = 255; 215 } 216 if((i>=192) && (i<256)) 217 { 218 c1.red = 0i; // blues 219 c1.green = 0; 220 c1.blue = j*4; 221 c1.alpha = 255; 222 } 223 if(j == 64) 224 j=0; 225 palette[i]=c1; 226 } 227 SetColorList(palette); 228 229 // allow the rendering thread to run. 230 thread_is_locked = false; 231 release_sem(sem); 232 } 233 else /* !connected */ 234 { 235 // block the rendering thread. 236 if(!thread_is_locked) 237 { 238 acquire_sem(sem); 239 thread_is_locked = true; 240 } 241 // kill the rendering and clean up when quitting 242 if((((NApplication*)be_app)->is_quitting)) 243 { 244 status_t ret; 245 kill_thread(tid); 246 wait_for_thread(tid,&ret); 247 delete_sem(sem); 248 delete_area(area); 249 free(particle_list); 250 } 251 else 252 { 253 // set the color list black so that the screen doesn't seem 254 // to freeze while saving the framebuffer 255 rgb_color c={0,0,0,255}; 256 rgb_color palette[256]; 257 // build the palette 258 for(int i=0;i<256;i++) 259 palette[i] = c; 260 // set the palette 261 SetColorList(palette); 262 // save the framebuffer 263 for(int y=0;y<480;y++) 264 memcpy(save_buffer+640*y,frame_buffer+y*line_length,640); 265 } 266 } 267 } 268 269 long NWindowScreen::Entry(void* p) { 270 return ((NWindowScreen*)p)->MyCode(); 271 } 272 273 long NWindowScreen::MyCode() 274 { 275 bigtime_t trgt = system_time() + frame_pause; 276 srandom(system_time()); 277 // beforehand stuff 278 particle_count = 1024*2; 279 particle_list = (particle *)malloc(sizeof(particle)*particle_count); 280 for (uint32 i=0; i<particle_count; i++) 281 { 282 uint32 rand_max = 0xffffffff; 283 particle_list[i].init_velocity = -((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max)*3.333; // magic number 284 particle_list[i].gravity = -(((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max))*0.599; // more magic 285 286 // make the particle initialy invisible and fixed, but at a random moment in time 287 particle_list[i].lx = 0; 288 particle_list[i].ly = 0; 289 particle_list[i].last_draw = INVALID; 290 particle_list[i].timeval = random() & 64; 291 particle_list[i].x = 0; // this gets figured out at drawtime 292 particle_list[i].y = 0; // same here 293 particle_list[i].cos_z_theta = cos(random() % 360); // grab an angle 294 } 295 296 297 // the loop o' fun 298 while(!(((NApplication*)be_app)->is_quitting)) 299 { 300 // try to sync with the vertical retrace 301 if(BScreen(this).WaitForRetrace() != B_OK) 302 { 303 // snoze for a bit so that other threads can be happy. 304 // We are realtime priority you know 305 if(system_time() < trgt) 306 snooze(trgt - system_time()); 307 trgt = system_time() + frame_pause; 308 } 309 310 // gain access to the framebuffer before writing to it. 311 acquire_sem(sem); // block until we're allowed to own the framebuffer 312 313 /////////////////////////////// 314 // do neat stuff here // 315 ////////////////////////////// 316 PRINT(("rendering a frame.\n")); 317 318 319 // eye candy VII - particles! - my own cookin 320 particle *s; 321 int32 x, y, cx,cy; 322 set_frame_rate(60.); // woo. ntsc 323 // calculate the center 324 cx = width/2; 325 cy = height/2; 326 327 // palette test 328 //set_frame_rate(0.1); 329 //for(int i=0;i<256;i++) 330 // draw_line(i,0,i,height, i); 331 332 PRINT(("Starting particle drawing loop\n")); 333 s = particle_list; 334 for (uint32 i=0; i<particle_count; i++) 335 { 336 PRINT(("drawing particle %d\r", i)); 337 338 // save the old position 339 s->lx = s->x; 340 s->ly = s->y; 341 342 PRINT(("cx=%d, cy=%d\n", cx,cy)); 343 344 // move the particle 345 // find y and x 346 // (s->gravity/2)*(s->timeval*s->timeval) * 1.85 is magic 347 y = s->y = (int32)(cy + (int32)((s->gravity/2)*(s->timeval*s->timeval)*1.94) + ((s->init_velocity - (s->gravity*s->timeval)) * s->timeval)); 348 x = s->x = (int32)(cx + (int32)(s->timeval * s->cos_z_theta)); // 3d rotation 349 350 // interate timeval 351 s->timeval++; 352 353 // sanity check 354 if(x <= 0) 355 goto erase_and_reset; 356 if(x > width) 357 goto erase_and_reset; 358 if(y < 0) 359 goto erase; // invisible + erase last position 360 if(y > height) 361 goto erase_and_reset; 362 363 // erase the previous position, if necessary 364 if (s->last_draw != INVALID) 365 set_pixel(s->lx,s->ly,0); 366 367 // if it's visible, then draw it. 368 set_pixel(s->x,s->y, 169); 369 s->last_draw = 1; 370 goto loop; 371 372 erase_and_reset: 373 if((s->lx <= width) && (s->lx >= 0) && (s->ly <= height) && (s->ly >= 0)) 374 set_pixel(s->lx, s->ly,0); 375 s->x = 0; 376 s->y = 0; 377 s->lx = 0; 378 s->ly = 0; 379 s->timeval = 0; 380 s->last_draw = INVALID; 381 goto loop; 382 383 erase: 384 // erase it. 385 if(s->last_draw != INVALID) 386 set_pixel(s->lx, s->ly,0); 387 s->lx = s->x; 388 s->ly = s->y; 389 s->last_draw = INVALID; 390 loop: 391 s++; 392 //printf("end draw loop\n"); 393 } 394 PRINT(("frame done\n")); 395 396 ////////////////////////////////// 397 // stop doing neat stuff // 398 ///////////////////////////////// 399 400 // release the semaphore while waiting. gotta release it 401 // at some point or nasty things will happen! 402 release_sem(sem); 403 // loop for another frame! 404 } 405 return B_OK; 406 } 407 408 ////////////////////////////// 409 // Misc - a place for demos to put their convenience functions 410 ////////////////////////////// 411 412 413 ////////////////////////////// 414 // My Silly Raster Lib 415 ////////////////////////////// 416 417 /* 418 419 Functions: 420 void draw_line(int x1, int y1, int x2, int y2, int color); 421 void draw_rect(int x, int y, int w, int h, int color); 422 void fill_rect(int x, int y, int w, int h, int color); 423 void draw_ellipse(int x, int y, int xradius, int yradius, int color); 424 void fill_ellipse(int x, int y, int xradius, int yradius, int color); 425 426 */ 427 428 void NWindowScreen::draw_line(int x1, int y1, int x2, int y2, int color) 429 { 430 // Simple Bresenham's line drawing algorithm 431 int d,x,y,ax,ay,sx,sy,dx,dy; 432 433 #define ABS(x) (((x)<0) ? -(x) : (x)) 434 #define SGN(x) (((x)<0) ? -1 : 1) 435 436 dx=x2-x1; ax=ABS(dx)<<1; sx=SGN(dx); 437 dy=y2-y1; ay=ABS(dy)<<1; sy=SGN(dy); 438 439 x=x1; 440 y=y1; 441 if(ax>ay) 442 { 443 d=ay-(ax>>1); 444 for(;;) 445 { 446 set_pixel(x,y,color); 447 if(x==x2) return; 448 if(d>=0) 449 { 450 y+=sy; 451 d-=ax; 452 } 453 x+=sx; 454 d+=ay; 455 } 456 } 457 else 458 { 459 d=ax-(ay>>1); 460 for(;;) 461 { 462 set_pixel(x,y,color); 463 if(y==y2) return; 464 if(d>=0) 465 { 466 x+=sx; 467 d-=ay; 468 } 469 y+=sy; 470 d+=ax; 471 } 472 } 473 } 474 475 void NWindowScreen::draw_rect(int x, int y, int w, int h, int color) 476 { 477 draw_line(x,y,x+w,y,color); 478 draw_line(x,y,x,y+h,color); 479 draw_line(x,y+h,x+w,y+h,color); 480 draw_line(x+w,y,x+w,y+h,color); 481 } 482 483 void NWindowScreen::fill_rect(int x, int y, int w, int h, int color) 484 { 485 for(int i=0;i<w;i++) 486 for(int j=0;j<h;j++) 487 set_pixel(i,j,color); 488 } 489 490 void NWindowScreen::draw_ellipse(int cx, int cy, int wide, int deep, int color) 491 { 492 // if we're asked to draw a really small ellipse, put a single pixel in the buffer 493 // and bail 494 if((wide < 1) || (deep < 1)) 495 { 496 set_pixel(cx,cy,color); 497 return; 498 } 499 500 // MidPoint Ellipse algorithm. 501 // page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book) 502 int16 x, y; 503 int16 wide_squared, deep_squared; 504 double d; 505 506 x = 0; 507 y = deep; 508 wide_squared = wide * wide; 509 deep_squared = deep * deep; 510 d = deep_squared - (wide_squared*deep) + (wide_squared/4); 511 512 ellipse_points(x, y, cx, cy, color); 513 while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1))) 514 { 515 if(d < 0) 516 d += deep_squared*(2*x + 3); 517 else 518 { 519 d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2); 520 y--; 521 } 522 x++; 523 ellipse_points(x, y, cx, cy, color); 524 } 525 526 d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared; 527 while(y > 0) 528 { 529 if(d < 0) 530 { 531 d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3); 532 x++; 533 } 534 else 535 d += wide_squared*(-2*y + 3); 536 y--; 537 ellipse_points(x, y, cx, cy, color); 538 } 539 } 540 541 void NWindowScreen::fill_ellipse(int cx, int cy, int wide, int deep, int color) 542 { 543 // if we're asked to draw a really small ellipse, put a single pixel in the buffer 544 // and bail 545 if((wide < 1) || (deep < 1)) 546 { 547 set_pixel(cx,cy,color); 548 return; 549 } 550 551 // MidPoint Ellipse algorithm. 552 // page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book) 553 int16 x, y; 554 int16 wide_squared, deep_squared; 555 double d; 556 557 x = 0; 558 y = deep; 559 wide_squared = wide * wide; 560 deep_squared = deep * deep; 561 d = deep_squared - (wide_squared*deep) + (wide_squared/4); 562 563 ellipse_fill_points(x, y, cx, cy, color); 564 while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1))) 565 { 566 if(d < 0) 567 d += deep_squared*(2*x + 3); 568 else 569 { 570 d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2); 571 y--; 572 } 573 x++; 574 ellipse_fill_points(x, y, cx, cy, color); 575 } 576 577 d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared; 578 while(y > 0) 579 { 580 if(d < 0) 581 { 582 d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3); 583 x++; 584 } 585 else 586 d += wide_squared*(-2*y + 3); 587 y--; 588 ellipse_fill_points(x, y, cx, cy, color); 589 } 590 } 591 592 void NWindowScreen::ellipse_points(int x, int y, int x_offset, int y_offset, int color) 593 { 594 // fill four pixels for every iteration in draw_ellipse 595 596 // the x_offset and y_offset values are needed since the midpoint ellipse algorithm 597 // assumes the midpoint to be at the origin 598 // do a sanity check before each set_pixel, that way we clip to the edges 599 600 int xCoord, yCoord; 601 602 xCoord = x_offset + x; 603 yCoord = y_offset + y; 604 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) 605 set_pixel(xCoord,yCoord,color); 606 xCoord = x_offset - x; 607 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) 608 set_pixel(xCoord,yCoord,color); 609 xCoord = x_offset + x; 610 yCoord = y_offset - y; 611 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) 612 set_pixel(xCoord,yCoord,color); 613 xCoord = x_offset - x; 614 if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) 615 set_pixel(xCoord,yCoord,color); 616 } 617 618 void NWindowScreen::ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color) 619 { 620 // put lines between two pixels twice. once for y positive, the other for y negative (symmetry) 621 // for every iteration in fill_ellipse 622 623 // the x_offset and y_offset values are needed since the midpoint ellipse algorithm 624 // assumes the midpoint to be at the origin 625 // do a sanity check before each set_pixel, that way we clip to the edges 626 627 int xCoord1, yCoord1; 628 int xCoord2, yCoord2; 629 630 xCoord1 = x_offset - x; 631 yCoord1 = y_offset + y; 632 xCoord2 = x_offset + x; 633 yCoord2 = y_offset + y; 634 if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height)) 635 if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height)) 636 draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color); 637 638 xCoord1 = x_offset - x; 639 yCoord1 = y_offset - y; 640 xCoord2 = x_offset + x; 641 yCoord2 = y_offset - y; 642 if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height)) 643 if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height)) 644 draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color); 645 646 } 647