Home | History | Annotate | Download | only in sdl
      1 /*
      2  * Copyright 2011 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 #include "SkOSWindow_SDL.h"
      8 #include "SkCanvas.h"
      9 
     10 #if defined(SK_BUILD_FOR_ANDROID)
     11 #include <GLES/gl.h>
     12 #elif defined(SK_BUILD_FOR_UNIX)
     13 #include <GL/gl.h>
     14 #elif defined(SK_BUILD_FOR_MAC)
     15 #include <gl.h>
     16 #endif
     17 
     18 const int kInitialWindowWidth = 640;
     19 const int kInitialWindowHeight = 480;
     20 static SkOSWindow* gCurrentWindow;
     21 
     22 static void report_sdl_error(const char* failure) {
     23     const char* error = SDL_GetError();
     24     SkASSERT(error); // Called only to check SDL error.
     25     SkDebugf("%s SDL Error: %s.\n", failure, error);
     26     SDL_ClearError();
     27 }
     28 SkOSWindow::SkOSWindow(void*)
     29     : fWindow(nullptr)
     30     , fGLContext(nullptr)
     31     , fWindowMSAASampleCount(0) {
     32 
     33     SkASSERT(!gCurrentWindow);
     34     gCurrentWindow = this;
     35 
     36     this->createWindow(0);
     37 }
     38 
     39 SkOSWindow::~SkOSWindow() {
     40     this->destroyWindow();
     41     gCurrentWindow = nullptr;
     42 }
     43 
     44 SkOSWindow* SkOSWindow::GetInstanceForWindowID(Uint32 windowID) {
     45     if (gCurrentWindow &&
     46         gCurrentWindow->fWindow &&
     47         SDL_GetWindowID(gCurrentWindow->fWindow) == windowID) {
     48         return gCurrentWindow;
     49     }
     50     return nullptr;
     51 }
     52 
     53 void SkOSWindow::detach() {
     54     if (fGLContext) {
     55         SDL_GL_DeleteContext(fGLContext);
     56         fGLContext = nullptr;
     57     }
     58 }
     59 
     60 bool SkOSWindow::attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info) {
     61     this->createWindow(msaaSampleCount);
     62     if (!fWindow) {
     63         return false;
     64     }
     65     if (!fGLContext) {
     66         fGLContext = SDL_GL_CreateContext(fWindow);
     67         if (!fGLContext) {
     68             report_sdl_error("Failed to create SDL GL context.");
     69             return false;
     70         }
     71         glClearColor(0, 0, 0, 0);
     72         glClearStencil(0);
     73         glStencilMask(0xffffffff);
     74         glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
     75     }
     76 
     77     if (SDL_GL_MakeCurrent(fWindow, fGLContext) != 0) {
     78         report_sdl_error("Failed to make SDL GL context current.");
     79         this->detach();
     80         return false;
     81     }
     82 
     83     info->fSampleCount = msaaSampleCount;
     84     info->fStencilBits = 8;
     85 
     86     glViewport(0, 0, SkScalarRoundToInt(this->width()), SkScalarRoundToInt(this->height()));
     87     return true;
     88 }
     89 
     90 void SkOSWindow::present() {
     91     if (!fWindow) {
     92         return;
     93     }
     94     SDL_GL_SwapWindow(fWindow);
     95 }
     96 
     97 bool SkOSWindow::makeFullscreen() {
     98     if (!fWindow) {
     99         return false;
    100     }
    101     SDL_SetWindowFullscreen(fWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
    102     return true;
    103 }
    104 
    105 void SkOSWindow::setVsync(bool vsync) {
    106     if (!fWindow) {
    107         return;
    108     }
    109     SDL_GL_SetSwapInterval(vsync ? 1 : 0);
    110 }
    111 
    112 void SkOSWindow::closeWindow() {
    113     this->destroyWindow();
    114 
    115     // Currently closing the window causes the app to quit.
    116     SDL_Event event;
    117     event.type = SDL_QUIT;
    118     SDL_PushEvent(&event);
    119 }
    120 
    121 static SkKey convert_sdlkey_to_skkey(SDL_Keycode src) {
    122     switch (src) {
    123         case SDLK_UP:
    124             return kUp_SkKey;
    125         case SDLK_DOWN:
    126             return kDown_SkKey;
    127         case SDLK_LEFT:
    128             return kLeft_SkKey;
    129         case SDLK_RIGHT:
    130             return kRight_SkKey;
    131         case SDLK_HOME:
    132             return kHome_SkKey;
    133         case SDLK_END:
    134             return kEnd_SkKey;
    135         case SDLK_ASTERISK:
    136             return kStar_SkKey;
    137         case SDLK_HASH:
    138             return kHash_SkKey;
    139         case SDLK_0:
    140             return k0_SkKey;
    141         case SDLK_1:
    142             return k1_SkKey;
    143         case SDLK_2:
    144             return k2_SkKey;
    145         case SDLK_3:
    146             return k3_SkKey;
    147         case SDLK_4:
    148             return k4_SkKey;
    149         case SDLK_5:
    150             return k5_SkKey;
    151         case SDLK_6:
    152             return k6_SkKey;
    153         case SDLK_7:
    154             return k7_SkKey;
    155         case SDLK_8:
    156             return k8_SkKey;
    157         case SDLK_9:
    158             return k9_SkKey;
    159         default:
    160             return kNONE_SkKey;
    161     }
    162 }
    163 
    164 void SkOSWindow::createWindow(int msaaSampleCount) {
    165     if (fWindowMSAASampleCount != msaaSampleCount) {
    166         this->destroyWindow();
    167     }
    168     if (fWindow) {
    169         return;
    170     }
    171     uint32_t windowFlags =
    172 #if defined(SK_BUILD_FOR_ANDROID)
    173             SDL_WINDOW_BORDERLESS | SDL_WINDOW_FULLSCREEN_DESKTOP |
    174             SDL_WINDOW_ALLOW_HIGHDPI |
    175 #endif
    176             SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
    177 
    178     // GL settings are part of SDL_WINDOW_OPENGL window creation arguments.
    179 #if defined(SK_BUILD_FOR_ANDROID)
    180     // TODO we should try and get a 3.0 context first
    181     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    182     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
    183     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
    184 #else
    185     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
    186     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
    187     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    188 #endif
    189     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
    190     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
    191     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
    192     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
    193     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    194     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
    195     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    196 #if defined(SK_BUILD_FOR_UNIX)
    197     // Apparently MSAA request matches "slow caveat". Make SDL not set anything for caveat for MSAA
    198     // by setting -1 for ACCELERATED_VISUAL. For non-MSAA, set ACCELERATED_VISUAL to 1 just for
    199     // compatiblity with other platforms.
    200     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, msaaSampleCount > 0 ? -1 : 1);
    201 #else
    202     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
    203 #endif
    204     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaaSampleCount > 0 ? 1 : 0);
    205     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaaSampleCount);
    206 
    207     // This is an approximation for sizing purposes.
    208     bool isInitialWindow = this->width() == 0 && this->height() == 0;
    209     SkScalar windowWidth = isInitialWindow ? kInitialWindowWidth : this->width();
    210     SkScalar windowHeight = isInitialWindow ? kInitialWindowHeight : this->height();
    211 
    212     fWindow = SDL_CreateWindow(this->getTitle(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
    213                                windowWidth, windowHeight, windowFlags);
    214     if (!fWindow) {
    215         report_sdl_error("Failed to create SDL window.");
    216         return;
    217     }
    218     fWindowMSAASampleCount = msaaSampleCount;
    219 }
    220 
    221 void SkOSWindow::destroyWindow() {
    222     this->detach();
    223     if (fWindow) {
    224         SDL_DestroyWindow(fWindow);
    225         fWindow = nullptr;
    226         fWindowMSAASampleCount = 0;
    227     }
    228 }
    229 
    230 bool SkOSWindow::HasDirtyWindows() {
    231     if (gCurrentWindow && gCurrentWindow->fWindow) {
    232         return gCurrentWindow->isDirty();
    233     }
    234     return false;
    235 }
    236 
    237 void SkOSWindow::UpdateDirtyWindows() {
    238     if (gCurrentWindow && gCurrentWindow->fWindow) {
    239         if (gCurrentWindow->isDirty()) {
    240             // This will call present.
    241             gCurrentWindow->update(nullptr);
    242         }
    243     }
    244 }
    245 
    246 void SkOSWindow::HandleEvent(const SDL_Event& event) {
    247     switch (event.type) {
    248         case SDL_MOUSEMOTION:
    249             if (SkOSWindow* window = GetInstanceForWindowID(event.motion.windowID)) {
    250                 if (event.motion.state == SDL_PRESSED) {
    251                     window->handleClick(event.motion.x, event.motion.y,
    252                                         SkView::Click::kMoved_State, nullptr);
    253                 }
    254             }
    255             break;
    256         case SDL_MOUSEBUTTONDOWN:
    257         case SDL_MOUSEBUTTONUP:
    258             if (SkOSWindow* window = GetInstanceForWindowID(event.button.windowID)) {
    259                 window->handleClick(event.button.x, event.button.y,
    260                                     event.button.state == SDL_PRESSED ?
    261                                     SkView::Click::kDown_State :
    262                                     SkView::Click::kUp_State, nullptr);
    263             }
    264             break;
    265         case SDL_KEYDOWN:
    266             if (SkOSWindow* window = GetInstanceForWindowID(event.key.windowID)) {
    267                 SDL_Keycode key = event.key.keysym.sym;
    268                 SkKey sk = convert_sdlkey_to_skkey(key);
    269                 if (kNONE_SkKey != sk) {
    270                     if (event.key.state == SDL_PRESSED) {
    271                         window->handleKey(sk);
    272                     } else {
    273                         window->handleKeyUp(sk);
    274                     }
    275                 } else if (key == SDLK_ESCAPE) {
    276                     window->closeWindow();
    277                 }
    278             }
    279             break;
    280         case SDL_TEXTINPUT:
    281             if (SkOSWindow* window = GetInstanceForWindowID(event.text.windowID)) {
    282                 size_t len = strlen(event.text.text);
    283                 for (size_t i = 0; i < len; i++) {
    284                     window->handleChar((SkUnichar)event.text.text[i]);
    285                 }
    286             }
    287             break;
    288         case SDL_WINDOWEVENT:
    289             switch (event.window.event) {
    290                 case SDL_WINDOWEVENT_SHOWN:
    291                     // For initialization purposes, we resize upon first show.
    292                     // Fallthrough.
    293                 case SDL_WINDOWEVENT_SIZE_CHANGED:
    294                     if (SkOSWindow* window = GetInstanceForWindowID(event.window.windowID)) {
    295                         int w = 0;
    296                         int h = 0;
    297                         SDL_GetWindowSize(window->fWindow, &w, &h);
    298                         window->resize(w, h);
    299                     }
    300                     break;
    301                 case SDL_WINDOWEVENT_FOCUS_GAINED:
    302                     if (GetInstanceForWindowID(event.text.windowID)) {
    303                         SDL_StartTextInput();
    304                     }
    305                     break;
    306                 default:
    307                     break;
    308             }
    309             break;
    310         default:
    311             break;
    312     }
    313 }
    314 
    315 SkMSec gTimerDelay;
    316 
    317 void SkOSWindow::RunEventLoop() {
    318     for (;;) {
    319         SkEvent::ServiceQueueTimer();
    320         bool hasMoreSkEvents = SkEvent::ProcessEvent();
    321 
    322         SDL_Event event;
    323         bool hasSDLEvents = SDL_PollEvent(&event) == 1;
    324 
    325         // Invalidations do not post to event loop, rather we just go through the
    326         // windows for each event loop iteration.
    327         bool hasDirtyWindows = HasDirtyWindows();
    328 
    329         if (!hasSDLEvents && !hasMoreSkEvents && !hasDirtyWindows) {
    330             // If there is no SDL events, SkOSWindow updates or SkEvents
    331             // to be done, wait for the SDL events.
    332             if (gTimerDelay > 0) {
    333                 hasSDLEvents = SDL_WaitEventTimeout(&event, gTimerDelay) == 1;
    334             } else {
    335                 hasSDLEvents = SDL_WaitEvent(&event) == 1;
    336             }
    337         }
    338         while (hasSDLEvents) {
    339             if (event.type == SDL_QUIT) {
    340                 return;
    341             }
    342             HandleEvent(event);
    343             hasSDLEvents = SDL_PollEvent(&event);
    344         }
    345         UpdateDirtyWindows();
    346     }
    347 }
    348 
    349 void SkOSWindow::onSetTitle(const char title[]) {
    350     if (!fWindow) {
    351         return;
    352     }
    353     this->updateWindowTitle();
    354 }
    355 
    356 void SkOSWindow::updateWindowTitle() {
    357     SDL_SetWindowTitle(fWindow, this->getTitle());
    358 }
    359 ///////////////////////////////////////////////////////////////////////////////////////
    360 
    361 void SkEvent::SignalNonEmptyQueue() {
    362     // nothing to do, since we spin on our event-queue
    363 }
    364 
    365 void SkEvent::SignalQueueTimer(SkMSec delay) {
    366     gTimerDelay = delay;
    367 }
    368 
    369 //////////////////////////////////////////////////////////////////////////////////////////////
    370 
    371 #include "SkApplication.h"
    372 #include "SkEvent.h"
    373 #include "SkWindow.h"
    374 
    375 #if defined(SK_BUILD_FOR_ANDROID)
    376 int SDL_main(int argc, char** argv) {
    377 #else
    378 int main(int argc, char** argv) {
    379 #endif
    380     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
    381         report_sdl_error("Failed to init SDL.");
    382         return -1;
    383     }
    384 
    385     application_init();
    386 
    387     SkOSWindow* window = create_sk_window(nullptr, argc, argv);
    388 
    389     // drain any events that occurred before |window| was assigned.
    390     while (SkEvent::ProcessEvent());
    391 
    392     SkOSWindow::RunEventLoop();
    393 
    394     delete window;
    395     application_term();
    396 
    397     SDL_Quit();
    398 
    399     return 0;
    400 }
    401