1/* 2* Copyright 2016 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 8#include "SkUtils.h" 9#include "Timer.h" 10#include "WindowContextFactory_mac.h" 11#include "Window_mac.h" 12 13namespace sk_app { 14 15SkTDynamicHash<Window_mac, Uint32> Window_mac::gWindowMap; 16 17Window* Window::CreateNativeWindow(void*) { 18 Window_mac* window = new Window_mac(); 19 if (!window->initWindow()) { 20 delete window; 21 return nullptr; 22 } 23 24 return window; 25} 26 27bool Window_mac::initWindow() { 28 if (fRequestedDisplayParams.fMSAASampleCount != fMSAASampleCount) { 29 this->closeWindow(); 30 } 31 // we already have a window 32 if (fWindow) { 33 return true; 34 } 35 36 constexpr int initialWidth = 1280; 37 constexpr int initialHeight = 960; 38 39 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 40 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 41 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 42 43 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); 44 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); 45 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); 46 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 47 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); 48 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 49 50 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); 51 52 if (fRequestedDisplayParams.fMSAASampleCount > 1) { 53 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); 54 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, fRequestedDisplayParams.fMSAASampleCount); 55 } else { 56 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); 57 } 58 // TODO: handle other display params 59 60 uint32_t windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; 61 fWindow = SDL_CreateWindow("SDL Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 62 initialWidth, initialHeight, windowFlags); 63 64 if (!fWindow) { 65 return false; 66 } 67 68 fMSAASampleCount = fRequestedDisplayParams.fMSAASampleCount; 69 70 // add to hashtable of windows 71 fWindowID = SDL_GetWindowID(fWindow); 72 gWindowMap.add(this); 73 74 return true; 75} 76 77void Window_mac::closeWindow() { 78 if (fWindow) { 79 gWindowMap.remove(fWindowID); 80 SDL_DestroyWindow(fWindow); 81 fWindowID = 0; 82 fWindow = nullptr; 83 } 84} 85 86static Window::Key get_key(const SDL_Keysym& keysym) { 87 static const struct { 88 SDL_Keycode fSDLK; 89 Window::Key fKey; 90 } gPair[] = { 91 { SDLK_BACKSPACE, Window::Key::kBack }, 92 { SDLK_CLEAR, Window::Key::kBack }, 93 { SDLK_RETURN, Window::Key::kOK }, 94 { SDLK_UP, Window::Key::kUp }, 95 { SDLK_DOWN, Window::Key::kDown }, 96 { SDLK_LEFT, Window::Key::kLeft }, 97 { SDLK_RIGHT, Window::Key::kRight }, 98 { SDLK_TAB, Window::Key::kTab }, 99 { SDLK_PAGEUP, Window::Key::kPageUp }, 100 { SDLK_PAGEDOWN, Window::Key::kPageDown }, 101 { SDLK_HOME, Window::Key::kHome }, 102 { SDLK_END, Window::Key::kEnd }, 103 { SDLK_DELETE, Window::Key::kDelete }, 104 { SDLK_ESCAPE, Window::Key::kEscape }, 105 { SDLK_LSHIFT, Window::Key::kShift }, 106 { SDLK_RSHIFT, Window::Key::kShift }, 107 { SDLK_LCTRL, Window::Key::kCtrl }, 108 { SDLK_RCTRL, Window::Key::kCtrl }, 109 { SDLK_LALT, Window::Key::kOption }, 110 { SDLK_LALT, Window::Key::kOption }, 111 { 'A', Window::Key::kA }, 112 { 'C', Window::Key::kC }, 113 { 'V', Window::Key::kV }, 114 { 'X', Window::Key::kX }, 115 { 'Y', Window::Key::kY }, 116 { 'Z', Window::Key::kZ }, 117 }; 118 for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { 119 if (gPair[i].fSDLK == keysym.sym) { 120 return gPair[i].fKey; 121 } 122 } 123 return Window::Key::kNONE; 124} 125 126static uint32_t get_modifiers(const SDL_Event& event) { 127 static const struct { 128 unsigned fSDLMask; 129 unsigned fSkMask; 130 } gModifiers[] = { 131 { KMOD_SHIFT, Window::kShift_ModifierKey }, 132 { KMOD_CTRL, Window::kControl_ModifierKey }, 133 { KMOD_ALT, Window::kOption_ModifierKey }, 134 }; 135 136 auto modifiers = 0; 137 138 switch (event.type) { 139 case SDL_KEYDOWN: 140 // fall through 141 case SDL_KEYUP: { 142 for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) { 143 if (event.key.keysym.mod & gModifiers[i].fSDLMask) { 144 modifiers |= gModifiers[i].fSkMask; 145 } 146 } 147 if (0 == event.key.repeat) { 148 modifiers |= Window::kFirstPress_ModifierKey; 149 } 150 break; 151 } 152 153 default: { 154 SDL_Keymod mod = SDL_GetModState(); 155 for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) { 156 if (mod & gModifiers[i].fSDLMask) { 157 modifiers |= gModifiers[i].fSkMask; 158 } 159 } 160 break; 161 } 162 } 163 return modifiers; 164} 165 166bool Window_mac::HandleWindowEvent(const SDL_Event& event) { 167 Window_mac* win = gWindowMap.find(event.window.windowID); 168 if (win && win->handleEvent(event)) { 169 return true; 170 } 171 172 return false; 173} 174 175bool Window_mac::handleEvent(const SDL_Event& event) { 176 switch (event.type) { 177 case SDL_WINDOWEVENT: 178 if (SDL_WINDOWEVENT_EXPOSED == event.window.event) { 179 this->onPaint(); 180 } else if (SDL_WINDOWEVENT_RESIZED == event.window.event) { 181 this->onResize(event.window.data1, event.window.data2); 182 } 183 break; 184 185 case SDL_MOUSEBUTTONDOWN: 186 if (event.button.button == SDL_BUTTON_LEFT) { 187 this->onMouse(event.button.x, event.button.y, 188 Window::kDown_InputState, get_modifiers(event)); 189 } 190 break; 191 192 case SDL_MOUSEBUTTONUP: 193 if (event.button.button == SDL_BUTTON_LEFT) { 194 this->onMouse(event.button.x, event.button.y, 195 Window::kUp_InputState, get_modifiers(event)); 196 } 197 break; 198 199 case SDL_MOUSEMOTION: 200 this->onMouse(event.motion.x, event.motion.y, 201 Window::kMove_InputState, get_modifiers(event)); 202 break; 203 204 case SDL_MOUSEWHEEL: 205 this->onMouseWheel(event.wheel.y, get_modifiers(event)); 206 break; 207 208 case SDL_KEYDOWN: { 209 Window::Key key = get_key(event.key.keysym); 210 if (key != Window::Key::kNONE) { 211 if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) { 212 if (event.key.keysym.sym == SDLK_ESCAPE) { 213 return true; 214 } 215 } 216 } 217 } break; 218 219 case SDL_KEYUP: { 220 Window::Key key = get_key(event.key.keysym); 221 if (key != Window::Key::kNONE) { 222 (void) this->onKey(key, Window::kUp_InputState, 223 get_modifiers(event)); 224 } 225 } break; 226 227 case SDL_TEXTINPUT: { 228 const char* textIter = &event.text.text[0]; 229 while (SkUnichar c = SkUTF8_NextUnichar(&textIter)) { 230 (void) this->onChar(c, get_modifiers(event)); 231 } 232 } break; 233 234 default: 235 break; 236 } 237 238 return false; 239} 240 241void Window_mac::setTitle(const char* title) { 242 SDL_SetWindowTitle(fWindow, title); 243} 244 245void Window_mac::show() { 246 SDL_ShowWindow(fWindow); 247} 248 249bool Window_mac::attach(BackendType attachType) { 250 this->initWindow(); 251 252 window_context_factory::MacWindowInfo info; 253 info.fWindow = fWindow; 254 switch (attachType) { 255 case kRaster_BackendType: 256 fWindowContext = NewRasterForMac(info, fRequestedDisplayParams); 257 break; 258 259 case kNativeGL_BackendType: 260 default: 261 fWindowContext = NewGLForMac(info, fRequestedDisplayParams); 262 break; 263 } 264 this->onBackendCreated(); 265 266 return (SkToBool(fWindowContext)); 267} 268 269void Window_mac::onInval() { 270 SDL_Event sdlevent; 271 sdlevent.type = SDL_WINDOWEVENT; 272 sdlevent.window.windowID = fWindowID; 273 sdlevent.window.event = SDL_WINDOWEVENT_EXPOSED; 274 SDL_PushEvent(&sdlevent); 275} 276 277} // namespace sk_app 278