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