| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| |
| #ifndef SDL_BWin_h_ |
| #define SDL_BWin_h_ |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| #include "../../SDL_internal.h" |
| #include "SDL.h" |
| #include "SDL_syswm.h" |
| #include "SDL_bframebuffer.h" |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #include <stdio.h> |
| #include <AppKit.h> |
| #include <Cursor.h> |
| #include <InterfaceKit.h> |
| #if SDL_VIDEO_OPENGL |
| #include <opengl/GLView.h> |
| #endif |
| #include "SDL_events.h" |
| #include "../../main/haiku/SDL_BApp.h" |
| |
| |
| enum WinCommands { |
| BWIN_MOVE_WINDOW, |
| BWIN_RESIZE_WINDOW, |
| BWIN_SHOW_WINDOW, |
| BWIN_HIDE_WINDOW, |
| BWIN_MAXIMIZE_WINDOW, |
| BWIN_MINIMIZE_WINDOW, |
| BWIN_RESTORE_WINDOW, |
| BWIN_SET_TITLE, |
| BWIN_SET_BORDERED, |
| BWIN_SET_RESIZABLE, |
| BWIN_FULLSCREEN, |
| BWIN_UPDATE_FRAMEBUFFER, |
| BWIN_MINIMUM_SIZE_WINDOW |
| }; |
| |
| // non-OpenGL framebuffer view |
| class SDL_BView: public BView |
| { |
| public: |
| SDL_BView(BRect frame, const char* name, uint32 resizingMode) |
| : BView(frame, name, resizingMode, B_WILL_DRAW), |
| fBitmap(NULL) |
| { |
| } |
| |
| void Draw(BRect dirty) |
| { |
| if (fBitmap != NULL) |
| DrawBitmap(fBitmap, B_ORIGIN); |
| } |
| |
| void SetBitmap(BBitmap *bitmap) |
| { |
| fBitmap = bitmap; |
| } |
| |
| private: |
| BBitmap *fBitmap; |
| }; |
| |
| class SDL_BWin: public BWindow |
| { |
| public: |
| /* Constructor/Destructor */ |
| SDL_BWin(BRect bounds, window_look look, uint32 flags) |
| : BWindow(bounds, "Untitled", look, B_NORMAL_WINDOW_FEEL, flags) |
| { |
| _last_buttons = 0; |
| |
| _cur_view = NULL; |
| _SDL_View = NULL; |
| |
| #if SDL_VIDEO_OPENGL |
| _SDL_GLView = NULL; |
| _gl_type = 0; |
| #endif |
| _shown = false; |
| _inhibit_resize = false; |
| _mouse_focused = false; |
| _prev_frame = NULL; |
| _fullscreen = NULL; |
| |
| /* Handle framebuffer stuff */ |
| _buffer_locker = new BLocker(); |
| _bitmap = NULL; |
| } |
| |
| virtual ~ SDL_BWin() |
| { |
| Lock(); |
| |
| if (_SDL_View != NULL && _SDL_View != _cur_view) { |
| delete _SDL_View; |
| _SDL_View = NULL; |
| } |
| |
| #if SDL_VIDEO_OPENGL |
| if (_SDL_GLView) { |
| if (((SDL_BApp*)be_app)->GetCurrentContext() == _SDL_GLView) |
| ((SDL_BApp*)be_app)->SetCurrentContext(NULL); |
| if (_SDL_GLView == _cur_view) |
| RemoveChild(_SDL_GLView); |
| _SDL_GLView = NULL; |
| // _SDL_GLView deleted by HAIKU_GL_DeleteContext |
| } |
| |
| #endif |
| Unlock(); |
| |
| delete _prev_frame; |
| |
| /* Clean up framebuffer stuff */ |
| _buffer_locker->Lock(); |
| delete _buffer_locker; |
| } |
| |
| void SetCurrentView(BView *view) |
| { |
| if (_cur_view != view) { |
| if (_cur_view != NULL) |
| RemoveChild(_cur_view); |
| _cur_view = view; |
| if (_cur_view != NULL) |
| AddChild(_cur_view); |
| } |
| } |
| |
| void UpdateCurrentView() |
| { |
| if (_SDL_GLView != NULL) { |
| SetCurrentView(_SDL_GLView); |
| } else if (_SDL_View != NULL) { |
| SetCurrentView(_SDL_View); |
| } else { |
| SetCurrentView(NULL); |
| } |
| } |
| |
| SDL_BView *CreateView() { |
| Lock(); |
| if (_SDL_View == NULL) { |
| _SDL_View = new SDL_BView(Bounds(), "SDL View", B_FOLLOW_ALL_SIDES); |
| UpdateCurrentView(); |
| } |
| Unlock(); |
| return _SDL_View; |
| } |
| |
| void RemoveView() { |
| Lock(); |
| if(_SDL_View != NULL) { |
| SDL_BView *oldView = _SDL_View; |
| _SDL_View = NULL; |
| UpdateCurrentView(); |
| delete oldView; |
| } |
| Unlock(); |
| } |
| |
| /* * * * * OpenGL functionality * * * * */ |
| #if SDL_VIDEO_OPENGL |
| BGLView *CreateGLView(Uint32 gl_flags) { |
| Lock(); |
| if (_SDL_GLView == NULL) { |
| _SDL_GLView = new BGLView(Bounds(), "SDL GLView", |
| B_FOLLOW_ALL_SIDES, |
| (B_WILL_DRAW | B_FRAME_EVENTS), |
| gl_flags); |
| _gl_type = gl_flags; |
| UpdateCurrentView(); |
| } |
| Unlock(); |
| return _SDL_GLView; |
| } |
| |
| void RemoveGLView() { |
| Lock(); |
| if(_SDL_GLView != NULL) { |
| if (((SDL_BApp*)be_app)->GetCurrentContext() == _SDL_GLView) |
| ((SDL_BApp*)be_app)->SetCurrentContext(NULL); |
| _SDL_GLView = NULL; |
| UpdateCurrentView(); |
| // _SDL_GLView deleted by HAIKU_GL_DeleteContext |
| } |
| Unlock(); |
| } |
| |
| void SwapBuffers(void) { |
| _SDL_GLView->SwapBuffers(); |
| } |
| #endif |
| |
| /* * * * * Event sending * * * * */ |
| /* Hook functions */ |
| virtual void FrameMoved(BPoint origin) { |
| /* Post a message to the BApp so that it can handle the window event */ |
| BMessage msg(BAPP_WINDOW_MOVED); |
| msg.AddInt32("window-x", (int)origin.x); |
| msg.AddInt32("window-y", (int)origin.y); |
| _PostWindowEvent(msg); |
| |
| /* Perform normal hook operations */ |
| BWindow::FrameMoved(origin); |
| } |
| |
| void FrameResized(float width, float height) { |
| /* Post a message to the BApp so that it can handle the window event */ |
| BMessage msg(BAPP_WINDOW_RESIZED); |
| |
| msg.AddInt32("window-w", (int)width + 1); |
| msg.AddInt32("window-h", (int)height + 1); |
| _PostWindowEvent(msg); |
| |
| /* Perform normal hook operations */ |
| BWindow::FrameResized(width, height); |
| } |
| |
| bool QuitRequested() { |
| BMessage msg(BAPP_WINDOW_CLOSE_REQUESTED); |
| _PostWindowEvent(msg); |
| |
| /* We won't allow a quit unless asked by DestroyWindow() */ |
| return false; |
| } |
| |
| void WindowActivated(bool active) { |
| BMessage msg(BAPP_KEYBOARD_FOCUS); /* Mouse focus sold separately */ |
| msg.AddBool("focusGained", active); |
| _PostWindowEvent(msg); |
| } |
| |
| void Zoom(BPoint origin, |
| float width, |
| float height) { |
| BMessage msg(BAPP_MAXIMIZE); /* Closest thing to maximization Haiku has */ |
| _PostWindowEvent(msg); |
| |
| /* Before the window zooms, record its size */ |
| if( !_prev_frame ) |
| _prev_frame = new BRect(Frame()); |
| |
| /* Perform normal hook operations */ |
| BWindow::Zoom(origin, width, height); |
| } |
| |
| /* Member functions */ |
| void Show() { |
| while(IsHidden()) { |
| BWindow::Show(); |
| } |
| _shown = true; |
| |
| BMessage msg(BAPP_SHOW); |
| _PostWindowEvent(msg); |
| } |
| |
| void Hide() { |
| BWindow::Hide(); |
| _shown = false; |
| |
| BMessage msg(BAPP_HIDE); |
| _PostWindowEvent(msg); |
| } |
| |
| void Minimize(bool minimize) { |
| BWindow::Minimize(minimize); |
| int32 minState = (minimize ? BAPP_MINIMIZE : BAPP_RESTORE); |
| |
| BMessage msg(minState); |
| _PostWindowEvent(msg); |
| } |
| |
| void ScreenChanged(BRect screenFrame, color_space depth) |
| { |
| if (_fullscreen) { |
| MoveTo(screenFrame.left, screenFrame.top); |
| ResizeTo(screenFrame.Width(), screenFrame.Height()); |
| } |
| } |
| |
| |
| /* BView message interruption */ |
| void DispatchMessage(BMessage * msg, BHandler * target) |
| { |
| BPoint where; /* Used by mouse moved */ |
| int32 buttons; /* Used for mouse button events */ |
| int32 key; /* Used for key events */ |
| |
| switch (msg->what) { |
| case B_MOUSE_MOVED: |
| int32 transit; |
| if (msg->FindPoint("where", &where) == B_OK |
| && msg->FindInt32("be:transit", &transit) == B_OK) { |
| _MouseMotionEvent(where, transit); |
| } |
| break; |
| |
| case B_MOUSE_DOWN: |
| if (msg->FindInt32("buttons", &buttons) == B_OK) { |
| _MouseButtonEvent(buttons, SDL_PRESSED); |
| } |
| break; |
| |
| case B_MOUSE_UP: |
| if (msg->FindInt32("buttons", &buttons) == B_OK) { |
| _MouseButtonEvent(buttons, SDL_RELEASED); |
| } |
| break; |
| |
| case B_MOUSE_WHEEL_CHANGED: |
| float x, y; |
| if (msg->FindFloat("be:wheel_delta_x", &x) == B_OK |
| && msg->FindFloat("be:wheel_delta_y", &y) == B_OK) { |
| _MouseWheelEvent((int)x, (int)y); |
| } |
| break; |
| |
| case B_KEY_DOWN: |
| { |
| int32 i = 0; |
| int8 byte; |
| int8 bytes[4] = { 0, 0, 0, 0 }; |
| while (i < 4 && msg->FindInt8("byte", i, &byte) == B_OK) { |
| bytes[i] = byte; |
| i++; |
| } |
| if (msg->FindInt32("key", &key) == B_OK) { |
| _KeyEvent((SDL_Scancode)key, &bytes[0], i, SDL_PRESSED); |
| } |
| } |
| break; |
| |
| case B_UNMAPPED_KEY_DOWN: /* modifier keys are unmapped */ |
| if (msg->FindInt32("key", &key) == B_OK) { |
| _KeyEvent((SDL_Scancode)key, NULL, 0, SDL_PRESSED); |
| } |
| break; |
| |
| case B_KEY_UP: |
| case B_UNMAPPED_KEY_UP: /* modifier keys are unmapped */ |
| if (msg->FindInt32("key", &key) == B_OK) { |
| _KeyEvent(key, NULL, 0, SDL_RELEASED); |
| } |
| break; |
| |
| default: |
| /* move it after switch{} so it's always handled |
| that way we keep Haiku features like: |
| - CTRL+Q to close window (and other shortcuts) |
| - PrintScreen to make screenshot into /boot/home |
| - etc.. */ |
| /* BWindow::DispatchMessage(msg, target); */ |
| break; |
| } |
| |
| BWindow::DispatchMessage(msg, target); |
| } |
| |
| /* Handle command messages */ |
| void MessageReceived(BMessage* message) { |
| switch (message->what) { |
| /* Handle commands from SDL */ |
| case BWIN_SET_TITLE: |
| _SetTitle(message); |
| break; |
| case BWIN_MOVE_WINDOW: |
| _MoveTo(message); |
| break; |
| case BWIN_RESIZE_WINDOW: |
| _ResizeTo(message); |
| break; |
| case BWIN_SET_BORDERED: { |
| bool bEnabled; |
| if (message->FindBool("window-border", &bEnabled) == B_OK) |
| _SetBordered(bEnabled); |
| break; |
| } |
| case BWIN_SET_RESIZABLE: { |
| bool bEnabled; |
| if (message->FindBool("window-resizable", &bEnabled) == B_OK) |
| _SetResizable(bEnabled); |
| break; |
| } |
| case BWIN_SHOW_WINDOW: |
| Show(); |
| break; |
| case BWIN_HIDE_WINDOW: |
| Hide(); |
| break; |
| case BWIN_MAXIMIZE_WINDOW: |
| BWindow::Zoom(); |
| break; |
| case BWIN_MINIMIZE_WINDOW: |
| Minimize(true); |
| break; |
| case BWIN_RESTORE_WINDOW: |
| _Restore(); |
| break; |
| case BWIN_FULLSCREEN: { |
| bool fullscreen; |
| if (message->FindBool("fullscreen", &fullscreen) == B_OK) |
| _SetFullScreen(fullscreen); |
| break; |
| } |
| case BWIN_MINIMUM_SIZE_WINDOW: |
| _SetMinimumSize(message); |
| break; |
| case BWIN_UPDATE_FRAMEBUFFER: { |
| BMessage* pendingMessage; |
| while ((pendingMessage |
| = MessageQueue()->FindMessage(BWIN_UPDATE_FRAMEBUFFER, 0))) { |
| MessageQueue()->RemoveMessage(pendingMessage); |
| delete pendingMessage; |
| } |
| if (_bitmap != NULL) { |
| if (_SDL_View != NULL && _cur_view == _SDL_View) |
| _SDL_View->Draw(Bounds()); |
| else if (_SDL_GLView != NULL && _cur_view == _SDL_GLView) { |
| _SDL_GLView->CopyPixelsIn(_bitmap, B_ORIGIN); |
| } |
| } |
| break; |
| } |
| default: |
| /* Perform normal message handling */ |
| BWindow::MessageReceived(message); |
| break; |
| } |
| |
| } |
| |
| |
| |
| /* Accessor methods */ |
| bool IsShown() { return _shown; } |
| int32 GetID() { return _id; } |
| BBitmap *GetBitmap() { return _bitmap; } |
| BView *GetCurView() { return _cur_view; } |
| SDL_BView *GetView() { return _SDL_View; } |
| #if SDL_VIDEO_OPENGL |
| BGLView *GetGLView() { return _SDL_GLView; } |
| Uint32 GetGLType() { return _gl_type; } |
| #endif |
| |
| /* Setter methods */ |
| void SetID(int32 id) { _id = id; } |
| void LockBuffer() { _buffer_locker->Lock(); } |
| void UnlockBuffer() { _buffer_locker->Unlock(); } |
| void SetBitmap(BBitmap *bitmap) { _bitmap = bitmap; if (_SDL_View != NULL) _SDL_View->SetBitmap(bitmap); } |
| |
| |
| private: |
| /* Event redirection */ |
| void _MouseMotionEvent(BPoint &where, int32 transit) { |
| if(transit == B_EXITED_VIEW) { |
| /* Change mouse focus */ |
| if(_mouse_focused) { |
| _MouseFocusEvent(false); |
| } |
| } else { |
| /* Change mouse focus */ |
| if (!_mouse_focused) { |
| _MouseFocusEvent(true); |
| } |
| BMessage msg(BAPP_MOUSE_MOVED); |
| msg.AddInt32("x", (int)where.x); |
| msg.AddInt32("y", (int)where.y); |
| |
| _PostWindowEvent(msg); |
| } |
| } |
| |
| void _MouseFocusEvent(bool focusGained) { |
| _mouse_focused = focusGained; |
| BMessage msg(BAPP_MOUSE_FOCUS); |
| msg.AddBool("focusGained", focusGained); |
| _PostWindowEvent(msg); |
| |
| /* FIXME: Why were these here? |
| if false: be_app->SetCursor(B_HAND_CURSOR); |
| if true: SDL_SetCursor(NULL); */ |
| } |
| |
| void _MouseButtonEvent(int32 buttons, Uint8 state) { |
| int32 buttonStateChange = buttons ^ _last_buttons; |
| |
| if(buttonStateChange & B_PRIMARY_MOUSE_BUTTON) { |
| _SendMouseButton(SDL_BUTTON_LEFT, state); |
| } |
| if(buttonStateChange & B_SECONDARY_MOUSE_BUTTON) { |
| _SendMouseButton(SDL_BUTTON_RIGHT, state); |
| } |
| if(buttonStateChange & B_TERTIARY_MOUSE_BUTTON) { |
| _SendMouseButton(SDL_BUTTON_MIDDLE, state); |
| } |
| |
| _last_buttons = buttons; |
| } |
| |
| void _SendMouseButton(int32 button, int32 state) { |
| BMessage msg(BAPP_MOUSE_BUTTON); |
| msg.AddInt32("button-id", button); |
| msg.AddInt32("button-state", state); |
| _PostWindowEvent(msg); |
| } |
| |
| void _MouseWheelEvent(int32 x, int32 y) { |
| /* Create a message to pass along to the BeApp thread */ |
| BMessage msg(BAPP_MOUSE_WHEEL); |
| msg.AddInt32("xticks", x); |
| msg.AddInt32("yticks", y); |
| _PostWindowEvent(msg); |
| } |
| |
| void _KeyEvent(int32 keyCode, const int8 *keyUtf8, const ssize_t & len, int32 keyState) { |
| /* Create a message to pass along to the BeApp thread */ |
| BMessage msg(BAPP_KEY); |
| msg.AddInt32("key-state", keyState); |
| msg.AddInt32("key-scancode", keyCode); |
| if (keyUtf8 != NULL) { |
| msg.AddData("key-utf8", B_INT8_TYPE, (const void*)keyUtf8, len); |
| } |
| be_app->PostMessage(&msg); |
| } |
| |
| void _RepaintEvent() { |
| /* Force a repaint: Call the SDL exposed event */ |
| BMessage msg(BAPP_REPAINT); |
| _PostWindowEvent(msg); |
| } |
| void _PostWindowEvent(BMessage &msg) { |
| msg.AddInt32("window-id", _id); |
| be_app->PostMessage(&msg); |
| } |
| |
| /* Command methods (functions called upon by SDL) */ |
| void _SetTitle(BMessage *msg) { |
| const char *title; |
| if( |
| msg->FindString("window-title", &title) != B_OK |
| ) { |
| return; |
| } |
| SetTitle(title); |
| } |
| |
| void _MoveTo(BMessage *msg) { |
| int32 x, y; |
| if( |
| msg->FindInt32("window-x", &x) != B_OK || |
| msg->FindInt32("window-y", &y) != B_OK |
| ) { |
| return; |
| } |
| if (_fullscreen) |
| _non_fullscreen_frame.OffsetTo(x, y); |
| else |
| MoveTo(x, y); |
| } |
| |
| void _ResizeTo(BMessage *msg) { |
| int32 w, h; |
| if( |
| msg->FindInt32("window-w", &w) != B_OK || |
| msg->FindInt32("window-h", &h) != B_OK |
| ) { |
| return; |
| } |
| if (_fullscreen) { |
| _non_fullscreen_frame.right = _non_fullscreen_frame.left + w; |
| _non_fullscreen_frame.bottom = _non_fullscreen_frame.top + h; |
| } else |
| ResizeTo(w, h); |
| } |
| |
| void _SetBordered(bool bEnabled) { |
| if (_fullscreen) |
| _bordered = bEnabled; |
| else |
| SetLook(bEnabled ? B_TITLED_WINDOW_LOOK : B_NO_BORDER_WINDOW_LOOK); |
| } |
| |
| void _SetResizable(bool bEnabled) { |
| if (_fullscreen) |
| _resizable = bEnabled; |
| else { |
| if (bEnabled) { |
| SetFlags(Flags() & ~(B_NOT_RESIZABLE | B_NOT_ZOOMABLE)); |
| } else { |
| SetFlags(Flags() | (B_NOT_RESIZABLE | B_NOT_ZOOMABLE)); |
| } |
| } |
| } |
| |
| void _SetMinimumSize(BMessage *msg) { |
| float maxHeight; |
| float maxWidth; |
| float _; |
| int32 minHeight; |
| int32 minWidth; |
| |
| // This is a bit convoluted, we only want to set the minimum not the maximum |
| // But there is no direct call to do that, so store the maximum size beforehand |
| GetSizeLimits(&_, &maxWidth, &_, &maxHeight); |
| if (msg->FindInt32("window-w", &minWidth) != B_OK) |
| return; |
| if (msg->FindInt32("window-h", &minHeight) != B_OK) |
| return; |
| SetSizeLimits((float)minWidth, maxWidth, (float)minHeight, maxHeight); |
| UpdateSizeLimits(); |
| } |
| |
| void _Restore() { |
| if(IsMinimized()) { |
| Minimize(false); |
| } else if(IsHidden()) { |
| Show(); |
| } else if (_fullscreen) { |
| |
| } else if(_prev_frame != NULL) { /* Zoomed */ |
| MoveTo(_prev_frame->left, _prev_frame->top); |
| ResizeTo(_prev_frame->Width(), _prev_frame->Height()); |
| } |
| } |
| |
| void _SetFullScreen(bool fullscreen) { |
| if (fullscreen != _fullscreen) { |
| if (fullscreen) { |
| BScreen screen(this); |
| BRect screenFrame = screen.Frame(); |
| printf("screen frame: "); screenFrame.PrintToStream(); printf("\n"); |
| _bordered = Look() != B_NO_BORDER_WINDOW_LOOK; |
| _resizable = !(Flags() & B_NOT_RESIZABLE); |
| _non_fullscreen_frame = Frame(); |
| _SetBordered(false); |
| _SetResizable(false); |
| MoveTo(screenFrame.left, screenFrame.top); |
| ResizeTo(screenFrame.Width(), screenFrame.Height()); |
| _fullscreen = fullscreen; |
| } else { |
| _fullscreen = fullscreen; |
| MoveTo(_non_fullscreen_frame.left, _non_fullscreen_frame.top); |
| ResizeTo(_non_fullscreen_frame.Width(), _non_fullscreen_frame.Height()); |
| _SetBordered(_bordered); |
| _SetResizable(_resizable); |
| } |
| } |
| } |
| |
| /* Members */ |
| |
| BView* _cur_view; |
| SDL_BView* _SDL_View; |
| #if SDL_VIDEO_OPENGL |
| BGLView * _SDL_GLView; |
| Uint32 _gl_type; |
| #endif |
| |
| int32 _last_buttons; |
| int32 _id; /* Window id used by SDL_BApp */ |
| bool _mouse_focused; /* Does this window have mouse focus? */ |
| bool _shown; |
| bool _inhibit_resize; |
| |
| BRect *_prev_frame; /* Previous position and size of the window */ |
| bool _fullscreen; |
| // valid only if fullscreen |
| BRect _non_fullscreen_frame; |
| bool _bordered; |
| bool _resizable; |
| |
| /* Framebuffer members */ |
| BLocker *_buffer_locker; |
| BBitmap *_bitmap; |
| }; |
| |
| |
| /* FIXME: |
| * An explanation of framebuffer flags. |
| * |
| * _connected - Original variable used to let the drawing thread know |
| * when changes are being made to the other framebuffer |
| * members. |
| * _connection_disabled - Used to signal to the drawing thread that the window |
| * is closing, and the thread should exit. |
| * _buffer_created - True if the current buffer is valid |
| * _buffer_dirty - True if the window should be redrawn. |
| * _trash_window_buffer - True if the window buffer needs to be trashed partway |
| * through a draw cycle. Occurs when the previous |
| * buffer provided by DirectConnected() is invalidated. |
| */ |
| #endif /* SDL_BWin_h_ */ |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |