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