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