diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 15:06:52 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-21 15:07:38 +0300 |
commit | d773b499e595a43b9b1ae449262dcf13cabf2d02 (patch) | |
tree | b1baeb12025a04f8316636b5d0ab18e30ceedb2c /src/ui/window.c |
Initial commit
Borrowing the app skeleton from Bitwise Harmony.
Diffstat (limited to 'src/ui/window.c')
-rw-r--r-- | src/ui/window.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/src/ui/window.c b/src/ui/window.c new file mode 100644 index 00000000..8b4226ef --- /dev/null +++ b/src/ui/window.c | |||
@@ -0,0 +1,441 @@ | |||
1 | #include "window.h" | ||
2 | |||
3 | #include "app.h" | ||
4 | #include "command.h" | ||
5 | #include "paint.h" | ||
6 | #include "text.h" | ||
7 | #include "util.h" | ||
8 | #include "labelwidget.h" | ||
9 | #include "inputwidget.h" | ||
10 | #include "embedded.h" | ||
11 | #if defined (iPlatformMsys) | ||
12 | # include "../win32.h" | ||
13 | #endif | ||
14 | #if defined (iPlatformApple) && !defined (iPlatformIOS) | ||
15 | # include "macos.h" | ||
16 | #endif | ||
17 | |||
18 | #include <the_Foundation/file.h> | ||
19 | #include <the_Foundation/path.h> | ||
20 | #include <SDL_hints.h> | ||
21 | #include <SDL_timer.h> | ||
22 | #include <SDL_syswm.h> | ||
23 | |||
24 | #define STB_IMAGE_IMPLEMENTATION | ||
25 | #include "stb_image.h" | ||
26 | |||
27 | static iWindow *theWindow_ = NULL; | ||
28 | |||
29 | #if defined (iPlatformApple) | ||
30 | static float initialUiScale_ = 1.0f; | ||
31 | #else | ||
32 | static float initialUiScale_ = 1.1f; | ||
33 | #endif | ||
34 | |||
35 | iDefineTypeConstruction(Window) | ||
36 | |||
37 | static iBool handleRootCommands_(iWidget *root, const char *cmd) { | ||
38 | iUnused(root); | ||
39 | if (equal_Command(cmd, "menu.open")) { | ||
40 | iWidget *button = pointer_Command(cmd); | ||
41 | iWidget *menu = findChild_Widget(button, "menu"); | ||
42 | iAssert(menu); | ||
43 | if (!isVisible_Widget(menu)) { | ||
44 | openMenu_Widget(menu, init_I2(0, button->rect.size.y)); | ||
45 | } | ||
46 | else { | ||
47 | closeMenu_Widget(menu); | ||
48 | } | ||
49 | return iTrue; | ||
50 | } | ||
51 | else if (handleCommand_App(cmd)) { | ||
52 | return iTrue; | ||
53 | } | ||
54 | return iFalse; | ||
55 | } | ||
56 | |||
57 | static const iMenuItem fileMenuItems[] = { | ||
58 | #if !defined (iPlatformApple) | ||
59 | { "Quit Lagrange", 'q', KMOD_PRIMARY, "quit" } | ||
60 | #endif | ||
61 | }; | ||
62 | |||
63 | static const iMenuItem editMenuItems[] = { | ||
64 | #if !defined (iPlatformApple) | ||
65 | { "Preferences...", SDLK_COMMA, KMOD_PRIMARY, "preferences" } | ||
66 | #endif | ||
67 | }; | ||
68 | |||
69 | static const iMenuItem viewMenuItems[] = { | ||
70 | }; | ||
71 | |||
72 | static void setupUserInterface_Window(iWindow *d) { | ||
73 | /* Children of root cover the entire window. */ | ||
74 | setFlags_Widget(d->root, resizeChildren_WidgetFlag, iTrue); | ||
75 | setCommandHandler_Widget(d->root, handleRootCommands_); | ||
76 | #if 0 | ||
77 | iWidget *mainDiv = makeHDiv_Widget(); | ||
78 | setId_Widget(mainDiv, "maindiv"); | ||
79 | addChild_Widget(d->root, iClob(mainDiv)); | ||
80 | |||
81 | iWidget *sidebar = makeVDiv_Widget(); | ||
82 | setFlags_Widget(sidebar, arrangeWidth_WidgetFlag, iTrue); | ||
83 | setId_Widget(sidebar, "sidebar"); | ||
84 | addChild_Widget(mainDiv, iClob(sidebar)); | ||
85 | |||
86 | /* Menus. */ { | ||
87 | #if defined (iPlatformApple) && !defined (iPlatformIOS) | ||
88 | /* Use the native menus. */ | ||
89 | insertMenuItems_MacOS("File", fileMenuItems, iElemCount(fileMenuItems)); | ||
90 | insertMenuItems_MacOS("Edit", editMenuItems, iElemCount(editMenuItems)); | ||
91 | insertMenuItems_MacOS("View", viewMenuItems, iElemCount(viewMenuItems)); | ||
92 | #else | ||
93 | iWidget *menubar = new_Widget(); | ||
94 | setBackgroundColor_Widget(menubar, gray25_ColorId); | ||
95 | setFlags_Widget(menubar, arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag, iTrue); | ||
96 | addChild_Widget(menubar, iClob(makeMenuButton_LabelWidget("File", fileMenuItems, iElemCount(fileMenuItems)))); | ||
97 | addChild_Widget(menubar, iClob(makeMenuButton_LabelWidget("Edit", editMenuItems, iElemCount(editMenuItems)))); | ||
98 | addChild_Widget(menubar, iClob(makeMenuButton_LabelWidget("View", viewMenuItems, iElemCount(viewMenuItems)))); | ||
99 | addChild_Widget(sidebar, iClob(menubar)); | ||
100 | #endif | ||
101 | } | ||
102 | /* Tracker info. */ { | ||
103 | iWidget *trackerInfo = addChild_Widget(sidebar, iClob(new_Widget())); | ||
104 | setId_Widget(trackerInfo, "trackerinfo"); | ||
105 | trackerInfo->rect.size.y = lineHeight_Text(default_FontId) + 2 * gap_UI; | ||
106 | setFlags_Widget(trackerInfo, arrangeHorizontal_WidgetFlag | resizeChildren_WidgetFlag, iTrue); | ||
107 | setId_Widget( | ||
108 | addChild_Widget(trackerInfo, iClob(new_LabelWidget("", 'p', KMOD_PRIMARY, "pattern.goto arg:-1"))), | ||
109 | "trackerinfo.current"); | ||
110 | iLabelWidget *dims = new_LabelWidget("", 'r', KMOD_PRIMARY | KMOD_ALT, "pattern.resize"); | ||
111 | setId_Widget(addChild_Widget(trackerInfo, iClob(dims)), "trackerinfo.dims"); | ||
112 | } | ||
113 | |||
114 | iLibraryWidget *lib = new_LibraryWidget(); | ||
115 | setId_Widget(as_Widget(lib), "library"); | ||
116 | addChildFlags_Widget(sidebar, iClob(lib), expand_WidgetFlag); | ||
117 | |||
118 | iPlaybackWidget *play = new_PlaybackWidget(); | ||
119 | setId_Widget(as_Widget(play), "playback"); | ||
120 | addChild_Widget(sidebar, iClob(play)); | ||
121 | |||
122 | iWidget *mainTabs = makeTabs_Widget(mainDiv); | ||
123 | setId_Widget(mainTabs, "maintabs"); | ||
124 | setFlags_Widget(mainTabs, expand_WidgetFlag, iTrue); | ||
125 | |||
126 | /* Optional sidebar on the right. */ | ||
127 | iWidget *sidebar2 = new_Widget(); | ||
128 | setId_Widget(addChild_Widget(mainDiv, iClob(sidebar2)), "sidebar2"); | ||
129 | setFlags_Widget( | ||
130 | sidebar2, fixedWidth_WidgetFlag | frameless_WidgetFlag | resizeChildren_WidgetFlag, iTrue); | ||
131 | |||
132 | /* Pattern sequence. */ { | ||
133 | iSequenceWidget *seq = new_SequenceWidget(); | ||
134 | appendTabPage_Widget(mainTabs, iClob(seq), "SEQUENCE", 0, 0); | ||
135 | } | ||
136 | /* Tracker. */ { | ||
137 | iTrackerWidget *tracker = new_TrackerWidget(); | ||
138 | appendTabPage_Widget(mainTabs, as_Widget(tracker), "PATTERN", 0, 0); | ||
139 | } | ||
140 | /* Voice editor. */ { | ||
141 | iWidget *voice = as_Widget(new_VoiceWidget()); | ||
142 | setId_Widget(voice, "voicelayers"); | ||
143 | appendTabPage_Widget(mainTabs, iClob(voice), "VOICE", '3', KMOD_PRIMARY); | ||
144 | } | ||
145 | /* Song information. */ { | ||
146 | iWidget *songPage = new_Widget(); | ||
147 | setId_Widget(songPage, "songinfo"); | ||
148 | setFlags_Widget(songPage, arrangeHorizontal_WidgetFlag, iTrue); | ||
149 | iWidget *headings = | ||
150 | addChildFlags_Widget(songPage, | ||
151 | iClob(new_Widget()), | ||
152 | resizeToParentHeight_WidgetFlag | resizeChildren_WidgetFlag | | ||
153 | arrangeVertical_WidgetFlag | arrangeWidth_WidgetFlag); | ||
154 | iWidget *values = addChildFlags_Widget( | ||
155 | songPage, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); | ||
156 | |||
157 | setId_Widget(addChild_Widget(headings, iClob(makePadding_Widget(2 * gap_UI))), "headings.padding"); | ||
158 | setId_Widget(addChild_Widget(values, iClob(makePadding_Widget(2 * gap_UI))), "values.padding"); | ||
159 | |||
160 | addChild_Widget(headings, iClob(makeHeading_Widget(cyan_ColorEscape "SONG PROPERTIES"))); | ||
161 | addChild_Widget(values, iClob(makeHeading_Widget(""))); | ||
162 | |||
163 | const int fieldWidth = advance_Text(monospace_FontId, "A").x * 40; | ||
164 | iWidget *field; | ||
165 | |||
166 | addChild_Widget(headings, iClob(makeHeading_Widget("Title:"))); | ||
167 | setId_Widget(field = addChild_Widget(values, iClob(new_InputWidget(0))), "info.title"); | ||
168 | field->rect.size.x = fieldWidth; | ||
169 | |||
170 | addChild_Widget(headings, iClob(makeHeading_Widget("Author:"))); | ||
171 | setId_Widget(field = addChild_Widget(values, iClob(new_InputWidget(0))), "info.author"); | ||
172 | field->rect.size.x = fieldWidth; | ||
173 | |||
174 | addChild_Widget(headings, iClob(makeHeading_Widget("Tempo:"))); | ||
175 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(3))), "info.tempo"); | ||
176 | |||
177 | addChild_Widget(headings, iClob(makeHeading_Widget("Events per Beat:"))); | ||
178 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(2))), "info.eventsperbeat"); | ||
179 | |||
180 | addChild_Widget(headings, iClob(makeHeading_Widget("Num of Tracks:"))); | ||
181 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(2))), "info.numtracks"); | ||
182 | |||
183 | addChild_Widget(headings, iClob(makePadding_Widget(2 * gap_UI))); | ||
184 | addChild_Widget(values, iClob(makePadding_Widget(2 * gap_UI))); | ||
185 | |||
186 | addChild_Widget(headings, iClob(makeHeading_Widget(cyan_ColorEscape "SONG METADATA"))); | ||
187 | addChild_Widget(values, iClob(makeHeading_Widget(""))); | ||
188 | |||
189 | addChild_Widget(headings, iClob(makeHeading_Widget("Duration:"))); | ||
190 | setId_Widget(addChildFlags_Widget(values, iClob(newEmpty_LabelWidget()), | ||
191 | alignLeft_WidgetFlag | frameless_WidgetFlag), | ||
192 | "info.duration"); | ||
193 | addChild_Widget(headings, iClob(makeHeading_Widget("Statistics:\n\n "))); | ||
194 | setId_Widget(addChildFlags_Widget(values, | ||
195 | iClob(newEmpty_LabelWidget()), | ||
196 | alignLeft_WidgetFlag | frameless_WidgetFlag), | ||
197 | "info.statistics"); | ||
198 | addChild_Widget(headings, iClob(makeHeading_Widget("Created on:"))); | ||
199 | setId_Widget(addChildFlags_Widget(values, | ||
200 | iClob(newEmpty_LabelWidget()), | ||
201 | alignLeft_WidgetFlag | frameless_WidgetFlag), | ||
202 | "info.created"); | ||
203 | |||
204 | addChild_Widget(headings, iClob(makeHeading_Widget("Last Modified on:"))); | ||
205 | setId_Widget(addChildFlags_Widget(values, | ||
206 | iClob(newEmpty_LabelWidget()), | ||
207 | alignLeft_WidgetFlag | frameless_WidgetFlag), | ||
208 | "info.lastmodified"); | ||
209 | /* App info in the bottom. */ { | ||
210 | addChildFlags_Widget(headings, iClob(new_Widget()), expand_WidgetFlag); | ||
211 | addChildFlags_Widget( | ||
212 | headings, | ||
213 | iClob(new_LabelWidget(gray50_ColorEscape "Version " BWH_APP_VERSION, 0, 0, NULL)), | ||
214 | frameless_WidgetFlag | alignLeft_WidgetFlag); | ||
215 | } | ||
216 | appendTabPage_Widget(mainTabs, iClob(songPage), "INFO", '4', KMOD_PRIMARY); | ||
217 | } | ||
218 | /* Application status. */ { | ||
219 | iWidget *status = addChildFlags_Widget(d->root, iClob(newEmpty_LabelWidget()), 0); | ||
220 | setFont_LabelWidget((iLabelWidget *) status, monospace_FontId); | ||
221 | setFlags_Widget(status, frameless_WidgetFlag | alignRight_WidgetFlag, iTrue); | ||
222 | setId_Widget(status, "status"); | ||
223 | } | ||
224 | #endif | ||
225 | /* Glboal keyboard shortcuts. */ { | ||
226 | // addAction_Widget(d->root, SDLK_LEFTBRACKET, KMOD_SHIFT | KMOD_PRIMARY, "tabs.prev"); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | static void updateRootSize_Window_(iWindow *d) { | ||
231 | iInt2 *size = &d->root->rect.size; | ||
232 | SDL_GetRendererOutputSize(d->render, &size->x, &size->y); | ||
233 | arrange_Widget(d->root); | ||
234 | postCommandf_App("window.resized width:%d height:%d", size->x, size->y); | ||
235 | } | ||
236 | |||
237 | static float pixelRatio_Window_(const iWindow *d) { | ||
238 | int dx, x; | ||
239 | SDL_GetRendererOutputSize(d->render, &dx, NULL); | ||
240 | SDL_GetWindowSize(d->win, &x, NULL); | ||
241 | return (float) dx / (float) x; | ||
242 | } | ||
243 | |||
244 | void init_Window(iWindow *d) { | ||
245 | theWindow_ = d; | ||
246 | uint32_t flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; | ||
247 | #if defined (iPlatformApple) | ||
248 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); | ||
249 | #else | ||
250 | flags |= SDL_WINDOW_OPENGL; | ||
251 | #endif | ||
252 | SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"); | ||
253 | if (SDL_CreateWindowAndRenderer(800, 500, flags, &d->win, &d->render)) { | ||
254 | fprintf(stderr, "Error when creating window: %s\n", SDL_GetError()); | ||
255 | exit(-2); | ||
256 | } | ||
257 | SDL_SetWindowMinimumSize(d->win, 640, 480); | ||
258 | SDL_SetWindowTitle(d->win, "Lagrange"); | ||
259 | SDL_ShowWindow(d->win); | ||
260 | /* Some info. */ { | ||
261 | SDL_RendererInfo info; | ||
262 | SDL_GetRendererInfo(d->render, &info); | ||
263 | printf("[window] renderer: %s\n", info.name); | ||
264 | } | ||
265 | d->uiScale = initialUiScale_; | ||
266 | d->pixelRatio = pixelRatio_Window_(d); | ||
267 | setPixelRatio_Metrics(d->pixelRatio * d->uiScale); | ||
268 | #if defined (iPlatformMsys) | ||
269 | useExecutableIconResource_SDLWindow(d->win); | ||
270 | #endif | ||
271 | #if defined (iPlatformLinux) | ||
272 | /* Load the window icon. */ { | ||
273 | int w, h, num; | ||
274 | const iBlock *icon = &imageAppicon64_Embedded; | ||
275 | stbi_uc *pixels = stbi_load_from_memory(constData_Block(icon), | ||
276 | size_Block(icon), | ||
277 | &w, | ||
278 | &h, | ||
279 | &num, | ||
280 | STBI_rgb_alpha); | ||
281 | SDL_Surface *surf = | ||
282 | SDL_CreateRGBSurfaceWithFormatFrom(pixels, w, h, 32, 4 * w, SDL_PIXELFORMAT_RGBA32); | ||
283 | SDL_SetWindowIcon(d->win, surf); | ||
284 | SDL_FreeSurface(surf); | ||
285 | stbi_image_free(pixels); | ||
286 | } | ||
287 | #endif | ||
288 | d->root = new_Widget(); | ||
289 | d->presentTime = 0.0; | ||
290 | setId_Widget(d->root, "root"); | ||
291 | init_Text(d->render); | ||
292 | #if defined (iPlatformApple) && !defined (iPlatformIOS) | ||
293 | setupApplication_MacOS(); | ||
294 | #endif | ||
295 | setupUserInterface_Window(d); | ||
296 | updateRootSize_Window_(d); | ||
297 | } | ||
298 | |||
299 | void deinit_Window(iWindow *d) { | ||
300 | if (theWindow_ == d) { | ||
301 | theWindow_ = NULL; | ||
302 | } | ||
303 | iReleasePtr(&d->root); | ||
304 | deinit_Text(); | ||
305 | SDL_DestroyRenderer(d->render); | ||
306 | SDL_DestroyWindow(d->win); | ||
307 | } | ||
308 | |||
309 | SDL_Renderer *renderer_Window(const iWindow *d) { | ||
310 | return d->render; | ||
311 | } | ||
312 | |||
313 | static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { | ||
314 | switch (ev->event) { | ||
315 | case SDL_WINDOWEVENT_RESIZED: | ||
316 | updateRootSize_Window_(d); | ||
317 | return iTrue; | ||
318 | case SDL_WINDOWEVENT_LEAVE: | ||
319 | unhover_Widget(); | ||
320 | return iTrue; | ||
321 | default: | ||
322 | break; | ||
323 | } | ||
324 | return iFalse; | ||
325 | } | ||
326 | |||
327 | iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | ||
328 | switch (ev->type) { | ||
329 | case SDL_WINDOWEVENT: { | ||
330 | return handleWindowEvent_Window_(d, &ev->window); | ||
331 | } | ||
332 | default: { | ||
333 | SDL_Event event = *ev; | ||
334 | /* Map mouse pointer coordinate to our coordinate system. */ | ||
335 | if (event.type == SDL_MOUSEMOTION) { | ||
336 | const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y); | ||
337 | event.motion.x = pos.x; | ||
338 | event.motion.y = pos.y; | ||
339 | } | ||
340 | else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { | ||
341 | const iInt2 pos = coord_Window(d, event.button.x, event.button.y); | ||
342 | event.button.x = pos.x; | ||
343 | event.button.y = pos.y; | ||
344 | } | ||
345 | iWidget *widget = d->root; | ||
346 | if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEWHEEL || | ||
347 | event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) { | ||
348 | if (mouseGrab_Widget()) { | ||
349 | widget = mouseGrab_Widget(); | ||
350 | } | ||
351 | } | ||
352 | return dispatchEvent_Widget(widget, &event); | ||
353 | } | ||
354 | } | ||
355 | return iFalse; | ||
356 | } | ||
357 | |||
358 | static void waitPresent_Window_(iWindow *d) { | ||
359 | const double ticksPerFrame = 1000.0 / 30.0; | ||
360 | uint32_t nowTime = SDL_GetTicks(); | ||
361 | if (nowTime < d->presentTime) { | ||
362 | SDL_Delay((uint32_t) (d->presentTime - nowTime)); | ||
363 | nowTime = SDL_GetTicks(); | ||
364 | } | ||
365 | /* Now it is the presentation time. */ | ||
366 | /* Figure out the next time in the future. */ | ||
367 | if (d->presentTime <= nowTime) { | ||
368 | d->presentTime += ticksPerFrame * ((int) ((nowTime - d->presentTime) / ticksPerFrame) + 1); | ||
369 | } | ||
370 | else { | ||
371 | d->presentTime = nowTime; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | void draw_Window(iWindow *d) { | ||
376 | /* Clear the window. */ | ||
377 | SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255); | ||
378 | SDL_RenderClear(d->render); | ||
379 | /* Draw widgets. */ | ||
380 | d->frameTime = SDL_GetTicks(); | ||
381 | draw_Widget(d->root); | ||
382 | #if 0 | ||
383 | /* Text cache debugging. */ { | ||
384 | SDL_Texture *cache = glyphCache_Text(); | ||
385 | SDL_Rect rect = { 140, 60, 512, 512 }; | ||
386 | SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255); | ||
387 | SDL_RenderFillRect(d->render, &rect); | ||
388 | SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect); | ||
389 | } | ||
390 | #endif | ||
391 | waitPresent_Window_(d); | ||
392 | SDL_RenderPresent(d->render); | ||
393 | } | ||
394 | |||
395 | void resize_Window(iWindow *d, int w, int h) { | ||
396 | SDL_SetWindowSize(d->win, w, h); | ||
397 | updateRootSize_Window_(d); | ||
398 | } | ||
399 | |||
400 | void setUiScale_Window(iWindow *d, float uiScale) { | ||
401 | uiScale = iClamp(uiScale, 0.5f, 4.0f); | ||
402 | if (d) { | ||
403 | d->uiScale = uiScale; | ||
404 | #if 0 | ||
405 | deinit_Text(); | ||
406 | setPixelRatio_Metrics(d->pixelRatio * d->uiScale); | ||
407 | init_Text(d->render); | ||
408 | postCommand_App("metrics.changed"); | ||
409 | /* TODO: Dynamic UI metrics change. Widgets need to update themselves. */ | ||
410 | #endif | ||
411 | } | ||
412 | else { | ||
413 | initialUiScale_ = uiScale; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | iInt2 rootSize_Window(const iWindow *d) { | ||
418 | return d->root->rect.size; | ||
419 | } | ||
420 | |||
421 | iInt2 coord_Window(const iWindow *d, int x, int y) { | ||
422 | return mulf_I2(init_I2(x, y), d->pixelRatio); | ||
423 | } | ||
424 | |||
425 | iInt2 mouseCoord_Window(const iWindow *d) { | ||
426 | int x, y; | ||
427 | SDL_GetMouseState(&x, &y); | ||
428 | return coord_Window(d, x, y); | ||
429 | } | ||
430 | |||
431 | float uiScale_Window(const iWindow *d) { | ||
432 | return d->uiScale; | ||
433 | } | ||
434 | |||
435 | uint32_t frameTime_Window(const iWindow *d) { | ||
436 | return d->frameTime; | ||
437 | } | ||
438 | |||
439 | iWindow *get_Window(void) { | ||
440 | return theWindow_; | ||
441 | } | ||