1 /* 2 * Copyright 2003-2006, Haiku Inc. 3 * Authors: 4 * Stefano Ceccherini (burton666@libero.it). 5 * Carwyn Jones (turok2@currantbun.com) 6 * 7 * Distributed under the terms of the MIT License. 8 */ 9 10 11 #include <DirectWindow.h> 12 #include <Screen.h> 13 14 #include <clipping.h> 15 #include <AppServerLink.h> 16 #include <DirectWindowPrivate.h> 17 #include <ServerProtocol.h> 18 19 //#define DEBUG 1 20 21 #if DEBUG 22 #include <string.h> 23 #include <stdio.h> 24 #endif 25 26 // We don't need this kind of locking, since the directDaemonFunc 27 // doesn't access critical shared data. 28 #define DW_NEEDS_LOCKING 0 29 30 enum dw_status_bits { 31 DW_STATUS_AREA_CLONED = 0x1, 32 DW_STATUS_THREAD_STARTED = 0x2, 33 DW_STATUS_SEM_CREATED = 0x4 34 }; 35 36 37 #if DEBUG 38 static void 39 print_direct_buffer_state(const direct_buffer_state &state) 40 { 41 char string[128]; 42 int modeState = state & B_DIRECT_MODE_MASK; 43 if (modeState == B_DIRECT_START) 44 strcpy(string, "B_DIRECT_START"); 45 else if (modeState == B_DIRECT_MODIFY) 46 strcpy(string, "B_DIRECT_MODIFY"); 47 else if (modeState == B_DIRECT_STOP) 48 strcpy(string, "B_DIRECT_STOP"); 49 50 if (state & B_CLIPPING_MODIFIED) 51 strcat(string, " | B_CLIPPING_MODIFIED"); 52 if (state & B_BUFFER_RESIZED) 53 strcat(string, " | B_BUFFER_RESIZED"); 54 if (state & B_BUFFER_MOVED) 55 strcat(string, " | B_BUFFER_MOVED"); 56 if (state & B_BUFFER_RESET) 57 strcat(string, " | B_BUFFER_RESET"); 58 59 printf("direct_buffer_state: %s\n", string); 60 } 61 62 63 static void 64 print_direct_driver_state(const direct_driver_state &state) 65 { 66 if (state == 0) 67 return; 68 69 char string[64]; 70 if (state == B_DRIVER_CHANGED) 71 strcpy(string, "B_DRIVER_CHANGED"); 72 else if (state == B_MODE_CHANGED) 73 strcpy(string, "B_MODE_CHANGED"); 74 75 printf("direct_driver_state: %s\n", string); 76 } 77 78 79 static void 80 print_direct_buffer_info(const direct_buffer_info &info) 81 { 82 print_direct_buffer_state(info.buffer_state); 83 print_direct_driver_state(info.driver_state); 84 } 85 86 #endif 87 88 BDirectWindow::BDirectWindow(BRect frame, const char *title, window_type type, 89 uint32 flags, uint32 workspace) 90 : BWindow(frame, title, type, flags, workspace) 91 { 92 InitData(); 93 } 94 95 96 BDirectWindow::BDirectWindow(BRect frame, const char *title, window_look look, 97 window_feel feel, uint32 flags, uint32 workspace) 98 : BWindow(frame, title, look, feel, flags, workspace) 99 { 100 InitData(); 101 } 102 103 104 BDirectWindow::~BDirectWindow() 105 { 106 DisposeData(); 107 } 108 109 110 // start of regular BWindow API 111 BArchivable * 112 BDirectWindow::Instantiate(BMessage *data) 113 { 114 return NULL; 115 } 116 117 118 status_t 119 BDirectWindow::Archive(BMessage *data, bool deep) const 120 { 121 return inherited::Archive(data, deep); 122 } 123 124 125 void 126 BDirectWindow::Quit() 127 { 128 inherited::Quit(); 129 } 130 131 132 void 133 BDirectWindow::DispatchMessage(BMessage *message, BHandler *handler) 134 { 135 inherited::DispatchMessage(message, handler); 136 } 137 138 139 void 140 BDirectWindow::MessageReceived(BMessage *message) 141 { 142 inherited::MessageReceived(message); 143 } 144 145 146 void 147 BDirectWindow::FrameMoved(BPoint new_position) 148 { 149 inherited::FrameMoved(new_position); 150 } 151 152 153 void 154 BDirectWindow::WorkspacesChanged(uint32 old_ws, uint32 new_ws) 155 { 156 inherited::WorkspacesChanged(old_ws, new_ws); 157 } 158 159 160 void 161 BDirectWindow::WorkspaceActivated(int32 ws, bool state) 162 { 163 inherited::WorkspaceActivated(ws, state); 164 } 165 166 167 void 168 BDirectWindow::FrameResized(float new_width, float new_height) 169 { 170 inherited::FrameResized(new_width, new_height); 171 } 172 173 174 void 175 BDirectWindow::Minimize(bool minimize) 176 { 177 inherited::Minimize(minimize); 178 } 179 180 181 void 182 BDirectWindow::Zoom(BPoint rec_position, float rec_width, float rec_height) 183 { 184 inherited::Zoom(rec_position, rec_width, rec_height); 185 } 186 187 188 void 189 BDirectWindow::ScreenChanged(BRect screen_size, color_space depth) 190 { 191 inherited::ScreenChanged(screen_size, depth); 192 } 193 194 195 void 196 BDirectWindow::MenusBeginning() 197 { 198 inherited::MenusBeginning(); 199 } 200 201 202 void 203 BDirectWindow::MenusEnded() 204 { 205 inherited::MenusEnded(); 206 } 207 208 209 void 210 BDirectWindow::WindowActivated(bool state) 211 { 212 inherited::WindowActivated(state); 213 } 214 215 216 void 217 BDirectWindow::Show() 218 { 219 inherited::Show(); 220 } 221 222 223 void 224 BDirectWindow::Hide() 225 { 226 inherited::Hide(); 227 } 228 229 230 BHandler * 231 BDirectWindow::ResolveSpecifier(BMessage *msg, int32 index, 232 BMessage *specifier, int32 form, const char *property) 233 { 234 return inherited::ResolveSpecifier(msg, index, specifier, form, property); 235 } 236 237 238 status_t 239 BDirectWindow::GetSupportedSuites(BMessage *data) 240 { 241 return inherited::GetSupportedSuites(data); 242 } 243 244 245 status_t 246 BDirectWindow::Perform(perform_code d, void *arg) 247 { 248 return inherited::Perform(d, arg); 249 } 250 251 252 void 253 BDirectWindow::task_looper() 254 { 255 inherited::task_looper(); 256 } 257 258 259 BMessage * 260 BDirectWindow::ConvertToMessage(void *raw, int32 code) 261 { 262 return inherited::ConvertToMessage(raw, code); 263 } 264 265 266 // #pragma mark - BDirectWindow specific API 267 268 269 void 270 BDirectWindow::DirectConnected(direct_buffer_info *info) 271 { 272 // implemented in subclasses 273 } 274 275 276 status_t 277 BDirectWindow::GetClippingRegion(BRegion *region, BPoint *origin) const 278 { 279 if (region == NULL) 280 return B_BAD_VALUE; 281 282 if (IsLocked() || !LockDirect()) 283 return B_ERROR; 284 285 if (fInDirectConnect) { 286 UnlockDirect(); 287 return B_ERROR; 288 } 289 290 // BPoint's coordinates are floats. We can only work 291 // with integers._DaemonStarter 292 int32 originX, originY; 293 if (origin == NULL) { 294 originX = 0; 295 originY = 0; 296 } else { 297 originX = (int32)origin->x; 298 originY = (int32)origin->y; 299 } 300 301 #ifndef HAIKU_TARGET_PLATFORM_DANO 302 // Since we are friend of BRegion, we can access its private members. 303 // Otherwise, we would need to call BRegion::Include(clipping_rect) 304 // for every clipping_rect in our clip_list, and that would be much 305 // more overkill than this (tested ). 306 if (!region->_SetSize(fBufferDesc->clip_list_count)) { 307 UnlockDirect(); 308 return B_NO_MEMORY; 309 } 310 region->fCount = fBufferDesc->clip_list_count; 311 region->fBounds = region->_ConvertToInternal(fBufferDesc->clip_bounds); 312 for (uint32 c = 0; c < fBufferDesc->clip_list_count; c++) { 313 region->fData[c] = region->_ConvertToInternal( 314 fBufferDesc->clip_list[c]); 315 } 316 317 // adjust bounds by the given origin point 318 region->OffsetBy(-originX, -originY); 319 #endif 320 321 UnlockDirect(); 322 323 return B_OK; 324 325 } 326 327 328 status_t 329 BDirectWindow::SetFullScreen(bool enable) 330 { 331 if (fIsFullScreen == enable) 332 return B_OK; 333 334 status_t status = B_ERROR; 335 if (Lock()) { 336 fLink->StartMessage(AS_DIRECT_WINDOW_SET_FULLSCREEN); 337 fLink->Attach<bool>(enable); 338 339 if (fLink->FlushWithReply(status) == B_OK 340 && status == B_OK) { 341 fIsFullScreen = enable; 342 } 343 Unlock(); 344 } 345 return status; 346 } 347 348 349 bool 350 BDirectWindow::IsFullScreen() const 351 { 352 return fIsFullScreen; 353 } 354 355 356 /*static*/ 357 bool 358 BDirectWindow::SupportsWindowMode(screen_id id) 359 { 360 /* display_mode mode; 361 status_t status = BScreen(id).GetMode(&mode); 362 if (status == B_OK) 363 return mode.flags & B_PARALLEL_ACCESS; 364 365 return false;*/ 366 // TODO: Apparently, the above is false for the vesa driver, 367 // but enabling it doesn't do any harm... maybe we should just return always true. 368 // At least, I can't see why window mode shouldn't be supported. 369 // additional NOTE: it probably depends on wether hardware cursor is supported or 370 // not 371 return true; 372 } 373 374 375 // #pragma mark - Private methods 376 377 /* static */ 378 int32 379 BDirectWindow::_DaemonStarter(void *arg) 380 { 381 return static_cast<BDirectWindow *>(arg)->DirectDaemonFunc(); 382 } 383 384 385 int32 386 BDirectWindow::DirectDaemonFunc() 387 { 388 while (!fDaemonKiller) { 389 // This sem is released by the app_server when our 390 // clipping region changes, or when our window is moved, 391 // resized, etc. etc. 392 status_t status; 393 do { 394 status = acquire_sem(fDisableSem); 395 } while (status == B_INTERRUPTED); 396 397 if (status < B_OK) 398 return -1; 399 400 #if DEBUG 401 print_direct_buffer_info(*fBufferDesc); 402 #endif 403 404 if (LockDirect()) { 405 if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK) == B_DIRECT_START) 406 fConnectionEnable = true; 407 408 fInDirectConnect = true; 409 DirectConnected(fBufferDesc); 410 fInDirectConnect = false; 411 412 if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK) == B_DIRECT_STOP) 413 fConnectionEnable = false; 414 415 UnlockDirect(); 416 } 417 418 // The app_server then waits (with a timeout) on this sem. 419 // If we aren't quick enough to release this sem, our app 420 // will be terminated by the app_server 421 if (release_sem(fDisableSemAck) != B_OK) 422 return -1; 423 } 424 425 return 0; 426 } 427 428 429 // LockDirect() and UnlockDirect() are no-op on R5. I tried to call (R5's) LockDirect() 430 // repeatedly, from the same thread and from different threads, nothing happened. 431 // I implemented them anyway, as they were the first methods I wrote 432 // in this class (As you can see, I even needed to cast away their constness 433 // to make them do something useful). 434 // They're not needed though, as the direct_daemon_thread doesn't change 435 // any shared data. They are probably here for future enhancements (see also the 436 // comment in DriverSetup() 437 bool 438 BDirectWindow::LockDirect() const 439 { 440 status_t status = B_OK; 441 442 #if DW_NEEDS_LOCKING 443 BDirectWindow *casted = const_cast<BDirectWindow *>(this); 444 445 if (atomic_add(&casted->fDirectLock, 1) > 0) { 446 do { 447 status = acquire_sem(fDirectSem); 448 } while (status == B_INTERRUPTED); 449 } 450 451 if (status == B_OK) { 452 casted->fDirectLockOwner = find_thread(NULL); 453 casted->fDirectLockCount++; 454 } 455 #endif 456 457 return status == B_OK; 458 } 459 460 461 void 462 BDirectWindow::UnlockDirect() const 463 { 464 #if DW_NEEDS_LOCKING 465 BDirectWindow *casted = const_cast<BDirectWindow *>(this); 466 467 if (atomic_add(&casted->fDirectLock, -1) > 1) 468 release_sem(casted->fDirectSem); 469 470 casted->fDirectLockCount--; 471 #endif 472 } 473 474 475 void 476 BDirectWindow::InitData() 477 { 478 fConnectionEnable = false; 479 fIsFullScreen = false; 480 fInDirectConnect = false; 481 482 fInitStatus = 0; 483 484 fDirectDriverReady = false; 485 fDirectDriverType = 0; 486 fDirectDriverToken = 0; 487 direct_driver = NULL; 488 489 status_t status = B_ERROR; 490 struct direct_window_sync_data syncData; 491 if (Lock()) { 492 fLink->StartMessage(AS_DIRECT_WINDOW_GET_SYNC_DATA); 493 if (fLink->FlushWithReply(status) == B_OK 494 && status == B_OK) { 495 fLink->Read<direct_window_sync_data>(&syncData); 496 } 497 Unlock(); 498 } 499 if (status < B_OK) 500 return; 501 502 #if DW_NEEDS_LOCKING 503 fDirectLock = 0; 504 fDirectLockCount = 0; 505 fDirectLockOwner = -1; 506 fDirectLockStack = NULL; 507 fDirectSem = create_sem(1, "direct sem"); 508 if (fDirectSem > 0) 509 fInitStatus |= DW_STATUS_SEM_CREATED; 510 #endif 511 512 fSourceClippingArea = syncData.area; 513 fDisableSem = syncData.disable_sem; 514 fDisableSemAck = syncData.disable_sem_ack; 515 516 fClonedClippingArea = clone_area("Clone direct area", (void**)&fBufferDesc, 517 B_ANY_ADDRESS, B_READ_AREA, fSourceClippingArea); 518 519 if (fClonedClippingArea > 0) { 520 fInitStatus |= DW_STATUS_AREA_CLONED; 521 522 fDirectDaemonId = spawn_thread(_DaemonStarter, "direct daemon", 523 B_DISPLAY_PRIORITY, this); 524 525 if (fDirectDaemonId > 0) { 526 fDaemonKiller = false; 527 if (resume_thread(fDirectDaemonId) == B_OK) 528 fInitStatus |= DW_STATUS_THREAD_STARTED; 529 else 530 kill_thread(fDirectDaemonId); 531 } 532 } 533 } 534 535 536 void 537 BDirectWindow::DisposeData() 538 { 539 // wait until the connection terminates: we can't destroy 540 // the object until the client receives the B_DIRECT_STOP 541 // notification, or bad things will happen 542 while (fConnectionEnable) 543 snooze(50000); 544 545 LockDirect(); 546 547 if (fInitStatus & DW_STATUS_THREAD_STARTED) { 548 fDaemonKiller = true; 549 // Release this sem, otherwise the Direct daemon thread 550 // will wait forever on it 551 release_sem(fDisableSem); 552 status_t retVal; 553 wait_for_thread(fDirectDaemonId, &retVal); 554 } 555 556 #if DW_NEEDS_LOCKING 557 if (fInitStatus & DW_STATUS_SEM_CREATED) 558 delete_sem(fDirectSem); 559 #endif 560 561 if (fInitStatus & DW_STATUS_AREA_CLONED) 562 delete_area(fClonedClippingArea); 563 } 564 565 566 status_t 567 BDirectWindow::DriverSetup() const 568 { 569 // Unimplemented in R5. 570 // This function is probably here because they wanted, in a future time, 571 // to implement graphic acceleration within BDirectWindow 572 // (in fact, there is also a BDirectDriver member in BDirectWindow, 573 // though it's not used). 574 575 return B_OK; 576 } 577 578 579 void BDirectWindow::_ReservedDirectWindow1() {} 580 void BDirectWindow::_ReservedDirectWindow2() {} 581 void BDirectWindow::_ReservedDirectWindow3() {} 582 void BDirectWindow::_ReservedDirectWindow4() {} 583