⛏️ index : haiku.git

/*
	Copyright 1999, Be Incorporated.   All Rights Reserved.
	This file may be used under the terms of the Be Sample Code License.
*/

#include <Application.h>
#include <WindowScreen.h>
#include <Screen.h>
#include <string.h>

typedef long (*blit_hook)(long,long,long,long,long,long);
typedef long (*sync_hook)();

class NApplication:public BApplication {
public:
	NApplication();
	bool is_quitting; // So that the WindowScreen knows what
                     //       to do when disconnected.
private:
	bool QuitRequested();
	void ReadyToRun();
};

class NWindowScreen:public BWindowScreen {
public:
	NWindowScreen(status_t*);
private:
	void ScreenConnected(bool);
	int32 DrawingCode();
	
	static int32 Entry(void*);

	thread_id fThreadId;
	sem_id fSem;
	area_id fArea;
	uint8* fSaveBuffer;
	uint8* fFrameBuffer;
	ulong fLineLength;
	bool fThreadIsLocked;	// small hack to allow to quit the
                         	// app from ScreenConnected()
	blit_hook fBlitHook; // hooks to the graphics driver functions
	sync_hook fSyncHook;
};


int
main()
{
	NApplication app;
}


NApplication::NApplication()
      :BApplication("application/x-vnd.Be-sample-jbq1")
{
 	Run(); // see you in ReadyToRun()
}


void
NApplication::ReadyToRun()
{
	status_t ret = B_ERROR;
	is_quitting = false;
	NWindowScreen* windowScreen = new NWindowScreen(&ret);
	// exit if constructing the WindowScreen failed.
	if (windowScreen == NULL || ret < B_OK)
 		PostMessage(B_QUIT_REQUESTED);
}


bool
NApplication::QuitRequested()
{
	is_quitting = true;
	return true;
}


