Continuando nossa série de artigos traduzidos do site lazyfoo, agora veremos como o SDL lida com múltiplas telas.
Um recurso novo do SDL 2 é a habilidade de lidar com múltiplas telas. Aqui iremos fazer nossa janela pular de tela em tela.
class LWindow { public: //Intializes internals LWindow(); //Creates window bool init(); //Handles window events void handleEvent( SDL_Event& e ); //Focuses on window void focus(); //Shows windows contents void render(); //Deallocates internals void free(); //Window dimensions int getWidth(); int getHeight(); //Window focii bool hasMouseFocus(); bool hasKeyboardFocus(); bool isMinimized(); bool isShown(); private: //Window data SDL_Window* mWindow; SDL_Renderer* mRenderer; int mWindowID; int mWindowDisplayID; //Window dimensions int mWidth; int mHeight; //Window focus bool mMouseFocus; bool mKeyboardFocus; bool mFullScreen; bool mMinimized; bool mShown; };
Aqui temos a nova janela dos artigos anteriores desta série com um ID associado para que possamos saber em qual tela a janela está.
//Our custom window LWindow gWindow; //Display data int gTotalDisplays = 0; SDL_Rect* gDisplayBounds = NULL
Todas as nossas telas possuem um ID inteiro e um retângulo associado à elas de forma que possamos saber a posição e dimensão de cada tela de nossa área de trabalho.
bool LWindow::init() { //Create window mWindow = SDL_CreateWindow( "SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE ); if( mWindow != NULL ) { mMouseFocus = true; mKeyboardFocus = true; mWidth = SCREEN_WIDTH; mHeight = SCREEN_HEIGHT; //Create renderer for window mRenderer = SDL_CreateRenderer( mWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC ); if( mRenderer == NULL ) { printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() ); SDL_DestroyWindow( mWindow ); mWindow = NULL; } else { //Initialize renderer color SDL_SetRenderDrawColor( mRenderer, 0xFF, 0xFF, 0xFF, 0xFF ); //Grab window identifiers mWindowID = SDL_GetWindowID( mWindow ); mWindowDisplayID = SDL_GetWindowDisplayIndex( mWindow ); //Flag as opened mShown = true; } } else { printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() ); } return mWindow != NULL && mRenderer != NULL; }
Nosso código de criação de janela é basicamente o mesmo do artigo anterior, mas agora fazemos uma chamada para SDL_GetWindowDisplayIndex para que possamos saber em qual tela a janela foi criada.
void LWindow::handleEvent( SDL_Event& e ) { //Caption update flag bool updateCaption = false; //If an event was detected for this window if( e.type == SDL_WINDOWEVENT && e.window.windowID == mWindowID ) { switch( e.window.event ) { //Window moved case SDL_WINDOWEVENT_MOVED: mWindowDisplayID = SDL_GetWindowDisplayIndex( mWindow ); updateCaption = true; break; //Window appeared case SDL_WINDOWEVENT_SHOWN: mShown = true; break; //Window disappeared case SDL_WINDOWEVENT_HIDDEN: mShown = false; break; //Get new dimensions and repaint case SDL_WINDOWEVENT_SIZE_CHANGED: mWidth = e.window.data1; mHeight = e.window.data2; SDL_RenderPresent( mRenderer ); break; //Repaint on expose case SDL_WINDOWEVENT_EXPOSED: SDL_RenderPresent( mRenderer ); break; //Mouse enter case SDL_WINDOWEVENT_ENTER: mMouseFocus = true; updateCaption = true; break; //Mouse exit case SDL_WINDOWEVENT_LEAVE: mMouseFocus = false; updateCaption = true; break; //Keyboard focus gained case SDL_WINDOWEVENT_FOCUS_GAINED: mKeyboardFocus = true; updateCaption = true; break; //Keyboard focus lost case SDL_WINDOWEVENT_FOCUS_LOST: mKeyboardFocus = false; updateCaption = true; break; //Window minimized case SDL_WINDOWEVENT_MINIMIZED: mMinimized = true; break; //Window maxized case SDL_WINDOWEVENT_MAXIMIZED: mMinimized = false; break; //Window restored case SDL_WINDOWEVENT_RESTORED: mMinimized = false; break; //Hide on close case SDL_WINDOWEVENT_CLOSE: SDL_HideWindow( mWindow ); break; } }
Aqui no manipulador de eventos de nossa janela lidaremos com o evento SDL_WINDOWEVENT_MOVED de forma que possamos atualizar a tela que a janela está usando usando SDL_GetWindowDisplayIndex.
else if( e.type == SDL_KEYDOWN ) { //Display change flag bool switchDisplay = false; //Cycle through displays on up/down switch( e.key.keysym.sym ) { case SDLK_UP: ++mWindowDisplayID; switchDisplay = true; break; case SDLK_DOWN: --mWindowDisplayID; switchDisplay = true; break; }
Quando pressionamos para cima ou para baixo mudamos o índice da tela de forma que nos movemos para a próxima tela.
//Display needs to be updated if( switchDisplay ) { //Bound display index if( mWindowDisplayID < 0 ) { mWindowDisplayID = gTotalDisplays - 1; } else if( mWindowDisplayID >= gTotalDisplays ) { mWindowDisplayID = 0; } //Move window to center of next display SDL_SetWindowPosition( mWindow, gDisplayBounds[ mWindowDisplayID ].x + ( gDisplayBounds[ mWindowDisplayID ].w - mWidth ) / 2, gDisplayBounds[ mWindowDisplayID ].y + ( gDisplayBounds[ mWindowDisplayID ].h - mHeight ) / 2 ); updateCaption = true; } } //Update window caption with new data if( updateCaption ) { std::stringstream caption; caption << "SDL Tutorial - ID: " << mWindowID << " Display: " << mWindowDisplayID << " MouseFocus:" << ( ( mMouseFocus ) ? "On" : "Off" ) << " KeyboardFocus:" << ( ( mKeyboardFocus ) ? "On" : "Off" ); SDL_SetWindowTitle( mWindow, caption.str().c_str() ); } }
Se precisarmos mover para a próxima tela, primeiro nos certificamos de que a tela tem um índice válido. Então atualizamos a posição da janela com SDL_SetWindowPosition. Essa chamada irã centralizar a janela na próxima tela.
bool init() { //Initialization flag bool success = true; //Initialize SDL if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() ); success = false; } else { //Set texture filtering to linear if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) ) { printf( "Warning: Linear texture filtering not enabled!" ); } //Get number of displays gTotalDisplays = SDL_GetNumVideoDisplays(); if( gTotalDisplays < 2 ) { printf( "Warning: Only one display connected!" ); }
Na nossa função de inicialização descobrimos quantas telas estão conectadas ao computador usando SDL_GetNumVideoDisplays. Se houver apenas 1 tela conectada, será exibido um aviso.
//Get bounds of each display gDisplayBounds = new SDL_Rect[ gTotalDisplays ]; for( int i = 0; i < gTotalDisplays; ++i ) { SDL_GetDisplayBounds( i, &gDisplayBounds[ i ] ); } //Create window if( !gWindow.init() ) { printf( "Window could not be created!\n" ); success = false; } } return success; }
Agora que sabemos quantas telas estão conectadas, alocaremos retângulos para cada uma delas e obtemos os limites de cada uma delas usando SDL_GetDisplayBounds. Depois disso, inicializamos nossa janela.
//Main loop flag bool quit = false; //Event handler SDL_Event e; //While application is running while( !quit ) { //Handle events on queue while( SDL_PollEvent( &e ) != 0 ) { //User requests quit if( e.type == SDL_QUIT ) { quit = true; } //Handle window events gWindow.handleEvent( e ); } //Update window gWindow.render(); }
Como nosso código está bem encapsulado, o loop principal não precisa ser alterado já que todas as alterações foram feitas sob o capô.
Baixe os arquivos de mídia e de código fonte desse artigo aqui.