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