// Copyright (C) 2012-2013 Kornel Kisielewicz // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh #include "nv/gl/gl_window.hh" #include "nv/logging.hh" #include "nv/lib/gl.hh" #include "nv/lib/sdl.hh" using namespace nv; static bool sdl_key_event_to_io_event( const SDL_KeyboardEvent& ke, io_event& kevent ) { kevent.type = EV_KEY; kevent.key.pressed = ( ke.state != SDL_RELEASED ); kevent.key.ascii = 0; kevent.key.code = KEY_NONE; #if NV_SDL_VERSION == NV_SDL_20 uint32 ucode = ke.keysym.sym; #else uint32 ucode = ke.keysym.unicode; #endif // if result is a typable char place it into the structure if (ucode >= 32 && ucode < 128 ) { kevent.key.ascii = static_cast( ucode ); #if NV_SDL_VERSION == NV_SDL_20 if (ucode >= 'a' && ucode <= 'z') { int shifted = !!(ke.keysym.mod & KMOD_SHIFT); int capslock = !!(ke.keysym.mod & KMOD_CAPS); if ((shifted ^ capslock) != 0) { kevent.key.ascii = (char8)SDL_toupper(ucode); } } #endif } // Get the Key value from the SDL Keyboard event int result = ke.keysym.sym; // Check the control and shift states kevent.key.alt = (ke.keysym.mod & KMOD_ALT ) != 0; kevent.key.control = (ke.keysym.mod & KMOD_CTRL ) != 0; kevent.key.shift = (ke.keysym.mod & KMOD_SHIFT ) != 0; // if result is a typable char from the directly transformable ranges if ( ( result >= KEY_A && result <= KEY_Z ) || ( result >= KEY_0 && result <= KEY_9 ) || result == ' ' ) { kevent.key.code = static_cast(result); } // if result is a typable char from the a..z range if ( result >= KEY_A + 32 && result <= KEY_Z + 32 ) { kevent.key.code = static_cast(result - 32); } if ( result >= SDLK_F1 && result <= SDLK_F12 ) { kevent.key.code = static_cast(result - SDLK_F1 + KEY_F1 ); } // other recognized codes switch ( result ) { case SDLK_BACKSPACE : kevent.key.code = KEY_BACK; break; case SDLK_TAB : kevent.key.code = KEY_TAB; break; case SDLK_RETURN : kevent.key.code = KEY_ENTER; break; case SDLK_PAGEUP : kevent.key.code = KEY_PGUP; break; case SDLK_PAGEDOWN : kevent.key.code = KEY_PGDOWN; break; case SDLK_END : kevent.key.code = KEY_END; break; case SDLK_HOME : kevent.key.code = KEY_HOME; break; case SDLK_LEFT : kevent.key.code = KEY_LEFT; break; case SDLK_UP : kevent.key.code = KEY_UP; break; case SDLK_RIGHT : kevent.key.code = KEY_RIGHT; break; case SDLK_DOWN : kevent.key.code = KEY_DOWN; break; case SDLK_DELETE : kevent.key.code = KEY_DELETE; break; case SDLK_INSERT : kevent.key.code = KEY_INSERT; break; //case SDLK_KP5 : kevent.key.code = KEY_CENTER; break; case SDLK_ESCAPE : kevent.key.code = KEY_ESCAPE; break; case SDLK_QUOTE : kevent.key.code = KEY_QUOTE; break; case SDLK_COMMA : kevent.key.code = KEY_COMMA; break; case SDLK_MINUS : kevent.key.code = KEY_MINUS; break; case SDLK_PERIOD : kevent.key.code = KEY_PERIOD; break; case SDLK_SLASH : kevent.key.code = KEY_SLASH; break; case SDLK_SEMICOLON : kevent.key.code = KEY_SCOLON; break; case SDLK_LEFTBRACKET : kevent.key.code = KEY_LBRACKET; break; case SDLK_BACKSLASH : kevent.key.code = KEY_BSLASH; break; case SDLK_RIGHTBRACKET : kevent.key.code = KEY_RBRACKET; break; case SDLK_BACKQUOTE : kevent.key.code = KEY_BQUOTE; break; default : break; } // If key was understood by nv, then it's valid, otherwise ignored return kevent.key.ascii != 0 || kevent.key.code != 0; } static bool sdl_mouse_button_to_io_event( const SDL_MouseButtonEvent& mb, io_event& mevent ) { mevent.type = EV_MOUSE_BUTTON; mevent.mbutton.button = MOUSE_NONE; mevent.mbutton.pressed = (mb.state != SDL_RELEASED); mevent.mbutton.x = static_cast< uint16 >( mb.x ); mevent.mbutton.y = static_cast< uint16 >( mb.y ); switch ( mb.button ) { case SDL_BUTTON_LEFT : mevent.mbutton.button = MOUSE_LEFT; break; case SDL_BUTTON_MIDDLE : mevent.mbutton.button = MOUSE_MIDDLE; break; case SDL_BUTTON_RIGHT : mevent.mbutton.button = MOUSE_RIGHT; break; #if NV_SDL_VERSION == NV_SDL_12 case SDL_BUTTON_WHEELUP : mevent.type = EV_MOUSE_WHEEL; mevent.mwheel.x = 0; mevent.mwheel.y = 3; return true; case SDL_BUTTON_WHEELDOWN : mevent.type = EV_MOUSE_WHEEL; mevent.mwheel.x = 0; mevent.mwheel.y = -3; return true; #endif default : break; } return mevent.mbutton.button != MOUSE_NONE; } #if NV_SDL_VERSION == NV_SDL_20 static bool sdl_mouse_wheel_to_io_event( const SDL_MouseWheelEvent& mm, io_event& mevent ) { mevent.type = EV_MOUSE_WHEEL; mevent.mwheel.x = static_cast< sint32 >( mm.x ); mevent.mwheel.y = static_cast< sint32 >( mm.y ); return true; } #endif static bool sdl_mouse_motion_to_io_event( const SDL_MouseMotionEvent& mm, io_event& mevent ) { mevent.type = EV_MOUSE_MOVE; mevent.mmove.pressed = (mm.state != SDL_RELEASED); mevent.mmove.x = static_cast< uint16 >( mm.x ); mevent.mmove.y = static_cast< uint16 >( mm.y ); mevent.mmove.rx = static_cast< sint16 >( mm.xrel ); mevent.mmove.ry = static_cast< sint16 >( mm.yrel ); return true; } static bool sdl_joy_button_event_to_io_event( const SDL_JoyButtonEvent& jb, io_event& jevent ) { jevent.type = EV_JOY_BUTTON; jevent.jbutton.id = jb.which; jevent.jbutton.button = jb.button; jevent.jbutton.pressed = (jb.type == SDL_PRESSED); return true; } static bool sdl_joy_axis_event_to_io_event( const SDL_JoyAxisEvent& ja, io_event& jevent ) { jevent.type = EV_JOY_AXIS; jevent.jaxis.id = ja.which; jevent.jaxis.axis = ja.axis; jevent.jaxis.value = ja.value; return true; } static bool sdl_joy_hat_event_to_io_event( const SDL_JoyHatEvent& jh, io_event& jevent ) { jevent.type = EV_JOY_HAT; jevent.jhat.id = jh.which; jevent.jhat.hat = jh.hat; jevent.jhat.value = jh.value; return true; } static bool sdl_joy_ball_event_to_io_event( const SDL_JoyBallEvent& jb, io_event& jevent ) { jevent.type = EV_JOY_HAT; jevent.jball.id = jb.which; jevent.jball.ball = jb.ball; jevent.jball.rx = jb.xrel; jevent.jball.ry = jb.yrel; return true; } static bool sdl_event_to_io_event( const SDL_Event& e, io_event& ioevent ) { switch ( e.type ) { case SDL_KEYDOWN : return sdl_key_event_to_io_event( e.key, ioevent ); case SDL_KEYUP : return sdl_key_event_to_io_event( e.key, ioevent ); case SDL_MOUSEMOTION : return sdl_mouse_motion_to_io_event( e.motion, ioevent ); case SDL_MOUSEBUTTONDOWN : return sdl_mouse_button_to_io_event( e.button, ioevent ); case SDL_MOUSEBUTTONUP : return sdl_mouse_button_to_io_event( e.button, ioevent ); #if NV_SDL_VERSION == NV_SDL_20 case SDL_MOUSEWHEEL : return sdl_mouse_wheel_to_io_event( e.wheel, ioevent ); #endif /* // SDL 2.0 incompatible case SDL_ACTIVEEVENT : ioevent.type = EV_ACTIVE; ioevent.active.gain = (e.active.gain != 0); return e.active.state == SDL_APPINPUTFOCUS; case SDL_VIDEORESIZE : ioevent.type = EV_RESIZE; ioevent.resize.x = e.resize.w; ioevent.resize.y = e.resize.h; return true; case SDL_NOEVENT : return false; case SDL_VIDEOEXPOSE : return false; */ case SDL_SYSWMEVENT : ioevent.type = EV_SYSTEM; return true; case SDL_QUIT : ioevent.type = EV_QUIT; return true; case SDL_JOYAXISMOTION : return sdl_joy_axis_event_to_io_event( e.jaxis, ioevent ); case SDL_JOYBALLMOTION : return sdl_joy_ball_event_to_io_event( e.jball, ioevent ); case SDL_JOYHATMOTION : return sdl_joy_hat_event_to_io_event( e.jhat, ioevent ); case SDL_JOYBUTTONDOWN : return sdl_joy_button_event_to_io_event( e.jbutton, ioevent ); case SDL_JOYBUTTONUP : return sdl_joy_button_event_to_io_event( e.jbutton, ioevent ); default : return false; } } gl_window::gl_window( device* dev, uint16 width, uint16 height, bool fullscreen ) : m_device( dev ), m_width( width ), m_height( height ), m_title("NV Engine"), m_handle( nullptr ), m_hwnd( 0 ), m_adopted( false ) { // bpp = m_info->vfmt->BitsPerPixel; SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); // SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 4 ); #if NV_SDL_VERSION == NV_SDL_20 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); #endif #if NV_SDL_VERSION == NV_SDL_12 uint32 flags = SDL_OPENGL; if (fullscreen) flags |= SDL_FULLSCREEN; m_handle = SDL_SetVideoMode( width, height, 32, flags ); #elif NV_SDL_VERSION == NV_SDL_20 uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; if (fullscreen) flags |= SDL_WINDOW_FULLSCREEN; m_handle = SDL_CreateWindow("Nova Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags ); #endif if ( m_handle == 0 ) { NV_LOG( LOG_CRITICAL, "Video mode set failed: " << SDL_GetError( ) ); return; // TODO: Error report } // NV_LOG( LOG_INFO, "Joystick count : " << SDL_NumJoysticks() ); // SDL_Joystick* j = SDL_JoystickOpen(0); // if (j) // { // NV_LOG( LOG_INFO, "Joystick Name: " << SDL_JoystickNameForIndex(0) ); // NV_LOG( LOG_INFO, "Joystick Number of Axes: " << SDL_JoystickNumAxes(j)); // NV_LOG( LOG_INFO, "Joystick Number of Buttons: " << SDL_JoystickNumButtons(j)); // NV_LOG( LOG_INFO, "Joystick Number of Balls: " << SDL_JoystickNumBalls(j)); // } m_context = new sdl_gl_context( m_device, m_handle ); m_context->set_viewport( nv::ivec4( 0, 0, m_width, m_height ) ); } uint16 gl_window::get_width() const { return m_width; } uint16 gl_window::get_height() const { return m_height; } string gl_window::get_title() const { return m_title; } void gl_window::set_title( const string& title ) { if ( m_adopted ) return; #if NV_SDL_VERSION == NV_SDL_20 SDL_SetWindowTitle( static_cast( m_handle ), title.c_str() ); #else SDL_WM_SetCaption( title.c_str(), title.c_str() ); #endif m_title = title; } bool gl_window::is_event_pending() { return SDL_PollEvent( nullptr ) != 0; } bool gl_window::poll_event( io_event& event ) { SDL_Event sdl_event; if ( SDL_PollEvent( &sdl_event ) == 0 ) return false; return sdl_event_to_io_event( sdl_event, event ); } void gl_window::swap_buffers() { if ( m_adopted ) { #if NV_PLATFORM == NV_WINDOWS ::SwapBuffers( (HDC)m_hwnd ); #else NV_ASSERT( false, "Native GL context only working with SDL 2.0!" ); #endif } #if NV_SDL_VERSION == NV_SDL_20 SDL_GL_SwapWindow( static_cast( m_handle ) ); #else SDL_GL_SwapBuffers(); #endif } gl_window::~gl_window() { delete m_context; #if NV_SDL_VERSION == NV_SDL_20 if ( !m_adopted ) SDL_DestroyWindow( static_cast( m_handle ) ); #endif } nv::gl_window::gl_window( device* dev, void* handle, void* dc ) : m_device( dev ), m_width( 0 ), m_height( 0 ), m_title(), m_handle( nullptr ), m_adopted( true ) { #if NV_PLATFORM == NV_WINDOWS #if NV_SDL_VERSION == NV_SDL_20 m_context = new native_gl_context( m_device, dc ); SDL_Window* window = SDL_CreateWindowFrom( handle ); // Doesn't work :/ // RECT rect; // GetClientRect( (HWND)handle, &rect ); // m_width = (uint16)rect.right; // m_height = (uint16)rect.bottom; m_handle = window; m_hwnd = ::GetDC( (HWND)handle ); m_context->set_viewport( nv::ivec4( 0, 0, m_width, m_height ) ); #else NV_ASSERT( false, "Native GL context only working with SDL 2.0!" ); #endif #else NV_ASSERT( false, "Native GL context adoption not implemented for this platform!" ); #endif }