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 
13 namespace sk_app {
14 
15 SkTDynamicHash<Window_mac, Uint32> Window_mac::gWindowMap;
16 
CreateNativeWindow(void *)17 Window* 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 
initWindow()27 bool 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     fGLContext = SDL_GL_CreateContext(fWindow);
75     if (!fGLContext) {
76         SkDebugf("%s\n", SDL_GetError());
77         this->closeWindow();
78         return false;
79     }
80 
81     return true;
82 }
83 
closeWindow()84 void Window_mac::closeWindow() {
85     if (fGLContext) {
86         SDL_GL_DeleteContext(fGLContext);
87         fGLContext = nullptr;
88     }
89 
90     if (fWindow) {
91         gWindowMap.remove(fWindowID);
92         SDL_DestroyWindow(fWindow);
93         fWindowID = 0;
94         fWindow = nullptr;
95     }
96 }
97 
get_key(const SDL_Keysym & keysym)98 static Window::Key get_key(const SDL_Keysym& keysym) {
99     static const struct {
100         SDL_Keycode fSDLK;
101         Window::Key fKey;
102     } gPair[] = {
103         { SDLK_BACKSPACE, Window::Key::kBack },
104         { SDLK_CLEAR, Window::Key::kBack },
105         { SDLK_RETURN, Window::Key::kOK },
106         { SDLK_UP, Window::Key::kUp },
107         { SDLK_DOWN, Window::Key::kDown },
108         { SDLK_LEFT, Window::Key::kLeft },
109         { SDLK_RIGHT, Window::Key::kRight },
110         { SDLK_TAB, Window::Key::kTab },
111         { SDLK_PAGEUP, Window::Key::kPageUp },
112         { SDLK_PAGEDOWN, Window::Key::kPageDown },
113         { SDLK_HOME, Window::Key::kHome },
114         { SDLK_END, Window::Key::kEnd },
115         { SDLK_DELETE, Window::Key::kDelete },
116         { SDLK_ESCAPE, Window::Key::kEscape },
117         { SDLK_LSHIFT, Window::Key::kShift },
118         { SDLK_RSHIFT, Window::Key::kShift },
119         { SDLK_LCTRL, Window::Key::kCtrl },
120         { SDLK_RCTRL, Window::Key::kCtrl },
121         { SDLK_LALT, Window::Key::kOption },
122         { SDLK_LALT, Window::Key::kOption },
123         { 'A', Window::Key::kA },
124         { 'C', Window::Key::kC },
125         { 'V', Window::Key::kV },
126         { 'X', Window::Key::kX },
127         { 'Y', Window::Key::kY },
128         { 'Z', Window::Key::kZ },
129     };
130     for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) {
131         if (gPair[i].fSDLK == keysym.sym) {
132             return gPair[i].fKey;
133         }
134     }
135     return Window::Key::kNONE;
136 }
137 
get_modifiers(const SDL_Event & event)138 static uint32_t get_modifiers(const SDL_Event& event) {
139     static const struct {
140         unsigned    fSDLMask;
141         unsigned    fSkMask;
142     } gModifiers[] = {
143         { KMOD_SHIFT, Window::kShift_ModifierKey },
144         { KMOD_CTRL,  Window::kControl_ModifierKey },
145         { KMOD_ALT,   Window::kOption_ModifierKey },
146     };
147 
148     auto modifiers = 0;
149 
150     switch (event.type) {
151         case SDL_KEYDOWN:
152             // fall through
153         case SDL_KEYUP: {
154             for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
155                 if (event.key.keysym.mod & gModifiers[i].fSDLMask) {
156                     modifiers |= gModifiers[i].fSkMask;
157                 }
158             }
159             if (0 == event.key.repeat) {
160                 modifiers |= Window::kFirstPress_ModifierKey;
161             }
162             break;
163         }
164 
165         default: {
166             SDL_Keymod mod = SDL_GetModState();
167             for (size_t i = 0; i < SK_ARRAY_COUNT(gModifiers); ++i) {
168                 if (mod & gModifiers[i].fSDLMask) {
169                     modifiers |= gModifiers[i].fSkMask;
170                 }
171             }
172             break;
173         }
174     }
175     return modifiers;
176 }
177 
HandleWindowEvent(const SDL_Event & event)178 bool Window_mac::HandleWindowEvent(const SDL_Event& event) {
179     Window_mac* win = gWindowMap.find(event.window.windowID);
180     if (win && win->handleEvent(event)) {
181         return true;
182     }
183 
184     return false;
185 }
186 
handleEvent(const SDL_Event & event)187 bool Window_mac::handleEvent(const SDL_Event& event) {
188     switch (event.type) {
189         case SDL_WINDOWEVENT:
190             if (SDL_WINDOWEVENT_EXPOSED == event.window.event) {
191                 this->onPaint();
192             } else if (SDL_WINDOWEVENT_RESIZED == event.window.event) {
193                 this->onResize(event.window.data1, event.window.data2);
194             }
195             break;
196 
197         case SDL_MOUSEBUTTONDOWN:
198             if (event.button.button == SDL_BUTTON_LEFT) {
199                 this->onMouse(event.button.x, event.button.y,
200                               Window::kDown_InputState, get_modifiers(event));
201             }
202             break;
203 
204         case SDL_MOUSEBUTTONUP:
205             if (event.button.button == SDL_BUTTON_LEFT) {
206                 this->onMouse(event.button.x, event.button.y,
207                               Window::kUp_InputState, get_modifiers(event));
208             }
209             break;
210 
211         case SDL_MOUSEMOTION:
212             this->onMouse(event.motion.x, event.motion.y,
213                           Window::kMove_InputState, get_modifiers(event));
214             break;
215 
216         case SDL_MOUSEWHEEL:
217             this->onMouseWheel(event.wheel.y, get_modifiers(event));
218             break;
219 
220         case SDL_KEYDOWN: {
221             Window::Key key = get_key(event.key.keysym);
222             if (key != Window::Key::kNONE) {
223                 if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) {
224                     if (event.key.keysym.sym == SDLK_ESCAPE) {
225                         return true;
226                     }
227                 }
228             }
229         } break;
230 
231         case SDL_KEYUP: {
232             Window::Key key = get_key(event.key.keysym);
233             if (key != Window::Key::kNONE) {
234                 (void) this->onKey(key, Window::kUp_InputState,
235                                    get_modifiers(event));
236             }
237         } break;
238 
239         case SDL_TEXTINPUT: {
240             const char* textIter = &event.text.text[0];
241             while (SkUnichar c = SkUTF8_NextUnichar(&textIter)) {
242                 (void) this->onChar(c, get_modifiers(event));
243             }
244         } break;
245 
246         default:
247             break;
248     }
249 
250     return false;
251 }
252 
setTitle(const char * title)253 void Window_mac::setTitle(const char* title) {
254     SDL_SetWindowTitle(fWindow, title);
255 }
256 
show()257 void Window_mac::show() {
258     SDL_ShowWindow(fWindow);
259 }
260 
attach(BackendType attachType)261 bool Window_mac::attach(BackendType attachType) {
262     this->initWindow();
263 
264     window_context_factory::MacWindowInfo info;
265     info.fWindow = fWindow;
266     info.fGLContext = fGLContext;
267     switch (attachType) {
268         case kRaster_BackendType:
269             fWindowContext = NewRasterForMac(info, fRequestedDisplayParams);
270             break;
271 
272         case kNativeGL_BackendType:
273         default:
274             fWindowContext = NewGLForMac(info, fRequestedDisplayParams);
275             break;
276     }
277     this->onBackendCreated();
278 
279     return (SkToBool(fWindowContext));
280 }
281 
onInval()282 void Window_mac::onInval() {
283     SDL_Event sdlevent;
284     sdlevent.type = SDL_WINDOWEVENT;
285     sdlevent.window.windowID = fWindowID;
286     sdlevent.window.event = SDL_WINDOWEVENT_EXPOSED;
287     SDL_PushEvent(&sdlevent);
288 }
289 
290 }   // namespace sk_app
291