NWindowScreen::NWindowScreen(status_t* ret)
	:
	BWindowScreen("Example", B_8_BIT_640x480, ret),
	fThreadId(-1),
	fSem(-1),
	fArea(-1),
	fSaveBuffer(NULL),
	fFrameBuffer(NULL),
	fLineLength(0),
	fThreadIsLocked(true),
	fBlitHook(NULL),
	fSyncHook(NULL)	
{
	if (*ret < B_OK)
		return;

	// this semaphore controls the access to the WindowScreen
      	fSem = create_sem(0, "WindowScreen Access");
	if (fSem < B_OK) {
		*ret = fSem;
		return;
	}

	// this area is used to save the whole framebuffer when
	//       switching workspaces. (better than malloc()).
	fArea = create_area("save", (void**)&fSaveBuffer, B_ANY_ADDRESS,
				640 * 2048, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
	if (fArea < B_OK) {
		*ret = fArea;
		delete_sem(fSem);
		fSem = -1;
		return;
	}
	
	Show(); // let's go. See you in ScreenConnected.
}


void
NWindowScreen::ScreenConnected(bool connected)
{
	if (connected) {
		if (SetSpace(B_8_BIT_640x480) < B_OK || SetFrameBuffer(640, 2048) < B_OK) {
			// properly set the framebuffer. exit if an error occurs.
			be_app->PostMessage(B_QUIT_REQUESTED);
			return;
		}

		// get the hardware acceleration hooks. get them each time
		// the WindowScreen is connected, because of multiple
		// monitor support
		fBlitHook = (blit_hook)CardHookAt(7);
		fSyncHook = (sync_hook)CardHookAt(10);
		
		// cannot work with no hardware blitting
		if (fBlitHook == NULL) {
			be_app->PostMessage(B_QUIT_REQUESTED);
			return;
 		}
		// get the framebuffer-related info, each time the
		// WindowScreen is connected (multiple monitor)
		fFrameBuffer = (uint8 *)(CardInfo()->frame_buffer);
		fLineLength = FrameBufferInfo()->bytes_per_row;
		if (fThreadId == 0) {
			// clean the framebuffer
			memset(fFrameBuffer, 0, 2048 * fLineLength);
			// spawn the rendering thread. exit if an error occurs.
			fThreadId = spawn_thread(Entry, "rendering thread", B_URGENT_DISPLAY_PRIORITY, this);
			if (fThreadId < B_OK || resume_thread(fThreadId) < B_OK)
				be_app->PostMessage(B_QUIT_REQUESTED);
		} else {
			for (int y = 0; y < 2048; y++) {
				// restore the framebuffer when switching back from
				// another workspace.
            			memcpy(fFrameBuffer + y * fLineLength, fSaveBuffer + 640 * y, 640);
			}
		}
		
		// set our color list.
		rgb_color palette[256];
		for (int i = 0; i < 128; i++) {
			rgb_color c1 = {i * 2, i * 2, i * 2};
			rgb_color c2 = {127 + i, 2 * i, 254};
			palette[i] = c1;
			palette[i + 128] = c2;
		}
		SetColorList(palette);
		// allow the rendering thread to run.
		fThreadIsLocked = false;
		release_sem(fSem);
	} else {
		// block the rendering thread.
		if (!fThreadIsLocked) {
			acquire_sem(fSem);
			fThreadIsLocked = true;
 		}

		// kill the rendering and clean up when quitting
		if ((((NApplication*)be_app)->is_quitting)) {
			status_t ret;
			kill_thread(fThreadId);
			wait_for_thread(fThreadId, &ret);
			delete_sem(fSem);
			delete_area(fArea);
		} else {
			// set the color list black so that the screen doesn't seem
			// to freeze while saving the framebuffer
			rgb_color c = { 0, 0, 0 };
			rgb_color palette[256];
			for (int i = 0; i < 256; i++)
				palette[i] = c;
			SetColorList(palette);
			
			// save the framebuffer
			for (int y = 0; y < 2048; y++)
				memcpy(fSaveBuffer + 640 * y, fFrameBuffer + y * fLineLength, 640);
	   	}
	}
}


int32
NWindowScreen::Entry(void* castToThis)
{
	return ((NWindowScreen *)castToThis)->DrawingCode();
}


int32
NWindowScreen::DrawingCode()
{
	// gain access to the framebuffer before writing to it.

	acquire_sem(fSem);

	for (int j = 1440; j < 2048; j++) {
		for (int i; i < 640; i++) {
			// draw the backgroud ripple pattern
			float val=63.99*(1+cos(2*M_PI*((i-320)*(i-320)+(j-1744)*(j-1744))/1216));
			fFrameBuffer[i + fLineLength*j]=int(val);
		}
	}

	ulong numframe = 0;
	bigtime_t trgt = 0;
	ulong y_origin;
	uint8* current_frame;
	while (true) {
		// the framebuffer coordinates of the next frame
		y_origin = 480 * (numframe % 3);
		// and a pointer to it
		current_frame = fFrameBuffer + y_origin * fLineLength;
		// copy the background
		int ytop = numframe % 608, ybot = ytop + 479;
		if (ybot < 608) {
			fBlitHook(0,1440+ytop,0,y_origin,639,479);
		} else {
			fBlitHook(0,1440+ytop,0,y_origin,639,1086-ybot);
			fBlitHook(0,1440,0,y_origin+1087-ybot,639,ybot-608);
		}
		// calculate the circle position. doing such calculations
		// between blit() and sync() is a good idea.
		uint32 x=(uint32)(287.99*(1+sin(numframe/72.)));
		uint32 y=(uint32)(207.99*(1+sin(numframe/52.)));
		if (fSyncHook)
			fSyncHook();
		// draw the circle
		for (int j = 0; j < 64; j++) {
			for (int i = 0; i < 64; i++) {
				if ((i-31)*(i-32)+(j-31)*(j-32)<=1024)
					current_frame[x + i + fLineLength * (y + j)] += 128;
			}
		}
		// release the semaphore while waiting. gotta release it
		// at some point or nasty things will happen!
		release_sem(fSem);
		// try to sync with the vertical retrace
		if (BScreen(this).WaitForRetrace() != B_OK) {
			// we're doing some triple buffering. unwanted things would
			// happen if we rendered more pictures than the card can
			// display. we here make sure not to render more than 55.5 
			// pictures per second if the card does not support retrace
			// syncing
			if (system_time() < trgt)
				snooze(trgt - system_time());
 			trgt = system_time() + 18000;
		}
		// acquire the semaphore back before talking to the driver
		acquire_sem(fSem);
		// do the page-flipping
		MoveDisplayArea(0, y_origin);
		// and go to the next frame!
		numframe++;
	}
	return 0;
}