root/src/tests/add-ons/opengl/fake_renderer/FakeRenderer.cpp
/*
 * Copyright 2006-2008, Haiku. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Philippe Houdoin, philippe.houdoin@free.fr
 */
/*
 * Mesa 3-D graphics library
 * Version:  6.1
 *
 * Copyright (C) 1999-2004  Brian Paul   All Rights Reserved.
 */

#include "FakeRenderer.h"

#include <stdio.h>

#include <Autolock.h>
#include <DirectWindowPrivate.h>
#include <GraphicsDefs.h>
#include <Screen.h>


extern const char * color_space_name(color_space space);


extern "C" _EXPORT BGLRenderer*
instantiate_gl_renderer(BGLView* view, ulong options,
        BGLDispatcher* dispatcher)
{
        return new FakeRenderer(view, options, dispatcher);
}


FakeRenderer::FakeRenderer(BGLView* view, ulong options,
                BGLDispatcher* dispatcher)
        : BGLRenderer(view, options, dispatcher),
        fOptions(options),
        fDrawLocker("direct draw locker"),
        fFrameBuffer(NULL),
        fColorSpace(B_NO_COLOR_SPACE),
        fRects(NULL),
        fConnected(false),
        fDisabled(false)
{
        fDrawSem = create_sem(0, "FakeRenderer draw");
        fDrawThread = spawn_thread(_DirectDrawThread, "FakeRenderer direct draw", B_DISPLAY_PRIORITY, this);
        resume_thread(fDrawThread);

}

FakeRenderer::~FakeRenderer()
{
        // Wakeup the draw thread by murdering its favorite semaphore
        delete_sem(fDrawSem);

        status_t exit_value;
        wait_for_thread(fDrawThread, &exit_value);

        free(fRects);
}


void
FakeRenderer::SwapBuffers(bool VSync)
{
        if (VSync && GLView()->Window()) {
                // TODO: find a way to check VSync support is actually working...
                BScreen screen(GLView()->Window());
                screen.WaitForRetrace();
        }

        // Simulate rendering a new buffer: randomized buffer color ;-)
        fDrawColor = make_color(rand() % 0xFF, rand() % 0xFF, rand() % 0xFF);

        if (!fConnected || fDisabled) {
                GLView()->LockLooper();
                // TODO : refresh area
                GLView()->UnlockLooper();
                return;
        }

        // Direct mode: wake up drawing thread
        release_sem(fDrawSem);
}


void
FakeRenderer::Draw(BRect updateRect)
{
/*
        if (fBitmap && (!fDirectModeEnabled || (fInfo == NULL)))
                GLView()->DrawBitmap(fBitmap, updateRect, updateRect);
*/      
}


void
FakeRenderer::EnableDirectMode(bool enabled)
{
        fDisabled = ! enabled;
}


void
FakeRenderer::DirectConnected(direct_buffer_info *info)
{
        if (!fConnected && fDisabled)
                return;
                
        BAutolock lock(fDrawLocker);
        
        switch (info->buffer_state & B_DIRECT_MODE_MASK) {
        case B_DIRECT_START:
                fConnected = true;
                /* fall through */
        case B_DIRECT_MODIFY:
                fFrameBuffer = (uint8 *) info->bits;
                fBytesPerRow = info->bytes_per_row;
                fColorSpace = info->pixel_format;
                
                free(fRects);
                fRectsCount = info->clip_list_count;
                fRects = (clipping_rect *) malloc(fRectsCount * sizeof(clipping_rect));
                memcpy(fRects, info->clip_list, fRectsCount * sizeof(clipping_rect));

                fprintf(stderr, "fFrameBuffer = %p\n"
                                                "fBytesPerRow = %d\n"
                                                "fColorSpace  = %s\n", fFrameBuffer, fBytesPerRow, color_space_name(fColorSpace));
                for (int i = 0; i < fRectsCount; i++) {
                        fprintf(stderr, "fRects[%d] = %d, %d to %d, %d\n", 
                                i, fRects[i].left, fRects[i].top, fRects[i].right, fRects[i].bottom);
                }
                break;
        
        case B_DIRECT_STOP:
                fConnected = false;
                break;
        }

        fprintf(stderr, "fConnected = %s\n", fConnected ? "true" : "false");
}


// ----


int32
FakeRenderer::_DirectDrawThread(void *data)
{       
        FakeRenderer *me = (FakeRenderer *) data;
        return me->_DirectDrawThread();
}


int32
FakeRenderer::_DirectDrawThread(void)
{
        // Let's wait forever/until semaphore death next redraw 
        while (acquire_sem(fDrawSem) == B_OK) {

                BAutolock lock(fDrawLocker);

                int i;
                int32 x, y;

                switch(fColorSpace) {
                case B_RGB32:
                case B_RGBA32:
                        for (i = 0; i < fRectsCount; i++) {
                                for (y = fRects[i].top; y <= fRects[i].bottom; y++) {
                                        uint8 * p = fFrameBuffer + ( y * fBytesPerRow ) + fRects[i].left * 4;
                                        for (x = fRects[i].left; x <= fRects[i].right; x++) {
                                                *p++ = fDrawColor.blue;
                                                *p++ = fDrawColor.green;
                                                *p++ = fDrawColor.red;
                                                *p++ = fDrawColor.alpha;
                                        }
                                }
                        }
                        break;
        
                case B_RGB24:
                case B_RGB32_BIG:
                case B_RGBA32_BIG:
                case B_RGB24_BIG:
                        /* fill this in with the color-space conversion
                         * code of your choosing
                         */
                        break;

                case B_RGB16:
                case B_RGB15:
                case B_RGBA15:
                case B_RGB16_BIG:
                case B_RGB15_BIG:
                case B_RGBA15_BIG:
                        /* same here */
                        break;

                case B_CMAP8:
                        /* same here */
                        break;

                default:
                        /* unsupported mode */
                        break;
                }
        
        } // while draw

        return 0;
}