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