1/* 2 * Copyright 2006-2012, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Jérôme Duval, korli@users.berlios.de 7 * Philippe Houdoin, philippe.houdoin@free.fr 8 * Stefano Ceccherini, burton666@libero.it 9 */ 10 11#include <kernel/image.h> 12 13#include <GLView.h> 14 15#include <assert.h> 16#include <stdio.h> 17#include <stdlib.h> 18#include <string.h> 19 20#include <DirectWindow.h> 21#include "GLRenderer.h" 22 23#include <private/interface/DirectWindowPrivate.h> 24#include "GLRendererRoster.h" 25 26#include "glapi/glapi.h" 27 28struct glview_direct_info { 29 direct_buffer_info* direct_info; 30 bool direct_connected; 31 bool enable_direct_mode; 32 33 glview_direct_info(); 34 ~glview_direct_info(); 35}; 36 37 38BGLView::BGLView(BRect rect, const char* name, ulong resizingMode, ulong mode, 39 ulong options) 40 : 41 BView(rect, name, B_FOLLOW_ALL_SIDES, mode | B_WILL_DRAW | B_FRAME_EVENTS), 42 fGc(NULL), 43 fOptions(options), 44 fDitherCount(0), 45 fDrawLock("BGLView draw lock"), 46 fDisplayLock("BGLView display lock"), 47 fClipInfo(NULL), 48 fRenderer(NULL), 49 fDitherMap(NULL) 50{ 51 fRenderer = GLRendererRoster::Roster()->GetRenderer(this, options); 52} 53 54 55BGLView::~BGLView() 56{ 57 delete fClipInfo; 58 if (fRenderer) 59 fRenderer->Release(); 60} 61 62 63void 64BGLView::LockGL() 65{ 66 // TODO: acquire the OpenGL API lock it on this glview 67 68 fDisplayLock.Lock(); 69 if (fRenderer != NULL && fDisplayLock.CountLocks() == 1) 70 fRenderer->LockGL(); 71} 72 73 74void 75BGLView::UnlockGL() 76{ 77 thread_id lockerThread = fDisplayLock.LockingThread(); 78 thread_id callerThread = find_thread(NULL); 79 80 if (lockerThread != B_ERROR && lockerThread != callerThread) { 81 printf("UnlockGL is called from wrong thread, lockerThread: %d, callerThread: %d\n", 82 (int)lockerThread, (int)callerThread); 83 debugger("[!]"); 84 } 85 86 if (fRenderer != NULL && fDisplayLock.CountLocks() == 1) 87 fRenderer->UnlockGL(); 88 fDisplayLock.Unlock(); 89 90 // TODO: release the GL API lock to others glviews 91} 92 93 94void 95BGLView::SwapBuffers() 96{ 97 SwapBuffers(false); 98} 99 100 101void 102BGLView::SwapBuffers(bool vSync) 103{ 104 if (fRenderer) { 105 _LockDraw(); 106 fRenderer->SwapBuffers(vSync); 107 _UnlockDraw(); 108 } 109} 110 111 112BView* 113BGLView::EmbeddedView() 114{ 115 return NULL; 116} 117 118 119void* 120BGLView::GetGLProcAddress(const char* procName) 121{ 122 return (void*)_glapi_get_proc_address(procName); 123} 124 125 126status_t 127BGLView::CopyPixelsOut(BPoint source, BBitmap* dest) 128{ 129 if (!fRenderer) 130 return B_ERROR; 131 132 if (!dest || !dest->Bounds().IsValid()) 133 return B_BAD_VALUE; 134 135 return fRenderer->CopyPixelsOut(source, dest); 136} 137 138 139status_t 140BGLView::CopyPixelsIn(BBitmap* source, BPoint dest) 141{ 142 if (!fRenderer) 143 return B_ERROR; 144 145 if (!source || !source->Bounds().IsValid()) 146 return B_BAD_VALUE; 147 148 return fRenderer->CopyPixelsIn(source, dest); 149} 150 151 152/*! Mesa's GLenum is not ulong but uint, so we can't use GLenum 153 without breaking this method signature. 154 Instead, we have to use the effective BeOS's SGI OpenGL GLenum type: 155 unsigned long. 156 */ 157void 158BGLView::ErrorCallback(unsigned long errorCode) 159{ 160 char msg[32]; 161 sprintf(msg, "GL: Error code $%04lx.", errorCode); 162 // TODO: under BeOS R5, it call debugger(msg); 163 fprintf(stderr, "%s\n", msg); 164} 165 166 167void 168BGLView::Draw(BRect updateRect) 169{ 170 if (fRenderer) { 171 if (!fClipInfo || !fClipInfo->enable_direct_mode) 172 fRenderer->Draw(updateRect); 173 return; 174 } 175 // TODO: auto-size and center the string 176 MovePenTo(8, 32); 177 DrawString("No OpenGL renderer available!"); 178} 179 180 181void 182BGLView::AttachedToWindow() 183{ 184 BView::AttachedToWindow(); 185 186 fBounds = Bounds(); 187 for (BView* view = this; view != NULL; view = view->Parent()) 188 view->ConvertToParent(&fBounds); 189 190 if (fRenderer != NULL) { 191 // Jackburton: The following code was commented because it doesn't look 192 // good in "direct" mode: 193 // when the window is moved, the app_server doesn't paint the view's 194 // background, and the stuff behind the window itself shows up. 195 // Setting the view color to black, instead, looks a bit more elegant. 196#if 0 197 // Don't paint white window background when resized 198 SetViewColor(B_TRANSPARENT_32_BIT); 199#else 200 SetViewColor(0, 0, 0); 201#endif 202 203 // Set default OpenGL viewport: 204 LockGL(); 205 glViewport(0, 0, Bounds().IntegerWidth(), Bounds().IntegerHeight()); 206 UnlockGL(); 207 fRenderer->FrameResized(Bounds().IntegerWidth(), 208 Bounds().IntegerHeight()); 209 210 if (fClipInfo) { 211 fRenderer->DirectConnected(fClipInfo->direct_info); 212 fRenderer->EnableDirectMode(fClipInfo->enable_direct_mode); 213 } 214 215 return; 216 } 217 218 fprintf(stderr, "no renderer found! \n"); 219 220 // No Renderer, no rendering. Setup a minimal "No Renderer" string drawing 221 // context 222 SetFont(be_bold_font); 223 // SetFontSize(16); 224} 225 226 227void 228BGLView::AllAttached() 229{ 230 BView::AllAttached(); 231} 232 233 234void 235BGLView::DetachedFromWindow() 236{ 237 BView::DetachedFromWindow(); 238} 239 240 241void 242BGLView::AllDetached() 243{ 244 BView::AllDetached(); 245} 246 247 248void 249BGLView::FrameResized(float width, float height) 250{ 251 fBounds = Bounds(); 252 for (BView* v = this; v; v = v->Parent()) 253 v->ConvertToParent(&fBounds); 254 255 if (fRenderer) { 256 //_LockDraw(); 257 fRenderer->FrameResized(width, height); 258 //_UnlockDraw(); 259 } 260 261 BView::FrameResized(width, height); 262} 263 264 265status_t 266BGLView::Perform(perform_code d, void* arg) 267{ 268 return BView::Perform(d, arg); 269} 270 271 272status_t 273BGLView::Archive(BMessage* data, bool deep) const 274{ 275 return BView::Archive(data, deep); 276} 277 278 279void 280BGLView::MessageReceived(BMessage* msg) 281{ 282 BView::MessageReceived(msg); 283} 284 285 286void 287BGLView::SetResizingMode(uint32 mode) 288{ 289 BView::SetResizingMode(mode); 290} 291 292 293void 294BGLView::GetPreferredSize(float* _width, float* _height) 295{ 296 if (_width) 297 *_width = 0; 298 if (_height) 299 *_height = 0; 300} 301 302 303void 304BGLView::Show() 305{ 306 BView::Show(); 307} 308 309 310void 311BGLView::Hide() 312{ 313 BView::Hide(); 314} 315 316 317BHandler* 318BGLView::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, 319 int32 form, const char* property) 320{ 321 return BView::ResolveSpecifier(msg, index, specifier, form, property); 322} 323 324 325status_t 326BGLView::GetSupportedSuites(BMessage* data) 327{ 328 return BView::GetSupportedSuites(data); 329} 330 331 332void 333BGLView::DirectConnected(direct_buffer_info* info) 334{ 335 printf("BGLView::DirectConnected\n"); 336 if (fClipInfo == NULL) { 337 fClipInfo = new (std::nothrow) glview_direct_info(); 338 if (fClipInfo == NULL) 339 return; 340 } 341 342 direct_buffer_info* localInfo = fClipInfo->direct_info; 343 344 _LockDraw(); 345 switch (info->buffer_state & B_DIRECT_MODE_MASK) { 346 case B_DIRECT_START: 347 fClipInfo->direct_connected = true; 348 memcpy(localInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE); 349 break; 350 351 case B_DIRECT_MODIFY: 352 memcpy(localInfo, info, DIRECT_BUFFER_INFO_AREA_SIZE); 353 break; 354 355 case B_DIRECT_STOP: 356 fClipInfo->direct_connected = false; 357 break; 358 } 359 360 if (fRenderer) 361 _CallDirectConnected(); 362 363 _UnlockDraw(); 364} 365 366 367void 368BGLView::EnableDirectMode(bool enabled) 369{ 370 printf("BGLView::EnableDirectMode: %d\n", (int)enabled); 371 if (fRenderer) 372 fRenderer->EnableDirectMode(enabled); 373 if (fClipInfo == NULL) { 374 fClipInfo = new (std::nothrow) glview_direct_info(); 375 if (fClipInfo == NULL) 376 return; 377 } 378 379 fClipInfo->enable_direct_mode = enabled; 380} 381 382 383void 384BGLView::_LockDraw() 385{ 386 if (!fClipInfo || !fClipInfo->enable_direct_mode) 387 return; 388 389 fDrawLock.Lock(); 390} 391 392 393void 394BGLView::_UnlockDraw() 395{ 396 if (!fClipInfo || !fClipInfo->enable_direct_mode) 397 return; 398 399 fDrawLock.Unlock(); 400} 401 402 403void 404BGLView::_CallDirectConnected() 405{ 406 if (!fClipInfo || !fClipInfo->direct_connected) { 407 fRenderer->DirectConnected(NULL); 408 return; 409 } 410 411 direct_buffer_info* localInfo = fClipInfo->direct_info; 412 direct_buffer_info* info = (direct_buffer_info*)malloc( 413 DIRECT_BUFFER_INFO_AREA_SIZE); 414 if (info == NULL) 415 return; 416 417 memcpy(info, localInfo, DIRECT_BUFFER_INFO_AREA_SIZE); 418 419 // Collect the rects into a BRegion, then clip to the view's bounds 420 BRegion region; 421 for (uint32 c = 0; c < localInfo->clip_list_count; c++) 422 region.Include(localInfo->clip_list[c]); 423 BRegion boundsRegion = fBounds.OffsetByCopy(localInfo->window_bounds.left, 424 localInfo->window_bounds.top); 425 info->window_bounds = boundsRegion.RectAtInt(0); 426 // window_bounds are now view bounds 427 region.IntersectWith(&boundsRegion); 428 429 info->clip_list_count = region.CountRects(); 430 info->clip_bounds = region.FrameInt(); 431 432 for (uint32 c = 0; c < info->clip_list_count; c++) 433 info->clip_list[c] = region.RectAtInt(c); 434 fRenderer->DirectConnected(info); 435 free(info); 436} 437 438 439//---- virtual reserved methods ---------- 440 441 442void BGLView::_ReservedGLView1() {} 443void BGLView::_ReservedGLView2() {} 444void BGLView::_ReservedGLView3() {} 445void BGLView::_ReservedGLView4() {} 446void BGLView::_ReservedGLView5() {} 447void BGLView::_ReservedGLView6() {} 448void BGLView::_ReservedGLView7() {} 449void BGLView::_ReservedGLView8() {} 450 451 452// #pragma mark - 453 454 455// BeOS compatibility: contrary to others BView's contructors, 456// BGLView one wants a non-const name argument. 457BGLView::BGLView(BRect rect, char* name, ulong resizingMode, ulong mode, 458 ulong options) 459 : 460 BView(rect, name, B_FOLLOW_ALL_SIDES, mode | B_WILL_DRAW | B_FRAME_EVENTS), 461 fGc(NULL), 462 fOptions(options), 463 fDitherCount(0), 464 fDrawLock("BGLView draw lock"), 465 fDisplayLock("BGLView display lock"), 466 fClipInfo(NULL), 467 fRenderer(NULL), 468 fDitherMap(NULL) 469{ 470 fRenderer = GLRendererRoster::Roster()->GetRenderer(this, options); 471} 472 473 474#if 0 475// TODO: implement BGLScreen class... 476 477 478BGLScreen::BGLScreen(char* name, ulong screenMode, ulong options, 479 status_t* error, bool debug) 480 : 481 BWindowScreen(name, screenMode, error, debug) 482{ 483} 484 485 486BGLScreen::~BGLScreen() 487{ 488} 489 490 491void 492BGLScreen::LockGL() 493{ 494} 495 496 497void 498BGLScreen::UnlockGL() 499{ 500} 501 502 503void 504BGLScreen::SwapBuffers() 505{ 506} 507 508 509void 510BGLScreen::ErrorCallback(unsigned long errorCode) 511{ 512 // Mesa's GLenum is not ulong but uint! 513 char msg[32]; 514 sprintf(msg, "GL: Error code $%04lx.", errorCode); 515 // debugger(msg); 516 fprintf(stderr, "%s\n", msg); 517 return; 518} 519 520 521void 522BGLScreen::ScreenConnected(bool enabled) 523{ 524} 525 526 527void 528BGLScreen::FrameResized(float width, float height) 529{ 530 return BWindowScreen::FrameResized(width, height); 531} 532 533 534status_t 535BGLScreen::Perform(perform_code d, void* arg) 536{ 537 return BWindowScreen::Perform(d, arg); 538} 539 540 541status_t 542BGLScreen::Archive(BMessage* data, bool deep) const 543{ 544 return BWindowScreen::Archive(data, deep); 545} 546 547 548void 549BGLScreen::MessageReceived(BMessage* msg) 550{ 551 BWindowScreen::MessageReceived(msg); 552} 553 554 555void 556BGLScreen::Show() 557{ 558 BWindowScreen::Show(); 559} 560 561 562void 563BGLScreen::Hide() 564{ 565 BWindowScreen::Hide(); 566} 567 568 569BHandler* 570BGLScreen::ResolveSpecifier(BMessage* msg, int32 index, BMessage* specifier, 571 int32 form, const char* property) 572{ 573 return BWindowScreen::ResolveSpecifier(msg, index, specifier, 574 form, property); 575} 576 577 578status_t 579BGLScreen::GetSupportedSuites(BMessage* data) 580{ 581 return BWindowScreen::GetSupportedSuites(data); 582} 583 584 585//---- virtual reserved methods ---------- 586 587void BGLScreen::_ReservedGLScreen1() {} 588void BGLScreen::_ReservedGLScreen2() {} 589void BGLScreen::_ReservedGLScreen3() {} 590void BGLScreen::_ReservedGLScreen4() {} 591void BGLScreen::_ReservedGLScreen5() {} 592void BGLScreen::_ReservedGLScreen6() {} 593void BGLScreen::_ReservedGLScreen7() {} 594void BGLScreen::_ReservedGLScreen8() {} 595#endif 596 597 598const char* color_space_name(color_space space) 599{ 600#define C2N(a) case a: return #a 601 602 switch (space) { 603 C2N(B_RGB24); 604 C2N(B_RGB32); 605 C2N(B_RGBA32); 606 C2N(B_RGB32_BIG); 607 C2N(B_RGBA32_BIG); 608 C2N(B_GRAY8); 609 C2N(B_GRAY1); 610 C2N(B_RGB16); 611 C2N(B_RGB15); 612 C2N(B_RGBA15); 613 C2N(B_CMAP8); 614 default: 615 return "Unknown!"; 616 }; 617 618#undef C2N 619}; 620 621 622glview_direct_info::glview_direct_info() 623{ 624 // TODO: See direct_window_data() in app_server's ServerWindow.cpp 625 direct_info = (direct_buffer_info*)calloc(1, DIRECT_BUFFER_INFO_AREA_SIZE); 626 direct_connected = false; 627 enable_direct_mode = false; 628} 629 630 631glview_direct_info::~glview_direct_info() 632{ 633 free(direct_info); 634} 635 636