1 /*
2 * Copyright 2017 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_ios.h"
11 #include "Window_ios.h"
12 
13 namespace sk_app {
14 
15 SkTDynamicHash<Window_ios, Uint32> Window_ios::gWindowMap;
16 
CreateNativeWindow(void *)17 Window* Window::CreateNativeWindow(void*) {
18     Window_ios* window = new Window_ios();
19     if (!window->initWindow()) {
20         delete window;
21         return nullptr;
22     }
23 
24     return window;
25 }
26 
initWindow()27 bool Window_ios::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_FULLSCREEN | SDL_WINDOW_ALLOW_HIGHDPI;
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_ios::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_ios::HandleWindowEvent(const SDL_Event& event) {
179     Window_ios* 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_ios::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_FINGERDOWN:
198             this->onTouch(event.tfinger.fingerId, Window::kDown_InputState,
199                           (int)(this->width()*event.tfinger.x),
200                           (int)(this->height()*event.tfinger.y));
201             break;
202 
203         case SDL_FINGERUP:
204             this->onTouch(event.tfinger.fingerId, Window::kUp_InputState,
205                           (int)(this->width()*event.tfinger.x),
206                           (int)(this->height()*event.tfinger.y));
207             break;
208 
209         case SDL_FINGERMOTION:
210             this->onTouch(event.tfinger.fingerId, Window::kMove_InputState,
211                           (int)(this->width()*event.tfinger.x),
212                           (int)(this->height()*event.tfinger.y));
213             break;
214 
215         case SDL_KEYDOWN: {
216             Window::Key key = get_key(event.key.keysym);
217             if (key != Window::Key::kNONE) {
218                 if (!this->onKey(key, Window::kDown_InputState, get_modifiers(event))) {
219                     if (event.key.keysym.sym == SDLK_ESCAPE) {
220                         return true;
221                     }
222                 }
223             }
224         } break;
225 
226         case SDL_KEYUP: {
227             Window::Key key = get_key(event.key.keysym);
228             if (key != Window::Key::kNONE) {
229                 (void) this->onKey(key, Window::kUp_InputState,
230                                    get_modifiers(event));
231             }
232         } break;
233 
234         case SDL_TEXTINPUT: {
235             const char* textIter = &event.text.text[0];
236             while (SkUnichar c = SkUTF8_NextUnichar(&textIter)) {
237                 (void) this->onChar(c, get_modifiers(event));
238             }
239         } break;
240 
241         default:
242             break;
243     }
244 
245     return false;
246 }
247 
setTitle(const char * title)248 void Window_ios::setTitle(const char* title) {
249     SDL_SetWindowTitle(fWindow, title);
250 }
251 
show()252 void Window_ios::show() {
253     SDL_ShowWindow(fWindow);
254 }
255 
attach(BackendType attachType)256 bool Window_ios::attach(BackendType attachType) {
257     this->initWindow();
258 
259     window_context_factory::IOSWindowInfo info;
260     info.fWindow = fWindow;
261     info.fGLContext = fGLContext;
262     switch (attachType) {
263         case kRaster_BackendType:
264             fWindowContext = NewRasterForIOS(info, fRequestedDisplayParams);
265             break;
266 
267         case kNativeGL_BackendType:
268         default:
269             fWindowContext = NewGLForIOS(info, fRequestedDisplayParams);
270             break;
271     }
272     this->onBackendCreated();
273 
274     return (SkToBool(fWindowContext));
275 }
276 
onInval()277 void Window_ios::onInval() {
278     SDL_Event sdlevent;
279     sdlevent.type = SDL_WINDOWEVENT;
280     sdlevent.window.windowID = fWindowID;
281     sdlevent.window.event = SDL_WINDOWEVENT_EXPOSED;
282     SDL_PushEvent(&sdlevent);
283 }
284 
285 }   // namespace sk_app
286