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