xref: /haiku/src/tests/kits/game/page_flipper/page_flip.cpp (revision 743fe40eba0003d71cf478e80c64c307e05078ac)
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 
11 typedef long (*blit_hook)(long,long,long,long,long,long);
12 typedef long (*sync_hook)();
13 
14 class NApplication:public BApplication {
15 public:
16 	NApplication();
17 	bool is_quitting; // So that the WindowScreen knows what
18                      //       to do when disconnected.
19 private:
20 	bool QuitRequested();
21 	void ReadyToRun();
22 };
23 
24 class NWindowScreen:public BWindowScreen {
25 public:
26 	NWindowScreen(status_t*);
27 private:
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 
46 int
main()47 main()
48 {
49 	NApplication app;
50 }
51 
52 
NApplication()53 NApplication::NApplication()
54       :BApplication("application/x-vnd.Be-sample-jbq1")
55 {
56  	Run(); // see you in ReadyToRun()
57 }
58 
59 
60 void
ReadyToRun()61 NApplication::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 
72 bool
QuitRequested()73 NApplication::QuitRequested()
74 {
75 	is_quitting = true;
76 	return true;
77 }
78 
79 
NWindowScreen(status_t * ret)80 NWindowScreen::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 
118 void
ScreenConnected(bool connected)119 NWindowScreen::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 
201 int32
Entry(void * castToThis)202 NWindowScreen::Entry(void* castToThis)
203 {
204 	return ((NWindowScreen *)castToThis)->DrawingCode();
205 }
206 
207 
208 int32
DrawingCode()209 NWindowScreen::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