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