src/video/haiku/SDL_BWin.h
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 8093 b43765095a6f
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Fixed bug 2374 - Update copyright for 2014...

Is it that time already??
     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
    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_FULLSCREEN
    60 };
    61 
    62 
    63 class 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 
   439 private:
   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