src/video/haiku/SDL_BWin.h
author Ryan C. Gordon <icculus@icculus.org>
Thu, 29 Sep 2016 22:52:41 -0400
changeset 10385 ad12658bc7ae
parent 9998 f67cf37e9cd4
child 10388 0190965b0ec5
permissions -rw-r--r--
Added SDL_SetWindowResizable(). (thanks, Ethan!)
     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 */
    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 
    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
   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 
   144     virtual void RemoveGLView() {
   145         Lock();
   146         if(_SDL_GLView) {
   147             _SDL_GLView->UnlockGL();
   148             RemoveChild(_SDL_GLView);
   149         }
   150         Unlock();
   151     }
   152 
   153     virtual void SwapBuffers(void) {
   154         _SDL_GLView->UnlockGL();
   155         _SDL_GLView->LockGL();
   156         _SDL_GLView->SwapBuffers();
   157     }
   158 #endif
   159 
   160     /* * * * * Framebuffering* * * * */
   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 */
   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 
   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 
   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 
   251     virtual void WindowActivated(bool active) {
   252         BMessage msg(BAPP_KEYBOARD_FOCUS);  /* Mouse focus sold separately */
   253         _PostWindowEvent(msg);
   254     }
   255 
   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 */
   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 
   281     virtual void Hide() {
   282         BDirectWindow::Hide();
   283         _shown = false;
   284 
   285         BMessage msg(BAPP_HIDE);
   286         _PostWindowEvent(msg);
   287     }
   288 
   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 */
   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         case B_UNMAPPED_KEY_DOWN:      /* modifier keys are unmapped */
   341             if (msg->FindInt32("key", &key) == B_OK) {
   342                 _KeyEvent((SDL_Scancode)key, SDL_PRESSED);
   343             }
   344             break;
   345 
   346         case B_KEY_UP:
   347         case B_UNMAPPED_KEY_UP:        /* modifier keys are unmapped */
   348             if (msg->FindInt32("key", &key) == B_OK) {
   349                 _KeyEvent(key, SDL_RELEASED);
   350             }
   351             break;
   352 
   353         default:
   354             /* move it after switch{} so it's always handled
   355                that way we keep Haiku features like:
   356                - CTRL+Q to close window (and other shortcuts)
   357                - PrintScreen to make screenshot into /boot/home
   358                - etc.. */
   359             /* BDirectWindow::DispatchMessage(msg, target); */
   360             break;
   361         }
   362 
   363         BDirectWindow::DispatchMessage(msg, target);
   364     }
   365 
   366     /* Handle command messages */
   367     virtual void MessageReceived(BMessage* message) {
   368         switch (message->what) {
   369             /* Handle commands from SDL */
   370             case BWIN_SET_TITLE:
   371                 _SetTitle(message);
   372                 break;
   373             case BWIN_MOVE_WINDOW:
   374                 _MoveTo(message);
   375                 break;
   376             case BWIN_RESIZE_WINDOW:
   377                 _ResizeTo(message);
   378                 break;
   379             case BWIN_SET_BORDERED:
   380                 _SetBordered(message);
   381                 break;
   382             case BWIN_SET_RESIZABLE:
   383                 _SetResizable(message);
   384                 break;
   385             case BWIN_SHOW_WINDOW:
   386                 Show();
   387                 break;
   388             case BWIN_HIDE_WINDOW:
   389                 Hide();
   390                 break;
   391             case BWIN_MAXIMIZE_WINDOW:
   392                 BWindow::Zoom();
   393                 break;
   394             case BWIN_MINIMIZE_WINDOW:
   395                 Minimize(true);
   396                 break;
   397             case BWIN_RESTORE_WINDOW:
   398                 _Restore();
   399                 break;
   400             case BWIN_FULLSCREEN:
   401                 _SetFullScreen(message);
   402                 break;
   403             default:
   404                 /* Perform normal message handling */
   405                 BDirectWindow::MessageReceived(message);
   406                 break;
   407         }
   408 
   409     }
   410 
   411 
   412 
   413     /* Accessor methods */
   414     bool IsShown() { return _shown; }
   415     int32 GetID() { return _id; }
   416     uint32 GetRowBytes() { return _row_bytes; }
   417     int32 GetFbX() { return _bounds.left; }
   418     int32 GetFbY() { return _bounds.top; }
   419     bool ConnectionEnabled() { return !_connection_disabled; }
   420     bool Connected() { return _connected; }
   421     clipping_rect *GetClips() { return _clips; }
   422     int32 GetNumClips() { return _num_clips; }
   423     uint8* GetBufferPx() { return _bits; }
   424     int32 GetBytesPerPx() { return _bytes_per_px; }
   425     bool CanTrashWindowBuffer() { return _trash_window_buffer; }
   426     bool BufferExists() { return _buffer_created; }
   427     bool BufferIsDirty() { return _buffer_dirty; }
   428     BBitmap *GetBitmap() { return _bitmap; }
   429 #if SDL_VIDEO_OPENGL
   430     BGLView *GetGLView() { return _SDL_GLView; }
   431 #endif
   432 
   433     /* Setter methods */
   434     void SetID(int32 id) { _id = id; }
   435     void SetBufferExists(bool bufferExists) { _buffer_created = bufferExists; }
   436     void LockBuffer() { _buffer_locker->Lock(); }
   437     void UnlockBuffer() { _buffer_locker->Unlock(); }
   438     void SetBufferDirty(bool bufferDirty) { _buffer_dirty = bufferDirty; }
   439     void SetTrashBuffer(bool trash) { _trash_window_buffer = trash;     }
   440     void SetBitmap(BBitmap *bitmap) { _bitmap = bitmap; }
   441 
   442 
   443 private:
   444     /* Event redirection */
   445     void _MouseMotionEvent(BPoint &where, int32 transit) {
   446         if(transit == B_EXITED_VIEW) {
   447             /* Change mouse focus */
   448             if(_mouse_focused) {
   449                 _MouseFocusEvent(false);
   450             }
   451         } else {
   452             /* Change mouse focus */
   453             if (!_mouse_focused) {
   454                 _MouseFocusEvent(true);
   455             }
   456             BMessage msg(BAPP_MOUSE_MOVED);
   457             msg.AddInt32("x", (int)where.x);
   458             msg.AddInt32("y", (int)where.y);
   459 
   460             _PostWindowEvent(msg);
   461         }
   462     }
   463 
   464     void _MouseFocusEvent(bool focusGained) {
   465         _mouse_focused = focusGained;
   466         BMessage msg(BAPP_MOUSE_FOCUS);
   467         msg.AddBool("focusGained", focusGained);
   468         _PostWindowEvent(msg);
   469 
   470 /* FIXME: Why were these here?
   471  if false: be_app->SetCursor(B_HAND_CURSOR);
   472  if true:  SDL_SetCursor(NULL); */
   473     }
   474 
   475     void _MouseButtonEvent(int32 buttons) {
   476         int32 buttonStateChange = buttons ^ _last_buttons;
   477 
   478         /* Make sure at least one button has changed state */
   479         if( !(buttonStateChange) ) {
   480             return;
   481         }
   482 
   483         /* Add any mouse button events */
   484         if(buttonStateChange & B_PRIMARY_MOUSE_BUTTON) {
   485             _SendMouseButton(SDL_BUTTON_LEFT, buttons &
   486                 B_PRIMARY_MOUSE_BUTTON);
   487         }
   488         if(buttonStateChange & B_SECONDARY_MOUSE_BUTTON) {
   489             _SendMouseButton(SDL_BUTTON_RIGHT, buttons &
   490                 B_PRIMARY_MOUSE_BUTTON);
   491         }
   492         if(buttonStateChange & B_TERTIARY_MOUSE_BUTTON) {
   493             _SendMouseButton(SDL_BUTTON_MIDDLE, buttons &
   494                 B_PRIMARY_MOUSE_BUTTON);
   495         }
   496 
   497         _last_buttons = buttons;
   498     }
   499 
   500     void _SendMouseButton(int32 button, int32 state) {
   501         BMessage msg(BAPP_MOUSE_BUTTON);
   502         msg.AddInt32("button-id", button);
   503         msg.AddInt32("button-state", state);
   504         _PostWindowEvent(msg);
   505     }
   506 
   507     void _MouseWheelEvent(int32 x, int32 y) {
   508         /* Create a message to pass along to the BeApp thread */
   509         BMessage msg(BAPP_MOUSE_WHEEL);
   510         msg.AddInt32("xticks", x);
   511         msg.AddInt32("yticks", y);
   512         _PostWindowEvent(msg);
   513     }
   514 
   515     void _KeyEvent(int32 keyCode, int32 keyState) {
   516         /* Create a message to pass along to the BeApp thread */
   517         BMessage msg(BAPP_KEY);
   518         msg.AddInt32("key-state", keyState);
   519         msg.AddInt32("key-scancode", keyCode);
   520         be_app->PostMessage(&msg);
   521         /* Apparently SDL only uses the scancode */
   522     }
   523 
   524     void _RepaintEvent() {
   525         /* Force a repaint: Call the SDL exposed event */
   526         BMessage msg(BAPP_REPAINT);
   527         _PostWindowEvent(msg);
   528     }
   529     void _PostWindowEvent(BMessage &msg) {
   530         msg.AddInt32("window-id", _id);
   531         be_app->PostMessage(&msg);
   532     }
   533 
   534     /* Command methods (functions called upon by SDL) */
   535     void _SetTitle(BMessage *msg) {
   536         const char *title;
   537         if(
   538             msg->FindString("window-title", &title) != B_OK
   539         ) {
   540             return;
   541         }
   542         SetTitle(title);
   543     }
   544 
   545     void _MoveTo(BMessage *msg) {
   546         int32 x, y;
   547         if(
   548             msg->FindInt32("window-x", &x) != B_OK ||
   549             msg->FindInt32("window-y", &y) != B_OK
   550         ) {
   551             return;
   552         }
   553         MoveTo(x, y);
   554     }
   555 
   556     void _ResizeTo(BMessage *msg) {
   557         int32 w, h;
   558         if(
   559             msg->FindInt32("window-w", &w) != B_OK ||
   560             msg->FindInt32("window-h", &h) != B_OK
   561         ) {
   562             return;
   563         }
   564         ResizeTo(w, h);
   565     }
   566 
   567     void _SetBordered(BMessage *msg) {
   568         bool bEnabled;
   569         if(msg->FindBool("window-border", &bEnabled) != B_OK) {
   570             return;
   571         }
   572         SetLook(bEnabled ? B_BORDERED_WINDOW_LOOK : B_NO_BORDER_WINDOW_LOOK);
   573     }
   574 
   575     void _SetResizable(BMessage *msg) {
   576         bool bEnabled;
   577         if(msg->FindBool("window-resizable", &bEnabled) != B_OK) {
   578             return;
   579         }
   580         if (bEnabled) {
   581             SetFlags(GetFlags() & ~(B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
   582         } else {
   583             SetFlags(GetFlags() | (B_NOT_RESIZABLE | B_NOT_ZOOMABLE));
   584         }
   585     }
   586 
   587     void _Restore() {
   588         if(IsMinimized()) {
   589             Minimize(false);
   590         } else if(IsHidden()) {
   591             Show();
   592         } else if(_prev_frame != NULL) {    /* Zoomed */
   593             MoveTo(_prev_frame->left, _prev_frame->top);
   594             ResizeTo(_prev_frame->Width(), _prev_frame->Height());
   595         }
   596     }
   597 
   598     void _SetFullScreen(BMessage *msg) {
   599         bool fullscreen;
   600         if(
   601             msg->FindBool("fullscreen", &fullscreen) != B_OK
   602         ) {
   603             return;
   604         }
   605         SetFullScreen(fullscreen);
   606     }
   607 
   608     /* Members */
   609 #if SDL_VIDEO_OPENGL
   610     BGLView * _SDL_GLView;
   611 #endif
   612 
   613     int32 _last_buttons;
   614     int32 _id;  /* Window id used by SDL_BApp */
   615     bool  _mouse_focused;       /* Does this window have mouse focus? */
   616     bool  _shown;
   617     bool  _inhibit_resize;
   618 
   619     BRect *_prev_frame; /* Previous position and size of the window */
   620 
   621     /* Framebuffer members */
   622     bool            _connected,
   623                     _connection_disabled,
   624                     _buffer_created,
   625                     _buffer_dirty,
   626                     _trash_window_buffer;
   627     uint8          *_bits;
   628     uint32          _row_bytes;
   629     clipping_rect   _bounds;
   630     BLocker        *_buffer_locker;
   631     clipping_rect  *_clips;
   632     int32           _num_clips;
   633     int32           _bytes_per_px;
   634     thread_id       _draw_thread_id;
   635 
   636     BBitmap        *_bitmap;
   637 };
   638 
   639 
   640 /* FIXME:
   641  * An explanation of framebuffer flags.
   642  *
   643  * _connected -           Original variable used to let the drawing thread know
   644  *                         when changes are being made to the other framebuffer
   645  *                         members.
   646  * _connection_disabled - Used to signal to the drawing thread that the window
   647  *                         is closing, and the thread should exit.
   648  * _buffer_created -      True if the current buffer is valid
   649  * _buffer_dirty -        True if the window should be redrawn.
   650  * _trash_window_buffer - True if the window buffer needs to be trashed partway
   651  *                         through a draw cycle.  Occurs when the previous
   652  *                         buffer provided by DirectConnected() is invalidated.
   653  */
   654 #endif