/* * Copyright 2006-2012, Haiku. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Jérôme Duval, korli@users.berlios.de * Philippe Houdoin, philippe.houdoin@free.fr * Stefano Ceccherini, burton666@libero.it */ #include <kernel/image.h> #include <GLView.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <DirectWindow.h> #include <GLRenderer.h> #include "interface/DirectWindowPrivate.h" #include "GLDispatcher.h" #include "GLRendererRoster.h" struct glview_direct_info { direct_buffer_info* direct_info; bool direct_connected; bool enable_direct_mode; glview_direct_info(); ~glview_direct_info(); }; BGLView::BGLView(BRect rect, const char* name, ulong resizingMode, ulong mode, ulong options) : BView(rect, name, B_FOLLOW_ALL_SIDES, mode | B_WILL_DRAW | B_FRAME_EVENTS), // | B_FULL_UPDATE_ON_RESIZE) fGc(NULL), fOptions(options), fDitherCount(0), fDrawLock("BGLView draw lock"), fDisplayLock("BGLView display lock"), fClipInfo(NULL), fRenderer(NULL), fRoster(NULL), fDitherMap(NULL) { fRoster = new GLRendererRoster(this, options); } BGLView::~BGLView() { delete fClipInfo; if (fRenderer) fRenderer->Release(); } void BGLView::LockGL() { // TODO: acquire the OpenGL API lock it on this glview fDisplayLock.Lock(); if (fRenderer) fRenderer->LockGL(); } void BGLView::UnlockGL() { if (fRenderer) fRenderer->UnlockGL(); fDisplayLock.Unlock(); // TODO: release the GL API lock to others glviews } void BGLView::SwapBuffers() { SwapBuffers(false); } void BGLView::SwapBuffers(bool vSync) { if (fRenderer) { _LockDraw(); fRenderer->SwapBuffers(vSync); _UnlockDraw(); } } BView* BGLView::EmbeddedView() { return NULL; } void* BGLView::GetGLProcAddress(const char* procName) { BGLDispatcher* glDispatcher = NULL; if (fRenderer) glDispatcher = fRenderer->GLDispatcher(); if (glDispatcher) return (void*)glDispatcher->AddressOf(procName); return NULL; } status_t BGLView::CopyPixelsOut(BPoint source, BBitmap* dest) { if (!fRenderer) return B_ERROR; if (!dest || !dest->Bounds().IsValid()) return B_BAD_VALUE; return fRenderer->CopyPixelsOut(source, dest); } status_t BGLView::CopyPixelsIn(BBitmap* source, BPoint dest) { if (!fRenderer) return B_ERROR; if (!source || !source->Bounds().IsValid()) return B_BAD_VALUE; return fRenderer->CopyPixelsIn(source, dest); } /*! Mesa's GLenum is not ulong but uint, so we can't use GLenum without breaking this method signature. Instead, we have to use the effective BeOS's SGI OpenGL GLenum type: unsigned long. */ void BGLView::ErrorCallback(unsigned long errorCode) { char msg[32]; sprintf(msg, "GL: Error code $%04lx.", errorCode); // TODO: under BeOS R5, it call debugger(msg); fprintf(stderr, "%s\n", msg); } void BGLView::Draw(BRect updateRect) { if (fRenderer) { _LockDraw(); fRenderer->Draw(updateRect); _UnlockDraw(); return; } // TODO: auto-size and center the string MovePenTo(8, 32); DrawString("No OpenGL renderer available!"); } void BGLView::AttachedToWindow() { BView::AttachedToWindow(); fBounds = Bounds(); for (BView* view = this; view != NULL; view = view->Parent()) view->ConvertToParent(&fBounds); fRenderer = fRoster->GetRenderer(); if (fRenderer != NULL) { // Jackburton: The following code was commented because it doesn't look // good in "direct" mode: // when the window is moved, the app_server doesn't paint the view's // background, and the stuff behind the window itself shows up. // Setting the view color to black, instead, looks a bit more elegant. #if 0 // Don't paint white window background when resized SetViewColor(B_TRANSPARENT_32_BIT); #else SetViewColor(0, 0, 0); #endif // Set default OpenGL viewport: LockGL(); glViewport(0, 0, Bounds().IntegerWidth(), Bounds().IntegerHeight()); UnlockGL(); fRenderer->FrameResized(Bounds().IntegerWidth(), Bounds().IntegerHeight()); if (fClipInfo) { fRenderer->DirectConnected(fClipInfo->direct_info); fRenderer->EnableDirectMode(fClipInfo->enable_direct_mode); } return; } fprintf(stderr, "no renderer found! \n"); // No Renderer, no rendering. Setup a minimal "No Renderer" string drawing // context SetFont(be_bold_font); // SetFontSize(16); } void BGLView::AllAttached() { BView::AllAttached(); } void BGLView::DetachedFromWindow() { if (fRenderer) fRenderer->Release(); fRenderer = NULL; BView::DetachedFromWindow(); } void BGLView::AllDetached() { BView::AllDetached(); } void BGLView::FrameResized(float width, float height) { fBounds = Bounds(); for (BView* v = this; v; v = v->Parent()) v->ConvertToParent(&fBounds); if (fRenderer) { LockGL(); _LockDraw(); _CallDirectConnected(); fRenderer->FrameResized(width, height); _UnlockDraw(); UnlockGL(); } BView::FrameResized(width, height); } status_t BGLView::Perform(perform_code d, void* arg) { return BView::Perform(d, arg); } status_t BGLView::Archive(BMessage* data, bool deep) const { return BView::Archive(data, deep); } void BGLView::MessageReceived(BMessage* msg) { BView::MessageReceived(msg); } void BGLView::SetResizingMode(uint32 mode) { BView::SetResizingMode(mode); } void BGLView::GetPreferredSize(float* _width, float* _height) { if (_width) *_width = 0; if (_height) *_height = 0; } void BGLView::Show() { BView::Show(); } void BGLView::Hide() { BView::Hide(); } BHandler* BGLView::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, int32 form, const char* property) { return BView::ResolveSpecifier(msg, index, specifier, form, property); } status_t BGLView::GetSupportedSuites(BMessage* data) { return BView::GetSupportedSuites(data); } void BGLView::DirectConnected(direct_buffer_info* info) { if (fClipInfo == NULL) { fClipInfo = new (std::nothrow) glview_direct_info(); if (fClipInfo == NULL) return; } direct_buffer_info* localInfo = fClipInfo->direct_info; switch (info->buffer_state & B_DIRECT_MODE_MASK) { case B_DIRECT_START: fClipInfo->direct_connected = true; memcpy(localInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE); _UnlockDraw(); break; case B_DIRECT_MODIFY: _LockDraw(); memcpy(localInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE); _UnlockDraw(); break; case B_DIRECT_STOP: fClipInfo->direct_connected = false; _LockDraw(); break; } if (fRenderer) _CallDirectConnected(); } void BGLView::EnableDirectMode(bool enabled) { if (fRenderer) fRenderer->EnableDirectMode(enabled); if (fClipInfo == NULL) { fClipInfo = new (std::nothrow) glview_direct_info(); if (fClipInfo == NULL) return; } fClipInfo->enable_direct_mode = enabled; } void BGLView::_LockDraw() { if (!fClipInfo || !fClipInfo->enable_direct_mode) return; fDrawLock.Lock(); } void BGLView::_UnlockDraw() { if (!fClipInfo || !fClipInfo->enable_direct_mode) return; fDrawLock.Unlock(); } void BGLView::_CallDirectConnected() { if (!fClipInfo) return; direct_buffer_info* localInfo = fClipInfo->direct_info; direct_buffer_info* info = (direct_buffer_info*)malloc( DIRECT_BUFFER_INFO_AREA_SIZE); if (info == NULL) return; memcpy(info, localInfo, DIRECT_BUFFER_INFO_AREA_SIZE); // Collect the rects into a BRegion, then clip to the view's bounds BRegion region; for (uint32 c = 0; c < localInfo->clip_list_count; c++) region.Include(localInfo->clip_list[c]); BRegion boundsRegion = fBounds.OffsetByCopy(localInfo->window_bounds.left, localInfo->window_bounds.top); info->window_bounds = boundsRegion.RectAtInt(0); // window_bounds are now view bounds region.IntersectWith(&boundsRegion); info->clip_list_count = region.CountRects(); info->clip_bounds = region.FrameInt(); for (uint32 c = 0; c < info->clip_list_count; c++) info->clip_list[c] = region.RectAtInt(c); fRenderer->DirectConnected(info); free(info); } //---- virtual reserved methods ---------- void BGLView::_ReservedGLView1() {} void BGLView::_ReservedGLView2() {} void BGLView::_ReservedGLView3() {} void BGLView::_ReservedGLView4() {} void BGLView::_ReservedGLView5() {} void BGLView::_ReservedGLView6() {} void BGLView::_ReservedGLView7() {} void BGLView::_ReservedGLView8() {} // #pragma mark - // BeOS compatibility: contrary to others BView's contructors, // BGLView one wants a non-const name argument. BGLView::BGLView(BRect rect, char* name, ulong resizingMode, ulong mode, ulong options) : BView(rect, name, B_FOLLOW_ALL_SIDES, mode | B_WILL_DRAW | B_FRAME_EVENTS), fGc(NULL), fOptions(options), fDitherCount(0), fDrawLock("BGLView draw lock"), fDisplayLock("BGLView display lock"), fClipInfo(NULL), fRenderer(NULL), fRoster(NULL), fDitherMap(NULL) { fRoster = new GLRendererRoster(this, options); } #if 0 // TODO: implement BGLScreen class... BGLScreen::BGLScreen(char* name, ulong screenMode, ulong options, status_t* error, bool debug) : BWindowScreen(name, screenMode, error, debug) { } BGLScreen::~BGLScreen() { } void BGLScreen::LockGL() { } void BGLScreen::UnlockGL() { } void BGLScreen::SwapBuffers() { } void BGLScreen::ErrorCallback(unsigned long errorCode) { // Mesa's GLenum is not ulong but uint! char msg[32]; sprintf(msg, "GL: Error code $%04lx.", errorCode); // debugger(msg); fprintf(stderr, "%s\n", msg); return; } void BGLScreen::ScreenConnected(bool enabled) { } void BGLScreen::FrameResized(float width, float height) { return BWindowScreen::FrameResized(width, height); } status_t BGLScreen::Perform(perform_code d, void* arg) { return BWindowScreen::Perform(d, arg); } status_t BGLScreen::Archive(BMessage* data, bool deep) const { return BWindowScreen::Archive(data, deep); } void BGLScreen::MessageReceived(BMessage* msg) { BWindowScreen::MessageReceived(msg); } void BGLScreen::Show() { BWindowScreen::Show(); } void BGLScreen::Hide() { BWindowScreen::Hide(); } BHandler* BGLScreen::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, int32 form, const char* property) { return BWindowScreen::ResolveSpecifier(msg, index, specifier, form, property); } status_t BGLScreen::GetSupportedSuites(BMessage* data) { return BWindowScreen::GetSupportedSuites(data); } //---- virtual reserved methods ---------- void BGLScreen::_ReservedGLScreen1() {} void BGLScreen::_ReservedGLScreen2() {} void BGLScreen::_ReservedGLScreen3() {} void BGLScreen::_ReservedGLScreen4() {} void BGLScreen::_ReservedGLScreen5() {} void BGLScreen::_ReservedGLScreen6() {} void BGLScreen::_ReservedGLScreen7() {} void BGLScreen::_ReservedGLScreen8() {} #endif const char* color_space_name(color_space space) { #define C2N(a) case a: return #a switch (space) { C2N(B_RGB24); C2N(B_RGB32); C2N(B_RGBA32); C2N(B_RGB32_BIG); C2N(B_RGBA32_BIG); C2N(B_GRAY8); C2N(B_GRAY1); C2N(B_RGB16); C2N(B_RGB15); C2N(B_RGBA15); C2N(B_CMAP8); default: return "Unknown!"; }; #undef C2N }; glview_direct_info::glview_direct_info() { // TODO: See direct_window_data() in app_server's ServerWindow.cpp direct_info = (direct_buffer_info*)calloc(1, DIRECT_BUFFER_INFO_AREA_SIZE); direct_connected = false; enable_direct_mode = false; } glview_direct_info::~glview_direct_info() { free(direct_info); }