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