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