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