Fix T51889: new file or load factory settings results in broken UI.
[blender.git] / source / blender / windowmanager / intern / wm_window.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2007 Blender Foundation but based 
19  * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV
20  * All rights reserved.
21  *
22  * Contributor(s): Blender Foundation, 2008
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_window.c
28  *  \ingroup wm
29  *
30  * Window management, wrap GHOST.
31  */
32
33 #include <math.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "DNA_listBase.h"       
39 #include "DNA_screen_types.h"
40 #include "DNA_windowmanager_types.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "GHOST_C-api.h"
45
46 #include "BLI_math.h"
47 #include "BLI_blenlib.h"
48 #include "BLI_utildefines.h"
49
50 #include "BLT_translation.h"
51
52 #include "BKE_blender.h"
53 #include "BKE_context.h"
54 #include "BKE_library.h"
55 #include "BKE_global.h"
56 #include "BKE_main.h"
57
58
59 #include "RNA_access.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63 #include "wm.h"
64 #include "wm_draw.h"
65 #include "wm_window.h"
66 #include "wm_subwindow.h"
67 #include "wm_event_system.h"
68
69 #include "ED_screen.h"
70 #include "ED_fileselect.h"
71
72 #include "UI_interface.h"
73
74 #include "PIL_time.h"
75
76 #include "GPU_draw.h"
77 #include "GPU_extensions.h"
78 #include "GPU_init_exit.h"
79 #include "GPU_glew.h"
80 #include "BLF_api.h"
81
82 /* for assert */
83 #ifndef NDEBUG
84 #  include "BLI_threads.h"
85 #endif
86
87 /* the global to talk to ghost */
88 static GHOST_SystemHandle g_system = NULL;
89
90 typedef enum WinOverrideFlag {
91         WIN_OVERRIDE_GEOM     = (1 << 0),
92         WIN_OVERRIDE_WINSTATE = (1 << 1)
93 } WinOverrideFlag;
94
95 /* set by commandline */
96 static struct WMInitStruct {
97         /* window geometry */
98         int size_x, size_y;
99         int start_x, start_y;
100
101         int windowstate;
102         WinOverrideFlag override_flag;
103         
104         bool native_pixels;
105 } wm_init_state = {0, 0, 0, 0, GHOST_kWindowStateNormal, 0, true};
106
107 /* ******** win open & close ************ */
108
109 /* XXX this one should correctly check for apple top header...
110  * done for Cocoa : returns window contents (and not frame) max size*/
111 void wm_get_screensize(int *r_width, int *r_height)
112 {
113         unsigned int uiwidth;
114         unsigned int uiheight;
115         
116         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
117         *r_width = uiwidth;
118         *r_height = uiheight;
119 }
120
121 /* size of all screens (desktop), useful since the mouse is bound by this */
122 void wm_get_desktopsize(int *r_width, int *r_height)
123 {
124         unsigned int uiwidth;
125         unsigned int uiheight;
126
127         GHOST_GetAllDisplayDimensions(g_system, &uiwidth, &uiheight);
128         *r_width = uiwidth;
129         *r_height = uiheight;
130 }
131
132 /* keeps offset and size within monitor bounds */
133 /* XXX solve dual screen... */
134 static void wm_window_check_position(rcti *rect)
135 {
136         int width, height, d;
137         
138         wm_get_screensize(&width, &height);
139         
140         if (rect->xmin < 0) {
141                 rect->xmax -= rect->xmin;
142                 rect->xmin  = 0;
143         }
144         if (rect->ymin < 0) {
145                 rect->ymax -= rect->ymin;
146                 rect->ymin  = 0;
147         }
148         if (rect->xmax > width) {
149                 d = rect->xmax - width;
150                 rect->xmax -= d;
151                 rect->xmin -= d;
152         }
153         if (rect->ymax > height) {
154                 d = rect->ymax - height;
155                 rect->ymax -= d;
156                 rect->ymin -= d;
157         }
158         
159         if (rect->xmin < 0) rect->xmin = 0;
160         if (rect->ymin < 0) rect->ymin = 0;
161 }
162
163
164 static void wm_ghostwindow_destroy(wmWindow *win) 
165 {
166         if (win->ghostwin) {
167                 GHOST_DisposeWindow(g_system, win->ghostwin);
168                 win->ghostwin = NULL;
169                 win->multisamples = 0;
170         }
171 }
172
173 /* including window itself, C can be NULL. 
174  * ED_screen_exit should have been called */
175 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
176 {
177         wmTimer *wt, *wtnext;
178         
179         /* update context */
180         if (C) {
181                 WM_event_remove_handlers(C, &win->handlers);
182                 WM_event_remove_handlers(C, &win->modalhandlers);
183
184                 if (CTX_wm_window(C) == win)
185                         CTX_wm_window_set(C, NULL);
186         }
187
188         /* always set drawable and active to NULL,
189          * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
190         wm->windrawable = NULL;
191         wm->winactive = NULL;
192
193         /* end running jobs, a job end also removes its timer */
194         for (wt = wm->timers.first; wt; wt = wtnext) {
195                 wtnext = wt->next;
196                 if (wt->win == win && wt->event_type == TIMERJOBS)
197                         wm_jobs_timer_ended(wm, wt);
198         }
199         
200         /* timer removing, need to call this api function */
201         for (wt = wm->timers.first; wt; wt = wtnext) {
202                 wtnext = wt->next;
203                 if (wt->win == win)
204                         WM_event_remove_timer(wm, win, wt);
205         }
206
207         if (win->eventstate) MEM_freeN(win->eventstate);
208         
209         wm_event_free_all(win);
210         wm_subwindows_free(win);
211
212         wm_draw_data_free(win);
213
214         wm_ghostwindow_destroy(win);
215
216         MEM_freeN(win->stereo3d_format);
217
218         MEM_freeN(win);
219 }
220
221 static int find_free_winid(wmWindowManager *wm)
222 {
223         wmWindow *win;
224         int id = 1;
225         
226         for (win = wm->windows.first; win; win = win->next)
227                 if (id <= win->winid)
228                         id = win->winid + 1;
229         
230         return id;
231 }
232
233 /* don't change context itself */
234 wmWindow *wm_window_new(bContext *C)
235 {
236         wmWindowManager *wm = CTX_wm_manager(C);
237         wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
238         
239         BLI_addtail(&wm->windows, win);
240         win->winid = find_free_winid(wm);
241
242         win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)");
243
244         return win;
245 }
246
247
248 /* part of wm_window.c api */
249 wmWindow *wm_window_copy(bContext *C, wmWindow *win_src)
250 {
251         wmWindow *win_dst = wm_window_new(C);
252         
253         win_dst->posx = win_src->posx + 10;
254         win_dst->posy = win_src->posy;
255         win_dst->sizex = win_src->sizex;
256         win_dst->sizey = win_src->sizey;
257         
258         /* duplicate assigns to window */
259         win_dst->screen = ED_screen_duplicate(win_dst, win_src->screen);
260         BLI_strncpy(win_dst->screenname, win_dst->screen->id.name + 2, sizeof(win_dst->screenname));
261         win_dst->screen->winid = win_dst->winid;
262
263         win_dst->screen->do_refresh = true;
264         win_dst->screen->do_draw = true;
265
266         win_dst->drawmethod = U.wmdrawmethod;
267
268         BLI_listbase_clear(&win_dst->drawdata);
269
270         *win_dst->stereo3d_format = *win_src->stereo3d_format;
271
272         return win_dst;
273 }
274
275 /**
276  * A higher level version of copy that tests the new window can be added.
277  * (called from the operator directly)
278  */
279 wmWindow *wm_window_copy_test(bContext *C, wmWindow *win_src)
280 {
281         wmWindowManager *wm = CTX_wm_manager(C);
282         wmWindow *win_dst;
283
284         win_dst = wm_window_copy(C, win_src);
285
286         WM_check(C);
287
288         if (win_dst->ghostwin) {
289                 WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL);
290                 return win_dst;
291         }
292         else {
293                 wm_window_close(C, wm, win_dst);
294                 return NULL;
295         }
296 }
297
298 /* this is event from ghost, or exit-blender op */
299 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
300 {
301         wmWindow *tmpwin;
302         bool do_exit = false;
303         
304         /* first check if we have to quit (there are non-temp remaining windows) */
305         for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
306                 if (tmpwin == win)
307                         continue;
308                 if (tmpwin->screen->temp == 0)
309                         break;
310         }
311
312         if (tmpwin == NULL)
313                 do_exit = 1;
314         
315         if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved && !G.background) {
316                 if (do_exit) {
317                         if (!GHOST_confirmQuit(win->ghostwin))
318                                 return;
319                 }
320         }
321
322         /* let WM_exit do all freeing, for correct quit.blend save */
323         if (do_exit) {
324                 WM_exit(C);
325         }
326         else {
327                 bScreen *screen = win->screen;
328                 
329                 BLI_remlink(&wm->windows, win);
330                 
331                 wm_draw_window_clear(win);
332                 
333                 CTX_wm_window_set(C, win);  /* needed by handlers */
334                 WM_event_remove_handlers(C, &win->handlers);
335                 WM_event_remove_handlers(C, &win->modalhandlers);
336
337                 /* for regular use this will _never_ be NULL,
338                  * however we may be freeing an improperly initialized window. */
339                 if (win->screen) {
340                         ED_screen_exit(C, win, win->screen);
341                 }
342                 
343                 wm_window_free(C, wm, win);
344         
345                 /* if temp screen, delete it after window free (it stops jobs that can access it) */
346                 if (screen && screen->temp) {
347                         Main *bmain = CTX_data_main(C);
348                         BKE_libblock_free(bmain, screen);
349                 }
350         }               
351 }
352
353 void wm_window_title(wmWindowManager *wm, wmWindow *win)
354 {
355         if (win->screen && win->screen->temp) {
356                 /* nothing to do for 'temp' windows,
357                  * because WM_window_open_temp always sets window title  */
358         }
359         else if (win->ghostwin) {
360                 /* this is set to 1 if you don't have startup.blend open */
361                 if (G.save_over && G.main->name[0]) {
362                         char str[sizeof(G.main->name) + 24];
363                         BLI_snprintf(str, sizeof(str), "Blender%s [%s%s]", wm->file_saved ? "" : "*", G.main->name,
364                                      G.main->recovered ? " (Recovered)" : "");
365                         GHOST_SetTitle(win->ghostwin, str);
366                 }
367                 else
368                         GHOST_SetTitle(win->ghostwin, "Blender");
369
370                 /* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
371                  * and to give hint of unsaved changes for a user warning mechanism
372                  * in case of OS application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end) */
373                 GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8) !wm->file_saved);
374                 
375         }
376 }
377
378 void WM_window_set_dpi(wmWindow *win)
379 {
380         int auto_dpi = GHOST_GetDPIHint(win->ghostwin);
381
382         /* Lazily init UI scale size, preserving backwards compatibility by
383          * computing UI scale from ratio of previous DPI and auto DPI */
384         if (U.ui_scale == 0) {
385                 int virtual_pixel = (U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1 : 2;
386
387                 if (U.dpi == 0) {
388                         U.ui_scale = virtual_pixel;
389                 }
390                 else {
391                         U.ui_scale = (virtual_pixel * U.dpi * 96.0f) / (auto_dpi * 72.0f);
392                 }
393
394                 CLAMP(U.ui_scale, 0.25f, 4.0f);
395         }
396
397         /* Blender's UI drawing assumes DPI 72 as a good default following macOS
398          * while Windows and Linux use DPI 96. GHOST assumes a default 96 so we
399          * remap the DPI to Blender's convention. */
400         int dpi = auto_dpi * U.ui_scale * (72.0 / 96.0f);
401
402         /* Automatically set larger pixel size for high DPI. */
403         int pixelsize = MAX2(1, dpi / 54);
404
405         /* Set user preferences globals for drawing, and for forward compatibility. */
406         U.pixelsize = GHOST_GetNativePixelSize(win->ghostwin) * pixelsize;
407         U.dpi = dpi / pixelsize;
408         U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE;
409         U.widget_unit = (U.pixelsize * U.dpi * 20 + 36) / 72;
410
411         /* update font drawing */
412         BLF_default_dpi(U.pixelsize * U.dpi);
413 }
414
415 /* belongs to below */
416 static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win)
417 {
418         GHOST_WindowHandle ghostwin;
419         GHOST_GLSettings glSettings = {0};
420         static int multisamples = -1;
421         int scr_w, scr_h, posy;
422         
423         /* force setting multisamples only once, it requires restart - and you cannot 
424          * mix it, either all windows have it, or none (tested in OSX opengl) */
425         if (multisamples == -1)
426                 multisamples = U.ogl_multisamples;
427
428         glSettings.numOfAASamples = multisamples;
429
430         /* a new window is created when pageflip mode is required for a window */
431         if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP)
432                 glSettings.flags |= GHOST_glStereoVisual;
433
434         if (G.debug & G_DEBUG_GPU) {
435                 glSettings.flags |= GHOST_glDebugContext;
436         }
437
438         wm_get_screensize(&scr_w, &scr_h);
439         posy = (scr_h - win->posy - win->sizey);
440         
441         ghostwin = GHOST_CreateWindow(g_system, title,
442                                       win->posx, posy, win->sizex, win->sizey,
443 #ifdef __APPLE__
444                                       /* we agreed to not set any fullscreen or iconized state on startup */
445                                       GHOST_kWindowStateNormal,
446 #else
447                                       (GHOST_TWindowState)win->windowstate,
448 #endif
449                                       GHOST_kDrawingContextTypeOpenGL,
450                                       glSettings);
451         
452         if (ghostwin) {
453                 GHOST_RectangleHandle bounds;
454                 
455                 /* the new window has already been made drawable upon creation */
456                 wm->windrawable = win;
457
458                 /* needed so we can detect the graphics card below */
459                 GPU_init();
460                 
461                 win->ghostwin = ghostwin;
462                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
463                 
464                 if (win->eventstate == NULL)
465                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
466
467                 /* store multisamples window was created with, in case user prefs change */
468                 win->multisamples = multisamples;
469                 
470                 /* store actual window size in blender window */
471                 bounds = GHOST_GetClientBounds(win->ghostwin);
472                 win->sizex = GHOST_GetWidthRectangle(bounds);
473                 win->sizey = GHOST_GetHeightRectangle(bounds);
474                 GHOST_DisposeRectangle(bounds);
475                 
476 #ifndef __APPLE__
477                 /* set the state here, so minimized state comes up correct on windows */
478                 GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate);
479 #endif
480                 /* until screens get drawn, make it nice gray */
481                 glClearColor(0.55, 0.55, 0.55, 0.0);
482                 /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
483                 if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
484                         glClear(GL_COLOR_BUFFER_BIT);
485                 }
486                 
487                 /* needed here, because it's used before it reads userdef */
488                 WM_window_set_dpi(win);
489                 
490                 wm_window_swap_buffers(win);
491                 
492                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
493                 
494                 /* standard state vars for window */
495                 glEnable(GL_SCISSOR_TEST);
496                 GPU_state_init();
497         }
498 }
499
500 /**
501  * Initialize #wmWindows without ghostwin, open these and clear.
502  *
503  * window size is read from window, if 0 it uses prefsize
504  * called in #WM_check, also inits stuff after file read.
505  *
506  * \warning
507  * After running, 'win->ghostwin' can be NULL in rare cases
508  * (where OpenGL driver fails to create a context for eg).
509  * We could remove them with #wm_window_ghostwindows_remove_invalid
510  * but better not since caller may continue to use.
511  * Instead, caller needs to handle the error case and cleanup.
512  */
513 void wm_window_ghostwindows_ensure(wmWindowManager *wm)
514 {
515         wmKeyMap *keymap;
516         wmWindow *win;
517         
518         BLI_assert(G.background == false);
519
520         /* no commandline prefsize? then we set this.
521          * Note that these values will be used only
522          * when there is no startup.blend yet.
523          */
524         if (wm_init_state.size_x == 0) {
525                 wm_get_screensize(&wm_init_state.size_x, &wm_init_state.size_y);
526                 
527                 /* note!, this isnt quite correct, active screen maybe offset 1000s if PX,
528                  * we'd need a wm_get_screensize like function that gives offset,
529                  * in practice the window manager will likely move to the correct monitor */
530                 wm_init_state.start_x = 0;
531                 wm_init_state.start_y = 0;
532
533 #ifdef WITH_X11 /* X11 */
534                 /* X11, start maximized but use default sane size */
535                 wm_init_state.size_x = min_ii(wm_init_state.size_x, WM_WIN_INIT_SIZE_X);
536                 wm_init_state.size_y = min_ii(wm_init_state.size_y, WM_WIN_INIT_SIZE_Y);
537                 /* pad */
538                 wm_init_state.start_x = WM_WIN_INIT_PAD;
539                 wm_init_state.start_y = WM_WIN_INIT_PAD;
540                 wm_init_state.size_x -= WM_WIN_INIT_PAD * 2;
541                 wm_init_state.size_y -= WM_WIN_INIT_PAD * 2;
542 #endif
543         }
544         
545         for (win = wm->windows.first; win; win = win->next) {
546                 if (win->ghostwin == NULL) {
547                         if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
548                                 win->posx = wm_init_state.start_x;
549                                 win->posy = wm_init_state.start_y;
550                                 win->sizex = wm_init_state.size_x;
551                                 win->sizey = wm_init_state.size_y;
552
553                                 win->windowstate = GHOST_kWindowStateNormal;
554                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
555                         }
556
557                         if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
558                                 win->windowstate = wm_init_state.windowstate;
559                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
560                         }
561
562                         /* without this, cursor restore may fail, T45456 */
563                         if (win->cursor == 0) {
564                                 win->cursor = CURSOR_STD;
565                         }
566
567                         wm_window_ghostwindow_add(wm, "Blender", win);
568                 }
569                 /* happens after fileread */
570                 if (win->eventstate == NULL)
571                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
572
573                 /* add keymap handlers (1 handler for all keys in map!) */
574                 keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0);
575                 WM_event_add_keymap_handler(&win->handlers, keymap);
576                 
577                 keymap = WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
578                 WM_event_add_keymap_handler(&win->handlers, keymap);
579
580                 keymap = WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
581                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
582                 
583                 /* add drop boxes */
584                 {
585                         ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
586                         WM_event_add_dropbox_handler(&win->handlers, lb);
587                 }
588                 wm_window_title(wm, win);
589         }
590 }
591
592 /**
593  * Call after #wm_window_ghostwindows_ensure or #WM_check
594  * (after loading a new file) in the unlikely event a window couldn't be created.
595  */
596 void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
597 {
598         wmWindow *win, *win_next;
599
600         BLI_assert(G.background == false);
601
602         for (win = wm->windows.first; win; win = win_next) {
603                 win_next = win->next;
604                 if (win->ghostwin == NULL) {
605                         wm_window_close(C, wm, win);
606                 }
607         }
608 }
609
610 /**
611  * new window, no screen yet, but we open ghostwindow for it,
612  * also gets the window level handlers
613  * \note area-rip calls this.
614  * \return the window or NULL.
615  */
616 wmWindow *WM_window_open(bContext *C, const rcti *rect)
617 {
618         wmWindow *win_prev = CTX_wm_window(C);
619         wmWindow *win = wm_window_new(C);
620         
621         win->posx = rect->xmin;
622         win->posy = rect->ymin;
623         win->sizex = BLI_rcti_size_x(rect);
624         win->sizey = BLI_rcti_size_y(rect);
625
626         win->drawmethod = U.wmdrawmethod;
627
628         WM_check(C);
629
630         if (win->ghostwin) {
631                 return win;
632         }
633         else {
634                 wm_window_close(C, CTX_wm_manager(C), win);
635                 CTX_wm_window_set(C, win_prev);
636                 return NULL;
637         }
638 }
639
640 /**
641  * Uses `screen->temp` tag to define what to do, currently it limits
642  * to only one "temp" window for render out, preferences, filewindow, etc...
643  *
644  * \param type: WM_WINDOW_RENDER, WM_WINDOW_USERPREFS...
645  * \return the window or NULL.
646  */
647 wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, int type)
648 {
649         wmWindow *win_prev = CTX_wm_window(C);
650         wmWindow *win;
651         ScrArea *sa;
652         Scene *scene = CTX_data_scene(C);
653         const char *title;
654
655         /* convert to native OS window coordinates */
656         const float native_pixel_size = GHOST_GetNativePixelSize(win_prev->ghostwin);
657         x /= native_pixel_size;
658         y /= native_pixel_size;
659         sizex /= native_pixel_size;
660         sizey /= native_pixel_size;
661
662         /* calculate postition */
663         rcti rect;
664         rect.xmin = x + win_prev->posx - sizex / 2;
665         rect.ymin = y + win_prev->posy - sizey / 2;
666         rect.xmax = rect.xmin + sizex;
667         rect.ymax = rect.ymin + sizey;
668
669         /* changes rect to fit within desktop */
670         wm_window_check_position(&rect);
671         
672         /* test if we have a temp screen already */
673         for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
674                 if (win->screen->temp)
675                         break;
676         
677         /* add new window? */
678         if (win == NULL) {
679                 win = wm_window_new(C);
680                 
681                 win->posx = rect.xmin;
682                 win->posy = rect.ymin;
683         }
684
685         win->sizex = BLI_rcti_size_x(&rect);
686         win->sizey = BLI_rcti_size_y(&rect);
687
688         if (win->ghostwin) {
689                 wm_window_set_size(win, win->sizex, win->sizey);
690                 wm_window_raise(win);
691         }
692         
693         if (win->screen == NULL) {
694                 /* add new screen */
695                 win->screen = ED_screen_add(win, scene, "temp");
696         }
697         else {
698                 /* switch scene for rendering */
699                 if (win->screen->scene != scene)
700                         ED_screen_set_scene(C, win->screen, scene);
701         }
702
703         win->screen->temp = 1; 
704         
705         /* make window active, and validate/resize */
706         CTX_wm_window_set(C, win);
707         WM_check(C);
708
709         /* It's possible `win->ghostwin == NULL`.
710          * instead of attempting to cleanup here (in a half finished state),
711          * finish setting up the screen, then free it at the end of the function,
712          * to avoid having to take into account a partially-created window.
713          */
714
715         /* ensure it shows the right spacetype editor */
716         sa = win->screen->areabase.first;
717         CTX_wm_area_set(C, sa);
718         
719         if (type == WM_WINDOW_RENDER) {
720                 ED_area_newspace(C, sa, SPACE_IMAGE, false);
721         }
722         else {
723                 ED_area_newspace(C, sa, SPACE_USERPREF, false);
724         }
725         
726         ED_screen_set(C, win->screen);
727         ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
728         
729         if (sa->spacetype == SPACE_IMAGE)
730                 title = IFACE_("Blender Render");
731         else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
732                 title = IFACE_("Blender User Preferences");
733         else if (sa->spacetype == SPACE_FILE)
734                 title = IFACE_("Blender File View");
735         else
736                 title = "Blender";
737
738         if (win->ghostwin) {
739                 GHOST_SetTitle(win->ghostwin, title);
740                 return win;
741         }
742         else {
743                 /* very unlikely! but opening a new window can fail */
744                 wm_window_close(C, CTX_wm_manager(C), win);
745                 CTX_wm_window_set(C, win_prev);
746
747                 return NULL;
748         }
749 }
750
751
752 /* ****************** Operators ****************** */
753
754 int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op))
755 {
756         wmWindowManager *wm = CTX_wm_manager(C);
757         wmWindow *win = CTX_wm_window(C);
758         wm_window_close(C, wm, win);
759         return OPERATOR_FINISHED;
760 }
761
762 /* operator callback */
763 int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
764 {
765         wmWindow *win_src = CTX_wm_window(C);
766         bool ok;
767
768         ok = (wm_window_copy_test(C, win_src) != NULL);
769
770         return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
771 }
772
773
774 /* fullscreen operator callback */
775 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
776 {
777         wmWindow *window = CTX_wm_window(C);
778         GHOST_TWindowState state;
779
780         if (G.background)
781                 return OPERATOR_CANCELLED;
782
783         state = GHOST_GetWindowState(window->ghostwin);
784         if (state != GHOST_kWindowStateFullScreen)
785                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
786         else
787                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
788
789         return OPERATOR_FINISHED;
790         
791 }
792
793
794 /* ************ events *************** */
795
796 void wm_cursor_position_from_ghost(wmWindow *win, int *x, int *y)
797 {
798         float fac = GHOST_GetNativePixelSize(win->ghostwin);
799         
800         GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
801         *x *= fac;
802         
803         *y = (win->sizey - 1) - *y;
804         *y *= fac;
805 }
806
807 void wm_cursor_position_to_ghost(wmWindow *win, int *x, int *y)
808 {
809         float fac = GHOST_GetNativePixelSize(win->ghostwin);
810
811         *x /= fac;
812         *y /= fac;
813         *y = win->sizey - *y - 1;
814
815         GHOST_ClientToScreen(win->ghostwin, *x, *y, x, y);
816 }
817
818 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
819 {
820         GHOST_GetCursorPosition(g_system, x, y);
821         wm_cursor_position_from_ghost(win, x, y);
822 }
823
824 typedef enum {
825         SHIFT    = 's',
826         CONTROL  = 'c',
827         ALT      = 'a',
828         OS       = 'C'
829 } modifierKeyType;
830
831 /* check if specified modifier key type is pressed */
832 static int query_qual(modifierKeyType qual) 
833 {
834         GHOST_TModifierKeyMask left, right;
835         int val = 0;
836         
837         switch (qual) {
838                 case SHIFT:
839                         left = GHOST_kModifierKeyLeftShift;
840                         right = GHOST_kModifierKeyRightShift;
841                         break;
842                 case CONTROL:
843                         left = GHOST_kModifierKeyLeftControl;
844                         right = GHOST_kModifierKeyRightControl;
845                         break;
846                 case OS:
847                         left = right = GHOST_kModifierKeyOS;
848                         break;
849                 case ALT:
850                 default:
851                         left = GHOST_kModifierKeyLeftAlt;
852                         right = GHOST_kModifierKeyRightAlt;
853                         break;
854         }
855         
856         GHOST_GetModifierKeyState(g_system, left, &val);
857         if (!val)
858                 GHOST_GetModifierKeyState(g_system, right, &val);
859         
860         return val;
861 }
862
863 void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) 
864 {
865         if (win != wm->windrawable && win->ghostwin) {
866 //              win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
867                 
868                 wm->windrawable = win;
869                 if (G.debug & G_DEBUG_EVENTS) {
870                         printf("%s: set drawable %d\n", __func__, win->winid);
871                 }
872                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
873                 
874                 /* this can change per window */
875                 WM_window_set_dpi(win);
876         }
877 }
878
879 /* called by ghost, here we handle events for windows themselves or send to event system */
880 /* mouse coordinate converversion happens here */
881 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
882 {
883         bContext *C = C_void_ptr;
884         wmWindowManager *wm = CTX_wm_manager(C);
885         GHOST_TEventType type = GHOST_GetEventType(evt);
886         int time = GHOST_GetEventTime(evt);
887         
888         if (type == GHOST_kEventQuit) {
889                 WM_exit(C);
890         }
891         else {
892                 GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt);
893                 GHOST_TEventDataPtr data = GHOST_GetEventData(evt);
894                 wmWindow *win;
895                 
896                 /* Ghost now can call this function for life resizes, but it should return if WM didn't initialize yet.
897                  * Can happen on file read (especially full size window)  */
898                 if ((wm->initialized & WM_INIT_WINDOW) == 0) {
899                         return 1;
900                 }
901                 if (!ghostwin) {
902                         /* XXX - should be checked, why are we getting an event here, and */
903                         /* what is it? */
904                         puts("<!> event has no window");
905                         return 1;
906                 }
907                 else if (!GHOST_ValidWindow(g_system, ghostwin)) {
908                         /* XXX - should be checked, why are we getting an event here, and */
909                         /* what is it? */
910                         puts("<!> event has invalid window");
911                         return 1;
912                 }
913                 else {
914                         win = GHOST_GetWindowUserData(ghostwin);
915                 }
916                 
917                 switch (type) {
918                         case GHOST_kEventWindowDeactivate:
919                                 wm_event_add_ghostevent(wm, win, type, time, data);
920                                 win->active = 0; /* XXX */
921                                 
922                                 /* clear modifiers for inactive windows */
923                                 win->eventstate->alt = 0;
924                                 win->eventstate->ctrl = 0;
925                                 win->eventstate->shift = 0;
926                                 win->eventstate->oskey = 0;
927                                 win->eventstate->keymodifier = 0;
928
929                                 break;
930                         case GHOST_kEventWindowActivate: 
931                         {
932                                 GHOST_TEventKeyData kdata;
933                                 wmEvent event;
934                                 int wx, wy;
935                                 const int keymodifier = ((query_qual(SHIFT)     ? KM_SHIFT : 0) |
936                                                          (query_qual(CONTROL)   ? KM_CTRL  : 0) |
937                                                          (query_qual(ALT)       ? KM_ALT   : 0) |
938                                                          (query_qual(OS)        ? KM_OSKEY : 0));
939
940                                 /* Win23/GHOST modifier bug, see T40317 */
941 #ifndef WIN32
942 //#  define USE_WIN_ACTIVATE
943 #endif
944
945                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
946                                 
947                                 win->active = 1;
948 //                              window_handle(win, INPUTCHANGE, win->active);
949                                 
950                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
951
952                                 /* TODO: This is not correct since a modifier may be held when a window is activated...
953                                  * better solve this at ghost level. attempted fix r54450 but it caused bug [#34255]
954                                  *
955                                  * For now don't send GHOST_kEventKeyDown events, just set the 'eventstate'.
956                                  */
957                                 kdata.ascii = '\0';
958                                 kdata.utf8_buf[0] = '\0';
959
960                                 if (win->eventstate->shift) {
961                                         if ((keymodifier & KM_SHIFT) == 0) {
962                                                 kdata.key = GHOST_kKeyLeftShift;
963                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
964                                         }
965                                 }
966 #ifdef USE_WIN_ACTIVATE
967                                 else {
968                                         if (keymodifier & KM_SHIFT) {
969                                                 win->eventstate->shift = KM_MOD_FIRST;
970                                         }
971                                 }
972 #endif
973                                 if (win->eventstate->ctrl) {
974                                         if ((keymodifier & KM_CTRL) == 0) {
975                                                 kdata.key = GHOST_kKeyLeftControl;
976                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
977                                         }
978                                 }
979 #ifdef USE_WIN_ACTIVATE
980                                 else {
981                                         if (keymodifier & KM_CTRL) {
982                                                 win->eventstate->ctrl = KM_MOD_FIRST;
983                                         }
984                                 }
985 #endif
986                                 if (win->eventstate->alt) {
987                                         if ((keymodifier & KM_ALT) == 0) {
988                                                 kdata.key = GHOST_kKeyLeftAlt;
989                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
990                                         }
991                                 }
992 #ifdef USE_WIN_ACTIVATE
993                                 else {
994                                         if (keymodifier & KM_ALT) {
995                                                 win->eventstate->alt = KM_MOD_FIRST;
996                                         }
997                                 }
998 #endif
999                                 if (win->eventstate->oskey) {
1000                                         if ((keymodifier & KM_OSKEY) == 0) {
1001                                                 kdata.key = GHOST_kKeyOS;
1002                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
1003                                         }
1004                                 }
1005 #ifdef USE_WIN_ACTIVATE
1006                                 else {
1007                                         if (keymodifier & KM_OSKEY) {
1008                                                 win->eventstate->oskey = KM_MOD_FIRST;
1009                                         }
1010                                 }
1011 #endif
1012
1013 #undef USE_WIN_ACTIVATE
1014
1015
1016                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
1017                                 win->eventstate->keymodifier = 0;
1018                                 
1019                                 /* entering window, update mouse pos. but no event */
1020                                 wm_get_cursor_position(win,  &wx, &wy);
1021
1022                                 win->eventstate->x = wx;
1023                                 win->eventstate->y = wy;
1024                                 
1025                                 win->addmousemove = 1;   /* enables highlighted buttons */
1026                                 
1027                                 wm_window_make_drawable(wm, win);
1028
1029                                 /* window might be focused by mouse click in configuration of window manager
1030                                  * when focus is not following mouse
1031                                  * click could have been done on a button and depending on window manager settings
1032                                  * click would be passed to blender or not, but in any case button under cursor
1033                                  * should be activated, so at max next click on button without moving mouse
1034                                  * would trigger it's handle function
1035                                  * currently it seems to be common practice to generate new event for, but probably
1036                                  * we'll need utility function for this? (sergey)
1037                                  */
1038                                 wm_event_init_from_window(win, &event);
1039                                 event.type = MOUSEMOVE;
1040                                 event.prevx = event.x;
1041                                 event.prevy = event.y;
1042
1043                                 wm_event_add(win, &event);
1044
1045                                 break;
1046                         }
1047                         case GHOST_kEventWindowClose:
1048                         {
1049                                 wm_window_close(C, wm, win);
1050                                 break;
1051                         }
1052                         case GHOST_kEventWindowUpdate:
1053                         {
1054                                 if (G.debug & G_DEBUG_EVENTS) {
1055                                         printf("%s: ghost redraw %d\n", __func__, win->winid);
1056                                 }
1057                                 
1058                                 wm_window_make_drawable(wm, win);
1059                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
1060
1061                                 break;
1062                         }
1063                         case GHOST_kEventWindowSize:
1064                         case GHOST_kEventWindowMove:
1065                         {
1066                                 GHOST_TWindowState state;
1067                                 state = GHOST_GetWindowState(win->ghostwin);
1068                                 win->windowstate = state;
1069
1070                                 /* stop screencast if resize */
1071                                 if (type == GHOST_kEventWindowSize) {
1072                                         WM_jobs_stop(wm, win->screen, NULL);
1073                                 }
1074
1075                                 WM_window_set_dpi(win);
1076                                 
1077                                 /* win32: gives undefined window size when minimized */
1078                                 if (state != GHOST_kWindowStateMinimized) {
1079                                         GHOST_RectangleHandle client_rect;
1080                                         int l, t, r, b, scr_w, scr_h;
1081                                         int sizex, sizey, posx, posy;
1082                                         
1083                                         client_rect = GHOST_GetClientBounds(win->ghostwin);
1084                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
1085                                         
1086                                         GHOST_DisposeRectangle(client_rect);
1087                                         
1088                                         wm_get_desktopsize(&scr_w, &scr_h);
1089                                         sizex = r - l;
1090                                         sizey = b - t;
1091                                         posx = l;
1092                                         posy = scr_h - t - win->sizey;
1093
1094                                         /*
1095                                          * Ghost sometimes send size or move events when the window hasn't changed.
1096                                          * One case of this is using compiz on linux. To alleviate the problem
1097                                          * we ignore all such event here.
1098                                          * 
1099                                          * It might be good to eventually do that at Ghost level, but that is for 
1100                                          * another time.
1101                                          */
1102                                         if (win->sizex != sizex ||
1103                                             win->sizey != sizey ||
1104                                             win->posx != posx ||
1105                                             win->posy != posy)
1106                                         {
1107                                                 win->sizex = sizex;
1108                                                 win->sizey = sizey;
1109                                                 win->posx = posx;
1110                                                 win->posy = posy;
1111
1112                                                 /* debug prints */
1113                                                 if (G.debug & G_DEBUG_EVENTS) {
1114                                                         const char *state_str;
1115                                                         state = GHOST_GetWindowState(win->ghostwin);
1116
1117                                                         if (state == GHOST_kWindowStateNormal) {
1118                                                                 state_str = "normal";
1119                                                         }
1120                                                         else if (state == GHOST_kWindowStateMinimized) {
1121                                                                 state_str = "minimized";
1122                                                         }
1123                                                         else if (state == GHOST_kWindowStateMaximized) {
1124                                                                 state_str = "maximized";
1125                                                         }
1126                                                         else if (state == GHOST_kWindowStateFullScreen) {
1127                                                                 state_str = "fullscreen";
1128                                                         }
1129                                                         else {
1130                                                                 state_str = "<unknown>";
1131                                                         }
1132
1133                                                         printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
1134
1135                                                         if (type != GHOST_kEventWindowSize) {
1136                                                                 printf("win move event pos %d %d size %d %d\n",
1137                                                                        win->posx, win->posy, win->sizex, win->sizey);
1138                                                         }
1139                                                 }
1140                                         
1141                                                 wm_window_make_drawable(wm, win);
1142                                                 wm_draw_window_clear(win);
1143                                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1144                                                 WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1145                                                 
1146 #if defined(__APPLE__) || defined(WIN32)
1147                                                 /* OSX and Win32 don't return to the mainloop while resize */
1148                                                 wm_event_do_notifiers(C);
1149                                                 wm_draw_update(C);
1150
1151                                                 /* Warning! code above nulls 'C->wm.window', causing BGE to quit, see: T45699.
1152                                                  * Further, its easier to match behavior across platforms, so restore the window. */
1153                                                 CTX_wm_window_set(C, win);
1154 #endif
1155                                         }
1156                                 }
1157                                 break;
1158                         }
1159
1160                         case GHOST_kEventWindowDPIHintChanged:
1161                         {
1162                                 WM_window_set_dpi(win);
1163                                 /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
1164                                 BLF_cache_clear();
1165
1166                                 WM_main_add_notifier(NC_WINDOW, NULL);      /* full redraw */
1167                                 WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);    /* refresh region sizes */
1168                                 break;
1169                         }
1170
1171                         case GHOST_kEventOpenMainFile:
1172                         {
1173                                 PointerRNA props_ptr;
1174                                 wmWindow *oldWindow;
1175                                 const char *path = GHOST_GetEventData(evt);
1176                                 
1177                                 if (path) {
1178                                         wmOperatorType *ot = WM_operatortype_find("WM_OT_open_mainfile", false);
1179                                         /* operator needs a valid window in context, ensures
1180                                          * it is correctly set */
1181                                         oldWindow = CTX_wm_window(C);
1182                                         CTX_wm_window_set(C, win);
1183                                         
1184                                         WM_operator_properties_create_ptr(&props_ptr, ot);
1185                                         RNA_string_set(&props_ptr, "filepath", path);
1186                                         WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr);
1187                                         WM_operator_properties_free(&props_ptr);
1188                                         
1189                                         CTX_wm_window_set(C, oldWindow);
1190                                 }
1191                                 break;
1192                         }
1193                         case GHOST_kEventDraggingDropDone:
1194                         {
1195                                 wmEvent event;
1196                                 GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
1197                                 int wx, wy;
1198                                 
1199                                 /* entering window, update mouse pos */
1200                                 wm_get_cursor_position(win, &wx, &wy);
1201                                 win->eventstate->x = wx;
1202                                 win->eventstate->y = wy;
1203                                 
1204                                 wm_event_init_from_window(win, &event);  /* copy last state, like mouse coords */
1205                                 
1206                                 /* activate region */
1207                                 event.type = MOUSEMOVE;
1208                                 event.prevx = event.x;
1209                                 event.prevy = event.y;
1210                                 
1211                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
1212                                 win->active = 1;
1213                                 
1214                                 wm_event_add(win, &event);
1215                                 
1216                                 
1217                                 /* make blender drop event with custom data pointing to wm drags */
1218                                 event.type = EVT_DROP;
1219                                 event.val = KM_RELEASE;
1220                                 event.custom = EVT_DATA_DRAGDROP;
1221                                 event.customdata = &wm->drags;
1222                                 event.customdatafree = 1;
1223                                 
1224                                 wm_event_add(win, &event);
1225                                 
1226                                 /* printf("Drop detected\n"); */
1227                                 
1228                                 /* add drag data to wm for paths: */
1229                                 
1230                                 if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
1231                                         GHOST_TStringArray *stra = ddd->data;
1232                                         int a, icon;
1233                                         
1234                                         for (a = 0; a < stra->count; a++) {
1235                                                 printf("drop file %s\n", stra->strings[a]);
1236                                                 /* try to get icon type from extension */
1237                                                 icon = ED_file_extension_icon((char *)stra->strings[a]);
1238                                                 
1239                                                 WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0, WM_DRAG_NOP);
1240                                                 /* void poin should point to string, it makes a copy */
1241                                                 break; /* only one drop element supported now */
1242                                         }
1243                                 }
1244                                 
1245                                 break;
1246                         }
1247                         case GHOST_kEventNativeResolutionChange:
1248                         {
1249                                 // only update if the actual pixel size changes
1250                                 float prev_pixelsize = U.pixelsize;
1251                                 WM_window_set_dpi(win);
1252
1253                                 if (U.pixelsize != prev_pixelsize) {
1254                                         // close all popups since they are positioned with the pixel
1255                                         // size baked in and it's difficult to correct them
1256                                         wmWindow *oldWindow = CTX_wm_window(C);
1257                                         CTX_wm_window_set(C, win);
1258                                         UI_popup_handlers_remove_all(C, &win->modalhandlers);
1259                                         CTX_wm_window_set(C, oldWindow);
1260
1261                                         wm_window_make_drawable(wm, win);
1262                                         wm_draw_window_clear(win);
1263
1264                                         WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1265                                         WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1266                                 }
1267
1268                                 break;
1269                         }
1270                         case GHOST_kEventTrackpad:
1271                         {
1272                                 GHOST_TEventTrackpadData *pd = data;
1273                                 
1274                                 wm_cursor_position_from_ghost(win, &pd->x, &pd->y);
1275                                 wm_event_add_ghostevent(wm, win, type, time, data);
1276                                 break;
1277                         }
1278                         case GHOST_kEventCursorMove:
1279                         {
1280                                 GHOST_TEventCursorData *cd = data;
1281                                 
1282                                 wm_cursor_position_from_ghost(win, &cd->x, &cd->y);
1283                                 wm_event_add_ghostevent(wm, win, type, time, data);
1284                                 break;
1285                         }
1286                         default:
1287                                 wm_event_add_ghostevent(wm, win, type, time, data);
1288                                 break;
1289                 }
1290
1291         }
1292         return 1;
1293 }
1294
1295
1296 /**
1297  * This timer system only gives maximum 1 timer event per redraw cycle,
1298  * to prevent queues to get overloaded.
1299  * Timer handlers should check for delta to decide if they just update, or follow real time.
1300  * Timer handlers can also set duration to match frames passed
1301  */
1302 static int wm_window_timer(const bContext *C)
1303 {
1304         wmWindowManager *wm = CTX_wm_manager(C);
1305         wmTimer *wt, *wtnext;
1306         wmWindow *win;
1307         double time = PIL_check_seconds_timer();
1308         int retval = 0;
1309         
1310         for (wt = wm->timers.first; wt; wt = wtnext) {
1311                 wtnext = wt->next; /* in case timer gets removed */
1312                 win = wt->win;
1313
1314                 if (wt->sleep == 0) {
1315                         if (time > wt->ntime) {
1316                                 wt->delta = time - wt->ltime;
1317                                 wt->duration += wt->delta;
1318                                 wt->ltime = time;
1319                                 wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep);
1320
1321                                 if (wt->event_type == TIMERJOBS)
1322                                         wm_jobs_timer(C, wm, wt);
1323                                 else if (wt->event_type == TIMERAUTOSAVE)
1324                                         wm_autosave_timer(C, wm, wt);
1325                                 else if (wt->event_type == TIMERNOTIFIER)
1326                                         WM_main_add_notifier(GET_UINT_FROM_POINTER(wt->customdata), NULL);
1327                                 else if (win) {
1328                                         wmEvent event;
1329                                         wm_event_init_from_window(win, &event);
1330                                         
1331                                         event.type = wt->event_type;
1332                                         event.val = KM_NOTHING;
1333                                         event.keymodifier = 0;
1334                                         event.custom = EVT_DATA_TIMER;
1335                                         event.customdata = wt;
1336                                         wm_event_add(win, &event);
1337
1338                                         retval = 1;
1339                                 }
1340                         }
1341                 }
1342         }
1343         return retval;
1344 }
1345
1346 void wm_window_process_events(const bContext *C) 
1347 {
1348         int hasevent;
1349
1350         BLI_assert(BLI_thread_is_main());
1351
1352         hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
1353
1354         if (hasevent)
1355                 GHOST_DispatchEvents(g_system);
1356         
1357         hasevent |= wm_window_timer(C);
1358
1359         /* no event, we sleep 5 milliseconds */
1360         if (hasevent == 0)
1361                 PIL_sleep_ms(5);
1362 }
1363
1364 void wm_window_process_events_nosleep(void) 
1365 {
1366         if (GHOST_ProcessEvents(g_system, 0))
1367                 GHOST_DispatchEvents(g_system);
1368 }
1369
1370 /* exported as handle callback to bke blender.c */
1371 void wm_window_testbreak(void)
1372 {
1373         static double ltime = 0;
1374         double curtime = PIL_check_seconds_timer();
1375
1376         BLI_assert(BLI_thread_is_main());
1377
1378         /* only check for breaks every 50 milliseconds
1379          * if we get called more often.
1380          */
1381         if ((curtime - ltime) > 0.05) {
1382                 int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
1383                 
1384                 if (hasevent)
1385                         GHOST_DispatchEvents(g_system);
1386                 
1387                 ltime = curtime;
1388         }
1389 }
1390
1391 /* **************** init ********************** */
1392
1393 void wm_ghost_init(bContext *C)
1394 {
1395         if (!g_system) {
1396                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
1397                 
1398                 g_system = GHOST_CreateSystem();
1399                 GHOST_AddEventConsumer(g_system, consumer);
1400                 
1401                 if (wm_init_state.native_pixels) {
1402                         GHOST_UseNativePixels();
1403                 }
1404         }
1405 }
1406
1407 void wm_ghost_exit(void)
1408 {
1409         if (g_system)
1410                 GHOST_DisposeSystem(g_system);
1411
1412         g_system = NULL;
1413 }
1414
1415 /* **************** timer ********************** */
1416
1417 /* to (de)activate running timers temporary */
1418 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer, bool do_sleep)
1419 {
1420         wmTimer *wt;
1421         
1422         for (wt = wm->timers.first; wt; wt = wt->next)
1423                 if (wt == timer)
1424                         break;
1425
1426         if (wt)
1427                 wt->sleep = do_sleep;
1428 }
1429
1430 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
1431 {
1432         wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1433
1434         wt->event_type = event_type;
1435         wt->ltime = PIL_check_seconds_timer();
1436         wt->ntime = wt->ltime + timestep;
1437         wt->stime = wt->ltime;
1438         wt->timestep = timestep;
1439         wt->win = win;
1440         
1441         BLI_addtail(&wm->timers, wt);
1442         
1443         return wt;
1444 }
1445
1446 wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm, wmWindow *win, unsigned int type, double timestep)
1447 {
1448         wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1449
1450         wt->event_type = TIMERNOTIFIER;
1451         wt->ltime = PIL_check_seconds_timer();
1452         wt->ntime = wt->ltime + timestep;
1453         wt->stime = wt->ltime;
1454         wt->timestep = timestep;
1455         wt->win = win;
1456         wt->customdata = SET_UINT_IN_POINTER(type);
1457
1458         BLI_addtail(&wm->timers, wt);
1459
1460         return wt;
1461 }
1462
1463 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
1464 {
1465         wmTimer *wt;
1466         
1467         /* extra security check */
1468         for (wt = wm->timers.first; wt; wt = wt->next)
1469                 if (wt == timer)
1470                         break;
1471         if (wt) {
1472                 wmWindow *win;
1473                 
1474                 if (wm->reports.reporttimer == wt)
1475                         wm->reports.reporttimer = NULL;
1476                 
1477                 BLI_remlink(&wm->timers, wt);
1478                 if (wt->customdata)
1479                         MEM_freeN(wt->customdata);
1480                 MEM_freeN(wt);
1481                 
1482                 /* there might be events in queue with this timer as customdata */
1483                 for (win = wm->windows.first; win; win = win->next) {
1484                         wmEvent *event;
1485                         for (event = win->queue.first; event; event = event->next) {
1486                                 if (event->customdata == wt) {
1487                                         event->customdata = NULL;
1488                                         event->type = EVENT_NONE;       /* timer users customdata, dont want NULL == NULL */
1489                                 }
1490                         }
1491                 }
1492         }
1493 }
1494
1495 void WM_event_remove_timer_notifier(wmWindowManager *wm, wmWindow *win, wmTimer *timer)
1496 {
1497         timer->customdata = NULL;
1498         WM_event_remove_timer(wm, win, timer);
1499 }
1500
1501 /* ******************* clipboard **************** */
1502
1503 static char *wm_clipboard_text_get_ex(bool selection, int *r_len,
1504                                       bool firstline)
1505 {
1506         char *p, *p2, *buf, *newbuf;
1507
1508         if (G.background) {
1509                 *r_len = 0;
1510                 return NULL;
1511         }
1512
1513         buf = (char *)GHOST_getClipboard(selection);
1514         if (!buf) {
1515                 *r_len = 0;
1516                 return NULL;
1517         }
1518         
1519         /* always convert from \r\n to \n */
1520         p2 = newbuf = MEM_mallocN(strlen(buf) + 1, __func__);
1521
1522         if (firstline) {
1523                 /* will return an over-alloc'ed value in the case there are newlines */
1524                 for (p = buf; *p; p++) {
1525                         if ((*p != '\n') && (*p != '\r')) {
1526                                 *(p2++) = *p;
1527                         }
1528                         else {
1529                                 break;
1530                         }
1531                 }
1532         }
1533         else {
1534                 for (p = buf; *p; p++) {
1535                         if (*p != '\r') {
1536                                 *(p2++) = *p;
1537                         }
1538                 }
1539         }
1540
1541         *p2 = '\0';
1542
1543         free(buf); /* ghost uses regular malloc */
1544         
1545         *r_len = (p2 - newbuf);
1546
1547         return newbuf;
1548 }
1549
1550 /**
1551  * Return text from the clipboard.
1552  *
1553  * \note Caller needs to check for valid utf8 if this is a requirement.
1554  */
1555 char *WM_clipboard_text_get(bool selection, int *r_len)
1556 {
1557         return wm_clipboard_text_get_ex(selection, r_len, false);
1558 }
1559
1560 /**
1561  * Convenience function for pasting to areas of Blender which don't support newlines.
1562  */
1563 char *WM_clipboard_text_get_firstline(bool selection, int *r_len)
1564 {
1565         return wm_clipboard_text_get_ex(selection, r_len, true);
1566 }
1567
1568 void WM_clipboard_text_set(const char *buf, bool selection)
1569 {
1570         if (!G.background) {
1571 #ifdef _WIN32
1572                 /* do conversion from \n to \r\n on Windows */
1573                 const char *p;
1574                 char *p2, *newbuf;
1575                 int newlen = 0;
1576                 
1577                 for (p = buf; *p; p++) {
1578                         if (*p == '\n')
1579                                 newlen += 2;
1580                         else
1581                                 newlen++;
1582                 }
1583                 
1584                 newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
1585         
1586                 for (p = buf, p2 = newbuf; *p; p++, p2++) {
1587                         if (*p == '\n') {
1588                                 *(p2++) = '\r'; *p2 = '\n';
1589                         }
1590                         else {
1591                                 *p2 = *p;
1592                         }
1593                 }
1594                 *p2 = '\0';
1595         
1596                 GHOST_putClipboard((GHOST_TInt8 *)newbuf, selection);
1597                 MEM_freeN(newbuf);
1598 #else
1599                 GHOST_putClipboard((GHOST_TInt8 *)buf, selection);
1600 #endif
1601         }
1602 }
1603
1604 /* ******************* progress bar **************** */
1605
1606 void WM_progress_set(wmWindow *win, float progress)
1607 {
1608         GHOST_SetProgressBar(win->ghostwin, progress);
1609 }
1610
1611 void WM_progress_clear(wmWindow *win)
1612 {
1613         GHOST_EndProgressBar(win->ghostwin);
1614 }
1615
1616 /* ************************************ */
1617
1618 void wm_window_get_position(wmWindow *win, int *r_pos_x, int *r_pos_y)
1619 {
1620         *r_pos_x = win->posx;
1621         *r_pos_y = win->posy;
1622 }
1623
1624 void wm_window_set_size(wmWindow *win, int width, int height) 
1625 {
1626         GHOST_SetClientSize(win->ghostwin, width, height);
1627 }
1628
1629 void wm_window_lower(wmWindow *win) 
1630 {
1631         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
1632 }
1633
1634 void wm_window_raise(wmWindow *win) 
1635 {
1636         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
1637 }
1638
1639 void wm_window_swap_buffers(wmWindow *win)
1640 {
1641         
1642 #ifdef WIN32
1643         glDisable(GL_SCISSOR_TEST);
1644         GHOST_SwapWindowBuffers(win->ghostwin);
1645         glEnable(GL_SCISSOR_TEST);
1646 #else
1647         GHOST_SwapWindowBuffers(win->ghostwin);
1648 #endif
1649 }
1650
1651 void wm_window_set_swap_interval (wmWindow *win, int interval)
1652 {
1653         GHOST_SetSwapInterval(win->ghostwin, interval);
1654 }
1655
1656 bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut)
1657 {
1658         return GHOST_GetSwapInterval(win->ghostwin, intervalOut);
1659 }
1660
1661
1662 /* ******************* exported api ***************** */
1663
1664
1665 /* called whem no ghost system was initialized */
1666 void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
1667 {
1668         wm_init_state.start_x = stax; /* left hand pos */
1669         wm_init_state.start_y = stay; /* bottom pos */
1670         wm_init_state.size_x = sizx < 640 ? 640 : sizx;
1671         wm_init_state.size_y = sizy < 480 ? 480 : sizy;
1672         wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
1673 }
1674
1675 /* for borderless and border windows set from command-line */
1676 void WM_init_state_fullscreen_set(void)
1677 {
1678         wm_init_state.windowstate = GHOST_kWindowStateFullScreen;
1679         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1680 }
1681
1682 void WM_init_state_normal_set(void)
1683 {
1684         wm_init_state.windowstate = GHOST_kWindowStateNormal;
1685         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1686 }
1687
1688 void WM_init_native_pixels(bool do_it)
1689 {
1690         wm_init_state.native_pixels = do_it;
1691 }
1692
1693 /* This function requires access to the GHOST_SystemHandle (g_system) */
1694 void WM_cursor_warp(wmWindow *win, int x, int y)
1695 {
1696         if (win && win->ghostwin) {
1697                 int oldx = x, oldy = y;
1698
1699                 wm_cursor_position_to_ghost(win, &x, &y);
1700                 GHOST_SetCursorPosition(g_system, x, y);
1701
1702                 win->eventstate->prevx = oldx;
1703                 win->eventstate->prevy = oldy;
1704
1705                 win->eventstate->x = oldx;
1706                 win->eventstate->y = oldy;
1707         }
1708 }
1709
1710 /**
1711  * Set x, y to values we can actually position the cursor to.
1712  */
1713 void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y)
1714 {
1715         float f = GHOST_GetNativePixelSize(win->ghostwin);
1716         if (f != 1.0f) {
1717                 *x = (int)(*x / f) * f;
1718                 *y = (int)(*y / f) * f;
1719         }
1720 }
1721
1722 /**
1723  * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event
1724  */
1725 float WM_cursor_pressure(const struct wmWindow *win)
1726 {
1727         const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin);
1728         /* if there's tablet data from an active tablet device then add it */
1729         if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
1730                 return td->Pressure;
1731         }
1732         else {
1733                 return -1.0f;
1734         }
1735 }
1736
1737 /* support for native pixel size */
1738 /* mac retina opens window in size X, but it has up to 2 x more pixels */
1739 int WM_window_pixels_x(wmWindow *win)
1740 {
1741         float f = GHOST_GetNativePixelSize(win->ghostwin);
1742         
1743         return (int)(f * (float)win->sizex);
1744 }
1745
1746 int WM_window_pixels_y(wmWindow *win)
1747 {
1748         float f = GHOST_GetNativePixelSize(win->ghostwin);
1749         
1750         return (int)(f * (float)win->sizey);
1751         
1752 }
1753
1754 bool WM_window_is_fullscreen(wmWindow *win)
1755 {
1756         return win->windowstate == GHOST_kWindowStateFullScreen;
1757 }
1758
1759
1760 #ifdef WITH_INPUT_IME
1761 /* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */
1762 void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete)
1763 {
1764         BLI_assert(win);
1765
1766         GHOST_BeginIME(win->ghostwin, x, win->sizey - y, w, h, complete);
1767 }
1768
1769 void wm_window_IME_end(wmWindow *win)
1770 {
1771         BLI_assert(win && win->ime_data);
1772
1773         GHOST_EndIME(win->ghostwin);
1774         win->ime_data = NULL;
1775 }
1776 #endif  /* WITH_INPUT_IME */