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