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