1/*
2	Copyright 1999, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5
6#include <Application.h>
7#include <WindowScreen.h>
8#include <Screen.h>
9#include <string.h>
10
11typedef long (*blit_hook)(long,long,long,long,long,long);
12typedef long (*sync_hook)();
13
14class NApplication:public BApplication {
15public:
16	NApplication();
17	bool is_quitting; // So that the WindowScreen knows what
18                     //       to do when disconnected.
19private:
20	bool QuitRequested();
21	void ReadyToRun();
22};
23
24class NWindowScreen:public BWindowScreen {
25public:
26	NWindowScreen(status_t*);
27private:
28	void ScreenConnected(bool);
29	int32 DrawingCode();
30
31	static int32 Entry(void*);
32
33	thread_id fThreadId;
34	sem_id fSem;
35	area_id fArea;
36	uint8* fSaveBuffer;
37	uint8* fFrameBuffer;
38	ulong fLineLength;
39	bool fThreadIsLocked;	// small hack to allow to quit the
40                         	// app from ScreenConnected()
41	blit_hook fBlitHook; // hooks to the graphics driver functions
42	sync_hook fSyncHook;
43};
44
45
46int
47main()
48{
49	NApplication app;
50}
51
52
53NApplication::NApplication()
54      :BApplication("application/x-vnd.Be-sample-jbq1")
55{
56 	Run(); // see you in ReadyToRun()
57}
58
59
60void
61NApplication::ReadyToRun()
62{
63	status_t ret = B_ERROR;
64	is_quitting = false;
65	NWindowScreen* windowScreen = new NWindowScreen(&ret);
66	// exit if constructing the WindowScreen failed.
67	if (windowScreen == NULL || ret < B_OK)
68 		PostMessage(B_QUIT_REQUESTED);
69}
70
71
72bool
73NApplication::QuitRequested()
74{
75	is_quitting = true;
76	return true;
77}
78
79
80NWindowScreen::NWindowScreen(status_t* ret)
81	:
82	BWindowScreen("Example", B_8_BIT_640x480, ret),
83	fThreadId(-1),
84	fSem(-1),
85	fArea(-1),
86	fSaveBuffer(NULL),
87	fFrameBuffer(NULL),
88	fLineLength(0),
89	fThreadIsLocked(true),
90	fBlitHook(NULL),
91	fSyncHook(NULL)
92{
93	if (*ret < B_OK)
94		return;
95
96	// this semaphore controls the access to the WindowScreen
97      	fSem = create_sem(0, "WindowScreen Access");
98	if (fSem < B_OK) {
99		*ret = fSem;
100		return;
101	}
102
103	// this area is used to save the whole framebuffer when
104	//       switching workspaces. (better than malloc()).
105	fArea = create_area("save", (void**)&fSaveBuffer, B_ANY_ADDRESS,
106				640 * 2048, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
107	if (fArea < B_OK) {
108		*ret = fArea;
109		delete_sem(fSem);
110		fSem = -1;
111		return;
112	}
113
114	Show(); // let's go. See you in ScreenConnected.
115}
116
117
118void
119NWindowScreen::ScreenConnected(bool connected)
120{
121	if (connected) {
122		if (SetSpace(B_8_BIT_640x480) < B_OK || SetFrameBuffer(640, 2048) < B_OK) {
123			// properly set the framebuffer. exit if an error occurs.
124			be_app->PostMessage(B_QUIT_REQUESTED);
125			return;
126		}
127
128		// get the hardware acceleration hooks. get them each time
129		// the WindowScreen is connected, because of multiple
130		// monitor support
131		fBlitHook = (blit_hook)CardHookAt(7);
132		fSyncHook = (sync_hook)CardHookAt(10);
133
134		// cannot work with no hardware blitting
135		if (fBlitHook == NULL) {
136			be_app->PostMessage(B_QUIT_REQUESTED);
137			return;
138 		}
139		// get the framebuffer-related info, each time the
140		// WindowScreen is connected (multiple monitor)
141		fFrameBuffer = (uint8 *)(CardInfo()->frame_buffer);
142		fLineLength = FrameBufferInfo()->bytes_per_row;
143		if (fThreadId == 0) {
144			// clean the framebuffer
145			memset(fFrameBuffer, 0, 2048 * fLineLength);
146			// spawn the rendering thread. exit if an error occurs.
147			fThreadId = spawn_thread(Entry, "rendering thread", B_URGENT_DISPLAY_PRIORITY, this);
148			if (fThreadId < B_OK || resume_thread(fThreadId) < B_OK)
149				be_app->PostMessage(B_QUIT_REQUESTED);
150		} else {
151			for (int y = 0; y < 2048; y++) {
152				// restore the framebuffer when switching back from
153				// another workspace.
154            			memcpy(fFrameBuffer + y * fLineLength, fSaveBuffer + 640 * y, 640);
155			}
156		}
157
158		// set our color list.
159		rgb_color palette[256];
160		for (int i = 0; i < 128; i++) {
161			rgb_color c1 = {i * 2, i * 2, i * 2};
162			rgb_color c2 = {127 + i, 2 * i, 254};
163			palette[i] = c1;
164			palette[i + 128] = c2;
165		}
166		SetColorList(palette);
167		// allow the rendering thread to run.
168		fThreadIsLocked = false;
169		release_sem(fSem);
170	} else {
171		// block the rendering thread.
172		if (!fThreadIsLocked) {
173			acquire_sem(fSem);
174			fThreadIsLocked = true;
175 		}
176
177		// kill the rendering and clean up when quitting
178		if ((((NApplication*)be_app)->is_quitting)) {
179			status_t ret;
180			kill_thread(fThreadId);
181			wait_for_thread(fThreadId, &ret);
182			delete_sem(fSem);
183			delete_area(fArea);
184		} else {
185			// set the color list black so that the screen doesn't seem
186			// to freeze while saving the framebuffer
187			rgb_color c = { 0, 0, 0 };
188			rgb_color palette[256];
189			for (int i = 0; i < 256; i++)
190				palette[i] = c;
191			SetColorList(palette);
192
193			// save the framebuffer
194			for (int y = 0; y < 2048; y++)
195				memcpy(fSaveBuffer + 640 * y, fFrameBuffer + y * fLineLength, 640);
196	   	}
197	}
198}
199
200
201int32
202NWindowScreen::Entry(void* castToThis)
203{
204	return ((NWindowScreen *)castToThis)->DrawingCode();
205}
206
207
208int32
209NWindowScreen::DrawingCode()
210{
211	// gain access to the framebuffer before writing to it.
212
213	acquire_sem(fSem);
214
215	for (int j = 1440; j < 2048; j++) {
216		for (int i; i < 640; i++) {
217			// draw the backgroud ripple pattern
218			float val=63.99*(1+cos(2*M_PI*((i-320)*(i-320)+(j-1744)*(j-1744))/1216));
219			fFrameBuffer[i + fLineLength*j]=int(val);
220		}
221	}
222
223	ulong numframe = 0;
224	bigtime_t trgt = 0;
225	ulong y_origin;
226	uint8* current_frame;
227	while (true) {
228		// the framebuffer coordinates of the next frame
229		y_origin = 480 * (numframe % 3);
230		// and a pointer to it
231		current_frame = fFrameBuffer + y_origin * fLineLength;
232		// copy the background
233		int ytop = numframe % 608, ybot = ytop + 479;
234		if (ybot < 608) {
235			fBlitHook(0,1440+ytop,0,y_origin,639,479);
236		} else {
237			fBlitHook(0,1440+ytop,0,y_origin,639,1086-ybot);
238			fBlitHook(0,1440,0,y_origin+1087-ybot,639,ybot-608);
239		}
240		// calculate the circle position. doing such calculations
241		// between blit() and sync() is a good idea.
242		uint32 x=(uint32)(287.99*(1+sin(numframe/72.)));
243		uint32 y=(uint32)(207.99*(1+sin(numframe/52.)));
244		if (fSyncHook)
245			fSyncHook();
246		// draw the circle
247		for (int j = 0; j < 64; j++) {
248			for (int i = 0; i < 64; i++) {
249				if ((i-31)*(i-32)+(j-31)*(j-32)<=1024)
250					current_frame[x + i + fLineLength * (y + j)] += 128;
251			}
252		}
253		// release the semaphore while waiting. gotta release it
254		// at some point or nasty things will happen!
255		release_sem(fSem);
256		// try to sync with the vertical retrace
257		if (BScreen(this).WaitForRetrace() != B_OK) {
258			// we're doing some triple buffering. unwanted things would
259			// happen if we rendered more pictures than the card can
260			// display. we here make sure not to render more than 55.5
261			// pictures per second if the card does not support retrace
262			// syncing
263			if (system_time() < trgt)
264				snooze(trgt - system_time());
265 			trgt = system_time() + 18000;
266		}
267		// acquire the semaphore back before talking to the driver
268		acquire_sem(fSem);
269		// do the page-flipping
270		MoveDisplayArea(0, y_origin);
271		// and go to the next frame!
272		numframe++;
273	}
274	return 0;
275}
276