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