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