1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20 */ 21 22 #ifndef _SDL_BWin_h 23 #define _SDL_BWin_h 24 25 #ifdef __cplusplus 26 extern "C" { 27 #endif 28 29 #include "../../SDL_internal.h" 30 #include "SDL.h" 31 #include "SDL_syswm.h" 32 #include "SDL_bframebuffer.h" 33 34 #ifdef __cplusplus 35 } 36 #endif 37 38 #include <stdio.h> 39 #include <AppKit.h> 40 #include <InterfaceKit.h> 41 #include <be/game/DirectWindow.h> 42 #if SDL_VIDEO_OPENGL 43 #include <be/opengl/GLView.h> 44 #endif 45 #include "SDL_events.h" 46 #include "../../main/haiku/SDL_BApp.h" 47 48 49 enum WinCommands { 50 BWIN_MOVE_WINDOW, 51 BWIN_RESIZE_WINDOW, 52 BWIN_SHOW_WINDOW, 53 BWIN_HIDE_WINDOW, 54 BWIN_MAXIMIZE_WINDOW, 55 BWIN_MINIMIZE_WINDOW, 56 BWIN_RESTORE_WINDOW, 57 BWIN_SET_TITLE, 58 BWIN_SET_BORDERED, 59 BWIN_SET_RESIZABLE, 60 BWIN_FULLSCREEN 61 }; 62 63 64 class SDL_BWin:public BDirectWindow 65 { 66 public: 67 /* Constructor/Destructor */ SDL_BWin(BRect bounds,window_look look,uint32 flags)68 SDL_BWin(BRect bounds, window_look look, uint32 flags) 69 : BDirectWindow(bounds, "Untitled", look, B_NORMAL_WINDOW_FEEL, flags) 70 { 71 _last_buttons = 0; 72 73 #if SDL_VIDEO_OPENGL 74 _SDL_GLView = NULL; 75 #endif 76 _shown = false; 77 _inhibit_resize = false; 78 _mouse_focused = false; 79 _prev_frame = NULL; 80 81 /* Handle framebuffer stuff */ 82 _connected = _connection_disabled = false; 83 _buffer_created = _buffer_dirty = false; 84 _trash_window_buffer = false; 85 _buffer_locker = new BLocker(); 86 _bitmap = NULL; 87 _clips = NULL; 88 89 #ifdef DRAWTHREAD 90 _draw_thread_id = spawn_thread(BE_DrawThread, "drawing_thread", 91 B_NORMAL_PRIORITY, (void*) this); 92 resume_thread(_draw_thread_id); 93 #endif 94 } 95 ~SDL_BWin()96 virtual ~ SDL_BWin() 97 { 98 Lock(); 99 _connection_disabled = true; 100 int32 result; 101 102 #if SDL_VIDEO_OPENGL 103 if (_SDL_GLView) { 104 _SDL_GLView->UnlockGL(); 105 RemoveChild(_SDL_GLView); /* Why was this outside the if 106 statement before? */ 107 } 108 109 #endif 110 Unlock(); 111 #if SDL_VIDEO_OPENGL 112 if (_SDL_GLView) { 113 delete _SDL_GLView; 114 } 115 #endif 116 117 /* Clean up framebuffer stuff */ 118 _buffer_locker->Lock(); 119 #ifdef DRAWTHREAD 120 wait_for_thread(_draw_thread_id, &result); 121 #endif 122 free(_clips); 123 delete _buffer_locker; 124 } 125 126 127 /* * * * * OpenGL functionality * * * * */ 128 #if SDL_VIDEO_OPENGL CreateGLView(Uint32 gl_flags)129 virtual BGLView *CreateGLView(Uint32 gl_flags) { 130 Lock(); 131 if (_SDL_GLView == NULL) { 132 _SDL_GLView = new BGLView(Bounds(), "SDL GLView", 133 B_FOLLOW_ALL_SIDES, 134 (B_WILL_DRAW | B_FRAME_EVENTS), 135 gl_flags); 136 } 137 AddChild(_SDL_GLView); 138 _SDL_GLView->EnableDirectMode(true); 139 _SDL_GLView->LockGL(); /* "New" GLViews are created */ 140 Unlock(); 141 return (_SDL_GLView); 142 } 143 RemoveGLView()144 virtual void RemoveGLView() { 145 Lock(); 146 if(_SDL_GLView) { 147 _SDL_GLView->UnlockGL(); 148 RemoveChild(_SDL_GLView); 149 } 150 Unlock(); 151 } 152 SwapBuffers(void)153 virtual void SwapBuffers(void) { 154 _SDL_GLView->UnlockGL(); 155 _SDL_GLView->LockGL(); 156 _SDL_GLView->SwapBuffers(); 157 } 158 #endif 159 160 /* * * * * Framebuffering* * * * */ DirectConnected(direct_buffer_info * info)161 virtual void DirectConnected(direct_buffer_info *info) { 162 if(!_connected && _connection_disabled) { 163 return; 164 } 165 166 /* Determine if the pixel buffer is usable after this update */ 167 _trash_window_buffer = _trash_window_buffer 168 || ((info->buffer_state & B_BUFFER_RESIZED) 169 || (info->buffer_state & B_BUFFER_RESET) 170 || (info->driver_state == B_MODE_CHANGED)); 171 LockBuffer(); 172 173 switch(info->buffer_state & B_DIRECT_MODE_MASK) { 174 case B_DIRECT_START: 175 _connected = true; 176 177 case B_DIRECT_MODIFY: 178 if(_clips) { 179 free(_clips); 180 _clips = NULL; 181 } 182 183 _num_clips = info->clip_list_count; 184 _clips = (clipping_rect *)malloc(_num_clips*sizeof(clipping_rect)); 185 if(_clips) { 186 memcpy(_clips, info->clip_list, 187 _num_clips*sizeof(clipping_rect)); 188 189 _bits = (uint8*) info->bits; 190 _row_bytes = info->bytes_per_row; 191 _bounds = info->window_bounds; 192 _bytes_per_px = info->bits_per_pixel / 8; 193 _buffer_dirty = true; 194 } 195 break; 196 197 case B_DIRECT_STOP: 198 _connected = false; 199 break; 200 } 201 #if SDL_VIDEO_OPENGL 202 if(_SDL_GLView) { 203 _SDL_GLView->DirectConnected(info); 204 } 205 #endif 206 207 208 /* Call the base object directconnected */ 209 BDirectWindow::DirectConnected(info); 210 211 UnlockBuffer(); 212 213 } 214 215 216 217 218 /* * * * * Event sending * * * * */ 219 /* Hook functions */ FrameMoved(BPoint origin)220 virtual void FrameMoved(BPoint origin) { 221 /* Post a message to the BApp so that it can handle the window event */ 222 BMessage msg(BAPP_WINDOW_MOVED); 223 msg.AddInt32("window-x", (int)origin.x); 224 msg.AddInt32("window-y", (int)origin.y); 225 _PostWindowEvent(msg); 226 227 /* Perform normal hook operations */ 228 BDirectWindow::FrameMoved(origin); 229 } 230 FrameResized(float width,float height)231 virtual void FrameResized(float width, float height) { 232 /* Post a message to the BApp so that it can handle the window event */ 233 BMessage msg(BAPP_WINDOW_RESIZED); 234 235 msg.AddInt32("window-w", (int)width + 1); 236 msg.AddInt32("window-h", (int)height + 1); 237 _PostWindowEvent(msg); 238 239 /* Perform normal hook operations */ 240 BDirectWindow::FrameResized(width, height); 241 } 242 QuitRequested()243 virtual bool QuitRequested() { 244 BMessage msg(BAPP_WINDOW_CLOSE_REQUESTED); 245 _PostWindowEvent(msg); 246 247 /* We won't allow a quit unless asked by DestroyWindow() */ 248 return false; 249 } 250 WindowActivated(bool active)251 virtual void WindowActivated(bool active) { 252 BMessage msg(BAPP_KEYBOARD_FOCUS); /* Mouse focus sold separately */ 253 _PostWindowEvent(msg); 254 } 255 Zoom(BPoint origin,float width,float height)256 virtual void Zoom(BPoint origin, 257 float width, 258 float height) { 259 BMessage msg(BAPP_MAXIMIZE); /* Closest thing to maximization Haiku has */ 260 _PostWindowEvent(msg); 261 262 /* Before the window zooms, record its size */ 263 if( !_prev_frame ) 264 _prev_frame = new BRect(Frame()); 265 266 /* Perform normal hook operations */ 267 BDirectWindow::Zoom(origin, width, height); 268 } 269 270 /* Member functions */ Show()271 virtual void Show() { 272 while(IsHidden()) { 273 BDirectWindow::Show(); 274 } 275 _shown = true; 276 277 BMessage msg(BAPP_SHOW); 278 _PostWindowEvent(msg); 279 } 280 Hide()281 virtual void Hide() { 282 BDirectWindow::Hide(); 283 _shown = false; 284 285 BMessage msg(BAPP_HIDE); 286 _PostWindowEvent(msg); 287 } 288 Minimize(bool minimize)289 virtual void Minimize(bool minimize) { 290 BDirectWindow::Minimize(minimize); 291 int32 minState = (minimize ? BAPP_MINIMIZE : BAPP_RESTORE); 292 293 BMessage msg(minState); 294 _PostWindowEvent(msg); 295 } 296 297 298 /* BView message interruption */ DispatchMessage(BMessage * msg,BHandler * target)299 virtual void DispatchMessage(BMessage * msg, BHandler * target) 300 { 301 BPoint where; /* Used by mouse moved */ 302 int32 buttons; /* Used for mouse button events */ 303 int32 key; /* Used for key events */ 304 305 switch (msg->what) { 306 case B_MOUSE_MOVED: 307 int32 transit; 308 if (msg->FindPoint("where", &where) == B_OK 309 && msg->FindInt32("be:transit", &transit) == B_OK) { 310 _MouseMotionEvent(where, transit); 311 } 312 313 /* FIXME: Apparently a button press/release event might be dropped 314 if made before before a different button is released. Does 315 B_MOUSE_MOVED have the data needed to check if a mouse button 316 state has changed? */ 317 if (msg->FindInt32("buttons", &buttons) == B_OK) { 318 _MouseButtonEvent(buttons); 319 } 320 break; 321 322 case B_MOUSE_DOWN: 323 case B_MOUSE_UP: 324 /* _MouseButtonEvent() detects any and all buttons that may have 325 changed state, as well as that button's new state */ 326 if (msg->FindInt32("buttons", &buttons) == B_OK) { 327 _MouseButtonEvent(buttons); 328 } 329 break; 330 331 case B_MOUSE_WHEEL_CHANGED: 332 float x, y; 333 if (msg->FindFloat("be:wheel_delta_x", &x) == B_OK 334 && msg->FindFloat("be:wheel_delta_y", &y) == B_OK) { 335 _MouseWheelEvent((int)x, (int)y); 336 } 337 break; 338 339 case B_KEY_DOWN: 340 { 341 int32 i = 0; 342 int8 byte; 343 int8 bytes[4] = { 0, 0, 0, 0 }; 344 while (i < 4 && msg->FindInt8("byte", i, &byte) == B_OK) { 345 bytes[i] = byte; 346 i++; 347 } 348 if (msg->FindInt32("key", &key) == B_OK) { 349 _KeyEvent((SDL_Scancode)key, &bytes[0], i, SDL_PRESSED); 350 } 351 } 352 break; 353 354 case B_UNMAPPED_KEY_DOWN: /* modifier keys are unmapped */ 355 if (msg->FindInt32("key", &key) == B_OK) { 356 _KeyEvent((SDL_Scancode)key, NULL, 0, SDL_PRESSED); 357 } 358 break; 359 360 case B_KEY_UP: 361 case B_UNMAPPED_KEY_UP: /* modifier keys are unmapped */ 362 if (msg->FindInt32("key", &key) == B_OK) { 363 _KeyEvent(key, NULL, 0, SDL_RELEASED); 364 } 365 break; 366 367 default: 368 /* move it after switch{} so it's always handled 369 that way we keep Haiku features like: 370 - CTRL+Q to close window (and other shortcuts) 371 - PrintScreen to make screenshot into /boot/home 372 - etc.. */ 373 /* BDirectWindow::DispatchMessage(msg, target); */ 374 break; 375 } 376 377 BDirectWindow::DispatchMessage(msg, target); 378 } 379 380 /* Handle command messages */ MessageReceived(BMessage * message)381 virtual void MessageReceived(BMessage* message) { 382 switch (message->what) { 383 /* Handle commands from SDL */ 384 case BWIN_SET_TITLE: 385 _SetTitle(message); 386 break; 387 case BWIN_MOVE_WINDOW: 388 _MoveTo(message); 389 break; 390 case BWIN_RESIZE_WINDOW: 391 _ResizeTo(message); 392 break; 393 case BWIN_SET_BORDERED: 394 _SetBordered(message); 395 break; 396 case BWIN_SET_RESIZABLE: 397 _SetResizable(message); 398 break; 399 case BWIN_SHOW_WINDOW: 400 Show(); 401 break; 402 case BWIN_HIDE_WINDOW: 403 Hide(); 404 break; 405 case BWIN_MAXIMIZE_WINDOW: 406 BWindow::Zoom(); 407 break; 408 case BWIN_MINIMIZE_WINDOW: 409 Minimize(true); 410 break; 411 case BWIN_RESTORE_WINDOW: 412 _Restore(); 413 break; 414 case BWIN_FULLSCREEN: 415 _SetFullScreen(message); 416 break; 417 default: 418 /* Perform normal message handling */ 419 BDirectWindow::MessageReceived(message); 420 break; 421 } 422 423 } 424 425 426 427 /* Accessor methods */ IsShown()428 bool IsShown() { return _shown; } GetID()429 int32 GetID() { return _id; } GetRowBytes()430 uint32 GetRowBytes() { return _row_bytes; } GetFbX()431 int32 GetFbX() { return _bounds.left; } GetFbY()432 int32 GetFbY() { return _bounds.top; } ConnectionEnabled()433 bool ConnectionEnabled() { return !_connection_disabled; } Connected()434 bool Connected() { return _connected; } GetClips()435 clipping_rect *GetClips() { return _clips; } GetNumClips()436 int32 GetNumClips() { return _num_clips; } GetBufferPx()437 uint8* GetBufferPx() { return _bits; } GetBytesPerPx()438 int32 GetBytesPerPx() { return _bytes_per_px; } CanTrashWindowBuffer()439 bool CanTrashWindowBuffer() { return _trash_window_buffer; } BufferExists()440 bool BufferExists() { return _buffer_created; } BufferIsDirty()441 bool BufferIsDirty() { return _buffer_dirty; } GetBitmap()442 BBitmap *GetBitmap() { return _bitmap; } 443 #if SDL_VIDEO_OPENGL GetGLView()444 BGLView *GetGLView() { return _SDL_GLView; } 445 #endif 446 447 /* Setter methods */ SetID(int32 id)448 void SetID(int32 id) { _id = id; } SetBufferExists(bool bufferExists)449 void SetBufferExists(bool bufferExists) { _buffer_created = bufferExists; } LockBuffer()450 void LockBuffer() { _buffer_locker->Lock(); } UnlockBuffer()451 void UnlockBuffer() { _buffer_locker->Unlock(); } SetBufferDirty(bool bufferDirty)452 void SetBufferDirty(bool bufferDirty) { _buffer_dirty = bufferDirty; } SetTrashBuffer(bool trash)453 void SetTrashBuffer(bool trash) { _trash_window_buffer = trash; } SetBitmap(BBitmap * bitmap)454 void SetBitmap(BBitmap *bitmap) { _bitmap = bitmap; } 455 456 457 private: 458 /* Event redirection */ _MouseMotionEvent(BPoint & where,int32 transit)459 void _MouseMotionEvent(BPoint &where, int32 transit) { 460 if(transit == B_EXITED_VIEW) { 461 /* Change mouse focus */ 462 if(_mouse_focused) { 463 _MouseFocusEvent(false); 464 } 465 } else { 466 /* Change mouse focus */ 467 if (!_mouse_focused) { 468 _MouseFocusEvent(true); 469 } 470 BMessage msg(BAPP_MOUSE_MOVED); 471 msg.AddInt32("x", (int)where.x); 472 msg.AddInt32("y", (int)where.y); 473 474 _PostWindowEvent(msg); 475 } 476 } 477 _MouseFocusEvent(bool focusGained)478 void _MouseFocusEvent(bool focusGained) { 479 _mouse_focused = focusGained; 480 BMessage msg(BAPP_MOUSE_FOCUS); 481 msg.AddBool("focusGained", focusGained); 482 _PostWindowEvent(msg); 483 484 /* FIXME: Why were these here? 485 if false: be_app->SetCursor(B_HAND_CURSOR); 486 if true: SDL_SetCursor(NULL); */ 487 } 488 _MouseButtonEvent(int32 buttons)489 void _MouseButtonEvent(int32 buttons) { 490 int32 buttonStateChange = buttons ^ _last_buttons; 491 492 /* Make sure at least one button has changed state */ 493 if( !(buttonStateChange) ) { 494 return; 495 } 496 497 /* Add any mouse button events */ 498 if(buttonStateChange & B_PRIMARY_MOUSE_BUTTON) { 499 _SendMouseButton(SDL_BUTTON_LEFT, buttons & 500 B_PRIMARY_MOUSE_BUTTON); 501 } 502 if(buttonStateChange & B_SECONDARY_MOUSE_BUTTON) { 503 _SendMouseButton(SDL_BUTTON_RIGHT, buttons & 504 B_PRIMARY_MOUSE_BUTTON); 505 } 506 if(buttonStateChange & B_TERTIARY_MOUSE_BUTTON) { 507 _SendMouseButton(SDL_BUTTON_MIDDLE, buttons & 508 B_PRIMARY_MOUSE_BUTTON); 509 } 510 511 _last_buttons = buttons; 512 } 513 _SendMouseButton(int32 button,int32 state)514 void _SendMouseButton(int32 button, int32 state) { 515 BMessage msg(BAPP_MOUSE_BUTTON); 516 msg.AddInt32("button-id", button); 517 msg.AddInt32("button-state", state); 518 _PostWindowEvent(msg); 519 } 520 _MouseWheelEvent(int32 x,int32 y)521 void _MouseWheelEvent(int32 x, int32 y) { 522 /* Create a message to pass along to the BeApp thread */ 523 BMessage msg(BAPP_MOUSE_WHEEL); 524 msg.AddInt32("xticks", x); 525 msg.AddInt32("yticks", y); 526 _PostWindowEvent(msg); 527 } 528 _KeyEvent(int32 keyCode,const int8 * keyUtf8,const ssize_t & len,int32 keyState)529 void _KeyEvent(int32 keyCode, const int8 *keyUtf8, const ssize_t & len, int32 keyState) { 530 /* Create a message to pass along to the BeApp thread */ 531 BMessage msg(BAPP_KEY); 532 msg.AddInt32("key-state", keyState); 533 msg.AddInt32("key-scancode", keyCode); 534 if (keyUtf8 != NULL) { 535 msg.AddData("key-utf8", B_INT8_TYPE, (const void*)keyUtf8, len); 536 } 537 be_app->PostMessage(&msg); 538 } 539 _RepaintEvent()540 void _RepaintEvent() { 541 /* Force a repaint: Call the SDL exposed event */ 542 BMessage msg(BAPP_REPAINT); 543 _PostWindowEvent(msg); 544 } _PostWindowEvent(BMessage & msg)545 void _PostWindowEvent(BMessage &msg) { 546 msg.AddInt32("window-id", _id); 547 be_app->PostMessage(&msg); 548 } 549 550 /* Command methods (functions called upon by SDL) */ _SetTitle(BMessage * msg)551 void _SetTitle(BMessage *msg) { 552 const char *title; 553 if( 554 msg->FindString("window-title", &title) != B_OK 555 ) { 556 return; 557 } 558 SetTitle(title); 559 } 560 _MoveTo(BMessage * msg)561 void _MoveTo(BMessage *msg) { 562 int32 x, y; 563 if( 564 msg->FindInt32("window-x", &x) != B_OK || 565 msg->FindInt32("window-y", &y) != B_OK 566 ) { 567 return; 568 } 569 MoveTo(x, y); 570 } 571 _ResizeTo(BMessage * msg)572 void _ResizeTo(BMessage *msg) { 573 int32 w, h; 574 if( 575 msg->FindInt32("window-w", &w) != B_OK || 576 msg->FindInt32("window-h", &h) != B_OK 577 ) { 578 return; 579 } 580 ResizeTo(w, h); 581 } 582 _SetBordered(BMessage * msg)583 void _SetBordered(BMessage *msg) { 584 bool bEnabled; 585 if(msg->FindBool("window-border", &bEnabled) != B_OK) { 586 return; 587 } 588 SetLook(bEnabled ? B_BORDERED_WINDOW_LOOK : B_NO_BORDER_WINDOW_LOOK); 589 } 590 _SetResizable(BMessage * msg)591 void _SetResizable(BMessage *msg) { 592 bool bEnabled; 593 if(msg->FindBool("window-resizable", &bEnabled) != B_OK) { 594 return; 595 } 596 if (bEnabled) { 597 SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_ZOOMABLE)); 598 } else { 599 SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_ZOOMABLE)); 600 } 601 } 602 _Restore()603 void _Restore() { 604 if(IsMinimized()) { 605 Minimize(false); 606 } else if(IsHidden()) { 607 Show(); 608 } else if(_prev_frame != NULL) { /* Zoomed */ 609 MoveTo(_prev_frame->left, _prev_frame->top); 610 ResizeTo(_prev_frame->Width(), _prev_frame->Height()); 611 } 612 } 613 _SetFullScreen(BMessage * msg)614 void _SetFullScreen(BMessage *msg) { 615 bool fullscreen; 616 if( 617 msg->FindBool("fullscreen", &fullscreen) != B_OK 618 ) { 619 return; 620 } 621 SetFullScreen(fullscreen); 622 } 623 624 /* Members */ 625 #if SDL_VIDEO_OPENGL 626 BGLView * _SDL_GLView; 627 #endif 628 629 int32 _last_buttons; 630 int32 _id; /* Window id used by SDL_BApp */ 631 bool _mouse_focused; /* Does this window have mouse focus? */ 632 bool _shown; 633 bool _inhibit_resize; 634 635 BRect *_prev_frame; /* Previous position and size of the window */ 636 637 /* Framebuffer members */ 638 bool _connected, 639 _connection_disabled, 640 _buffer_created, 641 _buffer_dirty, 642 _trash_window_buffer; 643 uint8 *_bits; 644 uint32 _row_bytes; 645 clipping_rect _bounds; 646 BLocker *_buffer_locker; 647 clipping_rect *_clips; 648 int32 _num_clips; 649 int32 _bytes_per_px; 650 thread_id _draw_thread_id; 651 652 BBitmap *_bitmap; 653 }; 654 655 656 /* FIXME: 657 * An explanation of framebuffer flags. 658 * 659 * _connected - Original variable used to let the drawing thread know 660 * when changes are being made to the other framebuffer 661 * members. 662 * _connection_disabled - Used to signal to the drawing thread that the window 663 * is closing, and the thread should exit. 664 * _buffer_created - True if the current buffer is valid 665 * _buffer_dirty - True if the window should be redrawn. 666 * _trash_window_buffer - True if the window buffer needs to be trashed partway 667 * through a draw cycle. Occurs when the previous 668 * buffer provided by DirectConnected() is invalidated. 669 */ 670 #endif 671