Fix T66862: crash with bevel modifier harden normals applied to curve object
[blender.git] / source / blender / windowmanager / intern / wm_window.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2007 Blender Foundation but based
17  * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV
18  * All rights reserved.
19  */
20
21 /** \file
22  * \ingroup wm
23  *
24  * Window management, wrap GHOST.
25  */
26
27 #include <math.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include "DNA_listBase.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_windowmanager_types.h"
35 #include "DNA_workspace_types.h"
36
37 #include "MEM_guardedalloc.h"
38
39 #include "GHOST_C-api.h"
40
41 #include "BLI_math.h"
42 #include "BLI_blenlib.h"
43 #include "BLI_utildefines.h"
44
45 #include "BLT_translation.h"
46
47 #include "BKE_blender.h"
48 #include "BKE_context.h"
49 #include "BKE_global.h"
50 #include "BKE_icons.h"
51 #include "BKE_layer.h"
52 #include "BKE_main.h"
53 #include "BKE_screen.h"
54 #include "BKE_workspace.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58 #include "RNA_enum_types.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62 #include "wm.h"
63 #include "wm_draw.h"
64 #include "wm_files.h"
65 #include "wm_window.h"
66 #include "wm_event_system.h"
67
68 #include "ED_anim_api.h"
69 #include "ED_render.h"
70 #include "ED_scene.h"
71 #include "ED_screen.h"
72 #include "ED_fileselect.h"
73
74 #include "UI_interface.h"
75 #include "UI_interface_icons.h"
76
77 #include "PIL_time.h"
78
79 #include "GPU_batch.h"
80 #include "GPU_batch_presets.h"
81 #include "GPU_draw.h"
82 #include "GPU_extensions.h"
83 #include "GPU_framebuffer.h"
84 #include "GPU_init_exit.h"
85 #include "GPU_immediate.h"
86 #include "GPU_material.h"
87 #include "GPU_texture.h"
88 #include "GPU_context.h"
89 #include "BLF_api.h"
90
91 #include "UI_resources.h"
92
93 /* for assert */
94 #ifndef NDEBUG
95 #  include "BLI_threads.h"
96 #endif
97
98 /* the global to talk to ghost */
99 static GHOST_SystemHandle g_system = NULL;
100
101 typedef enum eWinOverrideFlag {
102   WIN_OVERRIDE_GEOM = (1 << 0),
103   WIN_OVERRIDE_WINSTATE = (1 << 1),
104 } eWinOverrideFlag;
105
106 #define GHOST_WINDOW_STATE_DEFAULT GHOST_kWindowStateMaximized
107
108 /**
109  * Override defaults or startup file when #eWinOverrideFlag is set.
110  * These values are typically set by command line arguments.
111  */
112 static struct WMInitStruct {
113   /* window geometry */
114   int size_x, size_y;
115   int start_x, start_y;
116
117   int windowstate;
118   eWinOverrideFlag override_flag;
119
120   bool window_focus;
121   bool native_pixels;
122 } wm_init_state = {
123     .windowstate = GHOST_WINDOW_STATE_DEFAULT,
124     .window_focus = true,
125     .native_pixels = true,
126 };
127
128 /* -------------------------------------------------------------------- */
129 /** \name Window Open & Close
130  * \{ */
131
132 static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate);
133 static int wm_window_timer(const bContext *C);
134
135 /* XXX this one should correctly check for apple top header...
136  * done for Cocoa : returns window contents (and not frame) max size*/
137 void wm_get_screensize(int *r_width, int *r_height)
138 {
139   unsigned int uiwidth;
140   unsigned int uiheight;
141
142   GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
143   *r_width = uiwidth;
144   *r_height = uiheight;
145 }
146
147 /* size of all screens (desktop), useful since the mouse is bound by this */
148 void wm_get_desktopsize(int *r_width, int *r_height)
149 {
150   unsigned int uiwidth;
151   unsigned int uiheight;
152
153   GHOST_GetAllDisplayDimensions(g_system, &uiwidth, &uiheight);
154   *r_width = uiwidth;
155   *r_height = uiheight;
156 }
157
158 /* keeps offset and size within monitor bounds */
159 /* XXX solve dual screen... */
160 static void wm_window_check_position(rcti *rect)
161 {
162   int width, height, d;
163
164   wm_get_screensize(&width, &height);
165
166   if (rect->xmin < 0) {
167     rect->xmax -= rect->xmin;
168     rect->xmin = 0;
169   }
170   if (rect->ymin < 0) {
171     rect->ymax -= rect->ymin;
172     rect->ymin = 0;
173   }
174   if (rect->xmax > width) {
175     d = rect->xmax - width;
176     rect->xmax -= d;
177     rect->xmin -= d;
178   }
179   if (rect->ymax > height) {
180     d = rect->ymax - height;
181     rect->ymax -= d;
182     rect->ymin -= d;
183   }
184
185   if (rect->xmin < 0) {
186     rect->xmin = 0;
187   }
188   if (rect->ymin < 0) {
189     rect->ymin = 0;
190   }
191 }
192
193 static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
194 {
195   if (win->ghostwin) {
196     /* Prevents non-drawable state of main windows (bugs #22967,
197      * #25071 and possibly #22477 too). Always clear it even if
198      * this window was not the drawable one, because we mess with
199      * drawing context to discard the GW context. */
200     wm_window_clear_drawable(wm);
201
202     if (win == wm->winactive) {
203       wm->winactive = NULL;
204     }
205
206     /* We need this window's opengl context active to discard it. */
207     GHOST_ActivateWindowDrawingContext(win->ghostwin);
208     GPU_context_active_set(win->gpuctx);
209
210     /* Delete local gpu context.  */
211     GPU_context_discard(win->gpuctx);
212
213     GHOST_DisposeWindow(g_system, win->ghostwin);
214     win->ghostwin = NULL;
215     win->gpuctx = NULL;
216   }
217 }
218
219 /* including window itself, C can be NULL.
220  * ED_screen_exit should have been called */
221 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
222 {
223   wmTimer *wt, *wtnext;
224
225   /* update context */
226   if (C) {
227     WM_event_remove_handlers(C, &win->handlers);
228     WM_event_remove_handlers(C, &win->modalhandlers);
229
230     if (CTX_wm_window(C) == win) {
231       CTX_wm_window_set(C, NULL);
232     }
233   }
234
235   BKE_screen_area_map_free(&win->global_areas);
236
237   /* end running jobs, a job end also removes its timer */
238   for (wt = wm->timers.first; wt; wt = wtnext) {
239     wtnext = wt->next;
240     if (wt->win == win && wt->event_type == TIMERJOBS) {
241       wm_jobs_timer_ended(wm, wt);
242     }
243   }
244
245   /* timer removing, need to call this api function */
246   for (wt = wm->timers.first; wt; wt = wtnext) {
247     wtnext = wt->next;
248     if (wt->win == win) {
249       WM_event_remove_timer(wm, win, wt);
250     }
251   }
252
253   if (win->eventstate) {
254     MEM_freeN(win->eventstate);
255   }
256
257   if (win->cursor_keymap_status) {
258     MEM_freeN(win->cursor_keymap_status);
259   }
260
261   wm_event_free_all(win);
262
263   wm_ghostwindow_destroy(wm, win);
264
265   BKE_workspace_instance_hook_free(G_MAIN, win->workspace_hook);
266   MEM_freeN(win->stereo3d_format);
267
268   MEM_freeN(win);
269 }
270
271 static int find_free_winid(wmWindowManager *wm)
272 {
273   wmWindow *win;
274   int id = 1;
275
276   for (win = wm->windows.first; win; win = win->next) {
277     if (id <= win->winid) {
278       id = win->winid + 1;
279     }
280   }
281   return id;
282 }
283
284 /* don't change context itself */
285 wmWindow *wm_window_new(bContext *C, wmWindow *parent)
286 {
287   Main *bmain = CTX_data_main(C);
288   wmWindowManager *wm = CTX_wm_manager(C);
289   wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
290
291   BLI_addtail(&wm->windows, win);
292   win->winid = find_free_winid(wm);
293
294   win->parent = (parent && parent->parent) ? parent->parent : parent;
295   win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)");
296   win->workspace_hook = BKE_workspace_instance_hook_create(bmain);
297
298   return win;
299 }
300
301 /* part of wm_window.c api */
302 wmWindow *wm_window_copy(bContext *C,
303                          wmWindow *win_src,
304                          const bool duplicate_layout,
305                          const bool child)
306 {
307   Main *bmain = CTX_data_main(C);
308   wmWindow *win_parent = (child) ? win_src : win_src->parent;
309   wmWindow *win_dst = wm_window_new(C, win_parent);
310   WorkSpace *workspace = WM_window_get_active_workspace(win_src);
311   WorkSpaceLayout *layout_old = WM_window_get_active_layout(win_src);
312   WorkSpaceLayout *layout_new;
313
314   win_dst->posx = win_src->posx + 10;
315   win_dst->posy = win_src->posy;
316   win_dst->sizex = win_src->sizex;
317   win_dst->sizey = win_src->sizey;
318
319   win_dst->scene = win_src->scene;
320   STRNCPY(win_dst->view_layer_name, win_src->view_layer_name);
321   BKE_workspace_active_set(win_dst->workspace_hook, workspace);
322   layout_new = duplicate_layout ?
323                    ED_workspace_layout_duplicate(bmain, workspace, layout_old, win_dst) :
324                    layout_old;
325   BKE_workspace_hook_layout_for_workspace_set(win_dst->workspace_hook, workspace, layout_new);
326
327   *win_dst->stereo3d_format = *win_src->stereo3d_format;
328
329   return win_dst;
330 }
331
332 /**
333  * A higher level version of copy that tests the new window can be added.
334  * (called from the operator directly)
335  */
336 wmWindow *wm_window_copy_test(bContext *C,
337                               wmWindow *win_src,
338                               const bool duplicate_layout,
339                               const bool child)
340 {
341   wmWindowManager *wm = CTX_wm_manager(C);
342   wmWindow *win_dst;
343
344   win_dst = wm_window_copy(C, win_src, duplicate_layout, child);
345
346   WM_check(C);
347
348   if (win_dst->ghostwin) {
349     WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL);
350     return win_dst;
351   }
352   else {
353     wm_window_close(C, wm, win_dst);
354     return NULL;
355   }
356 }
357
358 /** \} */
359
360 /* -------------------------------------------------------------------- */
361 /** \name Quit Confirmation Dialog
362  * \{ */
363
364 static void wm_save_file_on_quit_dialog_callback(bContext *C, void *UNUSED(user_data))
365 {
366   wm_exit_schedule_delayed(C);
367 }
368
369 /**
370  * Call the confirm dialog on quitting. It's displayed in the context window so
371  * caller should set it as desired.
372  */
373 static void wm_confirm_quit(bContext *C)
374 {
375   wmGenericCallback *action = MEM_callocN(sizeof(*action), __func__);
376   action->exec = wm_save_file_on_quit_dialog_callback;
377   wm_close_file_dialog(C, action);
378 }
379
380 /**
381  * Call the quit confirmation prompt or exit directly if needed. The use can
382  * still cancel via the confirmation popup. Also, this may not quit Blender
383  * immediately, but rather schedule the closing.
384  *
385  * \param win: The window to show the confirmation popup/window in.
386  */
387 void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
388 {
389   wmWindow *win_ctx = CTX_wm_window(C);
390
391   /* The popup will be displayed in the context window which may not be set
392    * here (this function gets called outside of normal event handling loop). */
393   CTX_wm_window_set(C, win);
394
395   if (U.uiflag & USER_SAVE_PROMPT) {
396     if (wm_file_or_image_is_modified(C) && !G.background) {
397       wm_confirm_quit(C);
398     }
399     else {
400       wm_exit_schedule_delayed(C);
401     }
402   }
403   else {
404     wm_exit_schedule_delayed(C);
405   }
406
407   CTX_wm_window_set(C, win_ctx);
408 }
409
410 /** \} */
411
412 /* this is event from ghost, or exit-blender op */
413 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
414 {
415   /* First check if there is another main window remaining. */
416   wmWindow *win_other;
417   for (win_other = wm->windows.first; win_other; win_other = win_other->next) {
418     if (win_other != win && win_other->parent == NULL && !WM_window_is_temp_screen(win_other)) {
419       break;
420     }
421   }
422
423   if (win->parent == NULL && win_other == NULL) {
424     wm_quit_with_optional_confirmation_prompt(C, win);
425     return;
426   }
427
428   /* close child windows */
429   for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
430     if (win_child->parent == win) {
431       wm_window_close(C, wm, win_child);
432     }
433   }
434
435   bScreen *screen = WM_window_get_active_screen(win);
436   WorkSpace *workspace = WM_window_get_active_workspace(win);
437   WorkSpaceLayout *layout = BKE_workspace_active_layout_get(win->workspace_hook);
438
439   BLI_remlink(&wm->windows, win);
440
441   CTX_wm_window_set(C, win); /* needed by handlers */
442   WM_event_remove_handlers(C, &win->handlers);
443   WM_event_remove_handlers(C, &win->modalhandlers);
444
445   /* for regular use this will _never_ be NULL,
446    * however we may be freeing an improperly initialized window. */
447   if (screen) {
448     ED_screen_exit(C, win, screen);
449   }
450
451   wm_window_free(C, wm, win);
452
453   /* if temp screen, delete it after window free (it stops jobs that can access it) */
454   if (screen && screen->temp) {
455     Main *bmain = CTX_data_main(C);
456
457     BLI_assert(BKE_workspace_layout_screen_get(layout) == screen);
458     BKE_workspace_layout_remove(bmain, workspace, layout);
459   }
460 }
461
462 void wm_window_title(wmWindowManager *wm, wmWindow *win)
463 {
464   if (WM_window_is_temp_screen(win)) {
465     /* nothing to do for 'temp' windows,
466      * because WM_window_open_temp always sets window title  */
467   }
468   else if (win->ghostwin) {
469     /* this is set to 1 if you don't have startup.blend open */
470     if (G.save_over && BKE_main_blendfile_path_from_global()[0]) {
471       char str[sizeof(((Main *)NULL)->name) + 24];
472       BLI_snprintf(str,
473                    sizeof(str),
474                    "Blender%s [%s%s]",
475                    wm->file_saved ? "" : "*",
476                    BKE_main_blendfile_path_from_global(),
477                    G_MAIN->recovered ? " (Recovered)" : "");
478       GHOST_SetTitle(win->ghostwin, str);
479     }
480     else {
481       GHOST_SetTitle(win->ghostwin, "Blender");
482     }
483
484     /* Informs GHOST of unsaved changes, to set window modified visual indicator (macOS)
485      * and to give hint of unsaved changes for a user warning mechanism in case of OS
486      * application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end). */
487     GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8)!wm->file_saved);
488   }
489 }
490
491 void WM_window_set_dpi(wmWindow *win)
492 {
493   float auto_dpi = GHOST_GetDPIHint(win->ghostwin);
494
495   /* Clamp auto DPI to 96, since our font/interface drawing does not work well
496    * with lower sizes. The main case we are interested in supporting is higher
497    * DPI. If a smaller UI is desired it is still possible to adjust UI scale. */
498   auto_dpi = max_ff(auto_dpi, 96.0f);
499
500   /* Lazily init UI scale size, preserving backwards compatibility by
501    * computing UI scale from ratio of previous DPI and auto DPI */
502   if (U.ui_scale == 0) {
503     int virtual_pixel = (U.virtual_pixel == VIRTUAL_PIXEL_NATIVE) ? 1 : 2;
504
505     if (U.dpi == 0) {
506       U.ui_scale = virtual_pixel;
507     }
508     else {
509       U.ui_scale = (virtual_pixel * U.dpi * 96.0f) / (auto_dpi * 72.0f);
510     }
511
512     CLAMP(U.ui_scale, 0.25f, 4.0f);
513   }
514
515   /* Blender's UI drawing assumes DPI 72 as a good default following macOS
516    * while Windows and Linux use DPI 96. GHOST assumes a default 96 so we
517    * remap the DPI to Blender's convention. */
518   auto_dpi *= GHOST_GetNativePixelSize(win->ghostwin);
519   int dpi = auto_dpi * U.ui_scale * (72.0 / 96.0f);
520
521   /* Automatically set larger pixel size for high DPI. */
522   int pixelsize = max_ii(1, (int)(dpi / 64));
523   /* User adjustment for pixel size. */
524   pixelsize = max_ii(1, pixelsize + U.ui_line_width);
525
526   /* Set user preferences globals for drawing, and for forward compatibility. */
527   U.pixelsize = pixelsize;
528   U.dpi = dpi / pixelsize;
529   U.virtual_pixel = (pixelsize == 1) ? VIRTUAL_PIXEL_NATIVE : VIRTUAL_PIXEL_DOUBLE;
530   U.dpi_fac = ((U.pixelsize * (float)U.dpi) / 72.0f);
531   U.inv_dpi_fac = 1.0f / U.dpi_fac;
532
533   /* Set user preferences globals for drawing, and for forward compatibility. */
534   U.widget_unit = (U.pixelsize * U.dpi * 20 + 36) / 72;
535   /* If line thickness differs from scaling factor then adjustments need to be made */
536   U.widget_unit += 2 * ((int)U.pixelsize - (int)U.dpi_fac);
537
538   /* update font drawing */
539   BLF_default_dpi(U.pixelsize * U.dpi);
540 }
541
542 static void wm_window_ensure_eventstate(wmWindow *win)
543 {
544   if (win->eventstate) {
545     return;
546   }
547
548   win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
549   wm_get_cursor_position(win, &win->eventstate->x, &win->eventstate->y);
550 }
551
552 /* belongs to below */
553 static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win)
554 {
555   GHOST_WindowHandle ghostwin;
556   GHOST_GLSettings glSettings = {0};
557   int scr_w, scr_h, posy;
558
559   /* a new window is created when pageflip mode is required for a window */
560   if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
561     glSettings.flags |= GHOST_glStereoVisual;
562   }
563
564   if (G.debug & G_DEBUG_GPU) {
565     glSettings.flags |= GHOST_glDebugContext;
566   }
567
568   wm_get_screensize(&scr_w, &scr_h);
569   posy = (scr_h - win->posy - win->sizey);
570
571   /* Clear drawable so we can set the new window. */
572   wmWindow *prev_windrawable = wm->windrawable;
573   wm_window_clear_drawable(wm);
574
575   ghostwin = GHOST_CreateWindow(g_system,
576                                 title,
577                                 win->posx,
578                                 posy,
579                                 win->sizex,
580                                 win->sizey,
581                                 (GHOST_TWindowState)win->windowstate,
582                                 GHOST_kDrawingContextTypeOpenGL,
583                                 glSettings);
584
585   if (ghostwin) {
586     GHOST_RectangleHandle bounds;
587
588     GLuint default_fb = GHOST_GetDefaultOpenGLFramebuffer(ghostwin);
589     win->gpuctx = GPU_context_create(default_fb);
590
591     /* needed so we can detect the graphics card below */
592     GPU_init();
593
594     /* Set window as drawable upon creation. Note this has already been
595      * it has already been activated by GHOST_CreateWindow. */
596     wm_window_set_drawable(wm, win, false);
597
598     win->ghostwin = ghostwin;
599     GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
600
601     wm_window_ensure_eventstate(win);
602
603     /* store actual window size in blender window */
604     bounds = GHOST_GetClientBounds(win->ghostwin);
605
606     /* win32: gives undefined window size when minimized */
607     if (GHOST_GetWindowState(win->ghostwin) != GHOST_kWindowStateMinimized) {
608       win->sizex = GHOST_GetWidthRectangle(bounds);
609       win->sizey = GHOST_GetHeightRectangle(bounds);
610     }
611     GHOST_DisposeRectangle(bounds);
612
613 #ifndef __APPLE__
614     /* set the state here, so minimized state comes up correct on windows */
615     if (wm_init_state.window_focus) {
616       GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate);
617     }
618 #endif
619     /* until screens get drawn, make it nice gray */
620     glClearColor(0.55, 0.55, 0.55, 0.0);
621     /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
622     if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
623       glClear(GL_COLOR_BUFFER_BIT);
624     }
625
626     /* needed here, because it's used before it reads userdef */
627     WM_window_set_dpi(win);
628
629     wm_window_swap_buffers(win);
630
631     // GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
632
633     /* standard state vars for window */
634     GPU_state_init();
635   }
636   else {
637     wm_window_set_drawable(wm, prev_windrawable, false);
638   }
639 }
640
641 /**
642  * Initialize #wmWindow without ghostwin, open these and clear.
643  *
644  * window size is read from window, if 0 it uses prefsize
645  * called in #WM_check, also inits stuff after file read.
646  *
647  * \warning
648  * After running, 'win->ghostwin' can be NULL in rare cases
649  * (where OpenGL driver fails to create a context for eg).
650  * We could remove them with #wm_window_ghostwindows_remove_invalid
651  * but better not since caller may continue to use.
652  * Instead, caller needs to handle the error case and cleanup.
653  */
654 void wm_window_ghostwindows_ensure(wmWindowManager *wm)
655 {
656   wmKeyMap *keymap;
657   wmWindow *win;
658
659   BLI_assert(G.background == false);
660
661   /* no commandline prefsize? then we set this.
662    * Note that these values will be used only
663    * when there is no startup.blend yet.
664    */
665   if (wm_init_state.size_x == 0) {
666     wm_get_screensize(&wm_init_state.size_x, &wm_init_state.size_y);
667
668     /* note!, this isnt quite correct, active screen maybe offset 1000s if PX,
669      * we'd need a wm_get_screensize like function that gives offset,
670      * in practice the window manager will likely move to the correct monitor */
671     wm_init_state.start_x = 0;
672     wm_init_state.start_y = 0;
673
674 #ifdef WITH_X11 /* X11 */
675     /* X11, don't start maximized because we can't figure out the dimensions
676      * of a single display yet if there are multiple, due to lack of Xinerama
677      * handling in GHOST. */
678     wm_init_state.size_x = min_ii(wm_init_state.size_x, WM_WIN_INIT_SIZE_X);
679     wm_init_state.size_y = min_ii(wm_init_state.size_y, WM_WIN_INIT_SIZE_Y);
680     /* pad */
681     wm_init_state.start_x = WM_WIN_INIT_PAD;
682     wm_init_state.start_y = WM_WIN_INIT_PAD;
683     wm_init_state.size_x -= WM_WIN_INIT_PAD * 2;
684     wm_init_state.size_y -= WM_WIN_INIT_PAD * 2;
685 #endif
686   }
687
688   for (win = wm->windows.first; win; win = win->next) {
689     if (win->ghostwin == NULL) {
690       if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
691         win->posx = wm_init_state.start_x;
692         win->posy = wm_init_state.start_y;
693         win->sizex = wm_init_state.size_x;
694         win->sizey = wm_init_state.size_y;
695
696         if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
697           win->windowstate = GHOST_kWindowStateNormal;
698           wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
699         }
700         else {
701           win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
702         }
703       }
704
705       if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
706         win->windowstate = wm_init_state.windowstate;
707         wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
708       }
709
710       /* without this, cursor restore may fail, T45456 */
711       if (win->cursor == 0) {
712         win->cursor = CURSOR_STD;
713       }
714
715       wm_window_ghostwindow_add(wm, "Blender", win);
716     }
717
718     if (win->ghostwin != NULL) {
719       /* If we have no ghostwin this is a buggy window that should be removed.
720        * However we still need to initialize it correctly so the screen doesn't hang. */
721
722       /* happens after fileread */
723       wm_window_ensure_eventstate(win);
724     }
725
726     /* add keymap handlers (1 handler for all keys in map!) */
727     keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
728     WM_event_add_keymap_handler(&win->handlers, keymap);
729
730     keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
731     WM_event_add_keymap_handler(&win->handlers, keymap);
732
733     keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
734     WM_event_add_keymap_handler(&win->modalhandlers, keymap);
735
736     /* add drop boxes */
737     {
738       ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
739       WM_event_add_dropbox_handler(&win->handlers, lb);
740     }
741     wm_window_title(wm, win);
742
743     /* add topbar */
744     ED_screen_global_areas_refresh(win);
745   }
746 }
747
748 /**
749  * Call after #wm_window_ghostwindows_ensure or #WM_check
750  * (after loading a new file) in the unlikely event a window couldn't be created.
751  */
752 void wm_window_ghostwindows_remove_invalid(bContext *C, wmWindowManager *wm)
753 {
754   wmWindow *win, *win_next;
755
756   BLI_assert(G.background == false);
757
758   for (win = wm->windows.first; win; win = win_next) {
759     win_next = win->next;
760     if (win->ghostwin == NULL) {
761       wm_window_close(C, wm, win);
762     }
763   }
764 }
765
766 /**
767  * new window, no screen yet, but we open ghostwindow for it,
768  * also gets the window level handlers
769  * \note area-rip calls this.
770  * \return the window or NULL.
771  */
772 wmWindow *WM_window_open(bContext *C, const rcti *rect)
773 {
774   wmWindow *win_prev = CTX_wm_window(C);
775   wmWindow *win = wm_window_new(C, win_prev);
776
777   win->posx = rect->xmin;
778   win->posy = rect->ymin;
779   win->sizex = BLI_rcti_size_x(rect);
780   win->sizey = BLI_rcti_size_y(rect);
781
782   WM_check(C);
783
784   if (win->ghostwin) {
785     return win;
786   }
787   else {
788     wm_window_close(C, CTX_wm_manager(C), win);
789     CTX_wm_window_set(C, win_prev);
790     return NULL;
791   }
792 }
793
794 /**
795  * Uses `screen->temp` tag to define what to do, currently it limits
796  * to only one "temp" window for render out, preferences, filewindow, etc...
797  *
798  * \param type: WM_WINDOW_RENDER, WM_WINDOW_USERPREFS...
799  * \return the window or NULL.
800  */
801 wmWindow *WM_window_open_temp(bContext *C, int x, int y, int sizex, int sizey, int type)
802 {
803   Main *bmain = CTX_data_main(C);
804   wmWindow *win_prev = CTX_wm_window(C);
805   wmWindow *win;
806   bScreen *screen;
807   ScrArea *sa;
808   Scene *scene = CTX_data_scene(C);
809   ViewLayer *view_layer = CTX_data_view_layer(C);
810   const char *title;
811
812   /* convert to native OS window coordinates */
813   const float native_pixel_size = GHOST_GetNativePixelSize(win_prev->ghostwin);
814   x /= native_pixel_size;
815   y /= native_pixel_size;
816   sizex /= native_pixel_size;
817   sizey /= native_pixel_size;
818
819   /* calculate position */
820   rcti rect;
821   rect.xmin = x + win_prev->posx - sizex / 2;
822   rect.ymin = y + win_prev->posy - sizey / 2;
823   rect.xmax = rect.xmin + sizex;
824   rect.ymax = rect.ymin + sizey;
825
826   /* changes rect to fit within desktop */
827   wm_window_check_position(&rect);
828
829   /* test if we have a temp screen already */
830   for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) {
831     if (WM_window_is_temp_screen(win)) {
832       break;
833     }
834   }
835
836   /* add new window? */
837   if (win == NULL) {
838     win = wm_window_new(C, win_prev);
839
840     win->posx = rect.xmin;
841     win->posy = rect.ymin;
842   }
843
844   screen = WM_window_get_active_screen(win);
845
846   win->sizex = BLI_rcti_size_x(&rect);
847   win->sizey = BLI_rcti_size_y(&rect);
848
849   if (win->ghostwin) {
850     wm_window_set_size(win, win->sizex, win->sizey);
851     wm_window_raise(win);
852   }
853
854   if (WM_window_get_active_workspace(win) == NULL) {
855     WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
856     BKE_workspace_active_set(win->workspace_hook, workspace);
857   }
858
859   if (screen == NULL) {
860     /* add new screen layout */
861     WorkSpace *workspace = WM_window_get_active_workspace(win);
862     WorkSpaceLayout *layout = ED_workspace_layout_add(bmain, workspace, win, "temp");
863
864     screen = BKE_workspace_layout_screen_get(layout);
865     WM_window_set_active_layout(win, workspace, layout);
866   }
867
868   /* Set scene and view layer to match original window. */
869   STRNCPY(win->view_layer_name, view_layer->name);
870   if (WM_window_get_active_scene(win) != scene) {
871     ED_screen_scene_change(C, win, scene);
872   }
873
874   screen->temp = 1;
875
876   /* make window active, and validate/resize */
877   CTX_wm_window_set(C, win);
878   WM_check(C);
879
880   /* It's possible `win->ghostwin == NULL`.
881    * instead of attempting to cleanup here (in a half finished state),
882    * finish setting up the screen, then free it at the end of the function,
883    * to avoid having to take into account a partially-created window.
884    */
885
886   /* ensure it shows the right spacetype editor */
887   sa = screen->areabase.first;
888   CTX_wm_area_set(C, sa);
889
890   if (type == WM_WINDOW_RENDER) {
891     ED_area_newspace(C, sa, SPACE_IMAGE, false);
892   }
893   else if (type == WM_WINDOW_DRIVERS) {
894     ED_area_newspace(C, sa, SPACE_GRAPH, false);
895   }
896   else {
897     ED_area_newspace(C, sa, SPACE_USERPREF, false);
898   }
899
900   ED_screen_change(C, screen);
901   ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
902
903   /* do additional setup for specific editor type */
904   if (type == WM_WINDOW_DRIVERS) {
905     ED_drivers_editor_init(C, sa);
906   }
907
908   if (sa->spacetype == SPACE_IMAGE) {
909     title = IFACE_("Blender Render");
910   }
911   else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF)) {
912     title = IFACE_("Blender Preferences");
913   }
914   else if (sa->spacetype == SPACE_FILE) {
915     title = IFACE_("Blender File View");
916   }
917   else if (sa->spacetype == SPACE_GRAPH) {
918     title = IFACE_("Blender Drivers Editor");
919   }
920   else {
921     title = "Blender";
922   }
923
924   if (win->ghostwin) {
925     GHOST_SetTitle(win->ghostwin, title);
926     return win;
927   }
928   else {
929     /* very unlikely! but opening a new window can fail */
930     wm_window_close(C, CTX_wm_manager(C), win);
931     CTX_wm_window_set(C, win_prev);
932
933     return NULL;
934   }
935 }
936
937 /* ****************** Operators ****************** */
938
939 int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op))
940 {
941   wmWindowManager *wm = CTX_wm_manager(C);
942   wmWindow *win = CTX_wm_window(C);
943   wm_window_close(C, wm, win);
944   return OPERATOR_FINISHED;
945 }
946
947 int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op))
948 {
949   wmWindow *win_src = CTX_wm_window(C);
950   bool ok;
951
952   ok = (wm_window_copy_test(C, win_src, true, true) != NULL);
953
954   return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
955 }
956
957 int wm_window_new_main_exec(bContext *C, wmOperator *UNUSED(op))
958 {
959   wmWindow *win_src = CTX_wm_window(C);
960   bool ok;
961
962   ok = (wm_window_copy_test(C, win_src, true, false) != NULL);
963
964   return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
965 }
966
967 /* fullscreen operator callback */
968 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
969 {
970   wmWindow *window = CTX_wm_window(C);
971   GHOST_TWindowState state;
972
973   if (G.background) {
974     return OPERATOR_CANCELLED;
975   }
976
977   state = GHOST_GetWindowState(window->ghostwin);
978   if (state != GHOST_kWindowStateFullScreen) {
979     GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
980   }
981   else {
982     GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
983   }
984
985   return OPERATOR_FINISHED;
986 }
987
988 /* ************ events *************** */
989
990 void wm_cursor_position_from_ghost(wmWindow *win, int *x, int *y)
991 {
992   float fac = GHOST_GetNativePixelSize(win->ghostwin);
993
994   GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
995   *x *= fac;
996
997   *y = (win->sizey - 1) - *y;
998   *y *= fac;
999 }
1000
1001 void wm_cursor_position_to_ghost(wmWindow *win, int *x, int *y)
1002 {
1003   float fac = GHOST_GetNativePixelSize(win->ghostwin);
1004
1005   *x /= fac;
1006   *y /= fac;
1007   *y = win->sizey - *y - 1;
1008
1009   GHOST_ClientToScreen(win->ghostwin, *x, *y, x, y);
1010 }
1011
1012 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
1013 {
1014   if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
1015     *x = win->eventstate->x;
1016     *y = win->eventstate->y;
1017     return;
1018   }
1019   GHOST_GetCursorPosition(g_system, x, y);
1020   wm_cursor_position_from_ghost(win, x, y);
1021 }
1022
1023 typedef enum {
1024   SHIFT = 's',
1025   CONTROL = 'c',
1026   ALT = 'a',
1027   OS = 'C',
1028 } modifierKeyType;
1029
1030 /* check if specified modifier key type is pressed */
1031 static int query_qual(modifierKeyType qual)
1032 {
1033   GHOST_TModifierKeyMask left, right;
1034   int val = 0;
1035
1036   switch (qual) {
1037     case SHIFT:
1038       left = GHOST_kModifierKeyLeftShift;
1039       right = GHOST_kModifierKeyRightShift;
1040       break;
1041     case CONTROL:
1042       left = GHOST_kModifierKeyLeftControl;
1043       right = GHOST_kModifierKeyRightControl;
1044       break;
1045     case OS:
1046       left = right = GHOST_kModifierKeyOS;
1047       break;
1048     case ALT:
1049     default:
1050       left = GHOST_kModifierKeyLeftAlt;
1051       right = GHOST_kModifierKeyRightAlt;
1052       break;
1053   }
1054
1055   GHOST_GetModifierKeyState(g_system, left, &val);
1056   if (!val) {
1057     GHOST_GetModifierKeyState(g_system, right, &val);
1058   }
1059
1060   return val;
1061 }
1062
1063 static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate)
1064 {
1065   BLI_assert(ELEM(wm->windrawable, NULL, win));
1066
1067   wm->windrawable = win;
1068   if (activate) {
1069     GHOST_ActivateWindowDrawingContext(win->ghostwin);
1070   }
1071   GPU_context_active_set(win->gpuctx);
1072   immActivate();
1073 }
1074
1075 void wm_window_clear_drawable(wmWindowManager *wm)
1076 {
1077   if (wm->windrawable) {
1078     BLF_batch_reset();
1079     gpu_batch_presets_reset();
1080     immDeactivate();
1081     wm->windrawable = NULL;
1082   }
1083 }
1084
1085 void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win)
1086 {
1087   BLI_assert(GPU_framebuffer_active_get() == NULL);
1088
1089   if (win != wm->windrawable && win->ghostwin) {
1090     //      win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
1091     wm_window_clear_drawable(wm);
1092
1093     if (G.debug & G_DEBUG_EVENTS) {
1094       printf("%s: set drawable %d\n", __func__, win->winid);
1095     }
1096
1097     wm_window_set_drawable(wm, win, true);
1098
1099     /* this can change per window */
1100     WM_window_set_dpi(win);
1101   }
1102 }
1103
1104 /* Reset active the current window opengl drawing context. */
1105 void wm_window_reset_drawable(void)
1106 {
1107   BLI_assert(BLI_thread_is_main());
1108   BLI_assert(GPU_framebuffer_active_get() == NULL);
1109   wmWindowManager *wm = G_MAIN->wm.first;
1110
1111   if (wm == NULL) {
1112     return;
1113   }
1114   wmWindow *win = wm->windrawable;
1115
1116   if (win && win->ghostwin) {
1117     wm_window_clear_drawable(wm);
1118     wm_window_set_drawable(wm, win, true);
1119   }
1120 }
1121
1122 /**
1123  * Called by ghost, here we handle events for windows themselves or send to event system.
1124  *
1125  * Mouse coordinate conversion happens here.
1126  */
1127 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
1128 {
1129   bContext *C = C_void_ptr;
1130   wmWindowManager *wm = CTX_wm_manager(C);
1131   GHOST_TEventType type = GHOST_GetEventType(evt);
1132 #if 0
1133   /* We may want to use time from ghost, currently `PIL_check_seconds_timer` is used instead. */
1134   uint64_t time = GHOST_GetEventTime(evt);
1135 #endif
1136
1137   if (type == GHOST_kEventQuitRequest) {
1138     /* Find an active window to display quit dialog in. */
1139     GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt);
1140     wmWindow *win;
1141
1142     if (ghostwin && GHOST_ValidWindow(g_system, ghostwin)) {
1143       win = GHOST_GetWindowUserData(ghostwin);
1144     }
1145     else {
1146       win = wm->winactive;
1147     }
1148
1149     /* Display quit dialog or quit immediately. */
1150     if (win) {
1151       wm_quit_with_optional_confirmation_prompt(C, win);
1152     }
1153     else {
1154       wm_exit_schedule_delayed(C);
1155     }
1156   }
1157   else {
1158     GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt);
1159     GHOST_TEventDataPtr data = GHOST_GetEventData(evt);
1160     wmWindow *win;
1161
1162     /* Ghost now can call this function for life resizes,
1163      * but it should return if WM didn't initialize yet.
1164      * Can happen on file read (especially full size window). */
1165     if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) {
1166       return 1;
1167     }
1168     if (!ghostwin) {
1169       /* XXX - should be checked, why are we getting an event here, and */
1170       /* what is it? */
1171       puts("<!> event has no window");
1172       return 1;
1173     }
1174     else if (!GHOST_ValidWindow(g_system, ghostwin)) {
1175       /* XXX - should be checked, why are we getting an event here, and */
1176       /* what is it? */
1177       puts("<!> event has invalid window");
1178       return 1;
1179     }
1180     else {
1181       win = GHOST_GetWindowUserData(ghostwin);
1182     }
1183
1184     switch (type) {
1185       case GHOST_kEventWindowDeactivate:
1186         wm_event_add_ghostevent(wm, win, type, data);
1187         win->active = 0; /* XXX */
1188
1189         /* clear modifiers for inactive windows */
1190         win->eventstate->alt = 0;
1191         win->eventstate->ctrl = 0;
1192         win->eventstate->shift = 0;
1193         win->eventstate->oskey = 0;
1194         win->eventstate->keymodifier = 0;
1195
1196         break;
1197       case GHOST_kEventWindowActivate: {
1198         GHOST_TEventKeyData kdata;
1199         wmEvent event;
1200         int wx, wy;
1201         const int keymodifier = ((query_qual(SHIFT) ? KM_SHIFT : 0) |
1202                                  (query_qual(CONTROL) ? KM_CTRL : 0) |
1203                                  (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0));
1204
1205         /* Win23/GHOST modifier bug, see T40317 */
1206 #ifndef WIN32
1207 //#  define USE_WIN_ACTIVATE
1208 #endif
1209
1210         /* No context change! C->wm->windrawable is drawable, or for area queues. */
1211         wm->winactive = win;
1212
1213         win->active = 1;
1214         //              window_handle(win, INPUTCHANGE, win->active);
1215
1216         /* bad ghost support for modifier keys... so on activate we set the modifiers again */
1217
1218         /* TODO: This is not correct since a modifier may be held when a window is activated...
1219          * better solve this at ghost level. attempted fix r54450 but it caused bug [#34255]
1220          *
1221          * For now don't send GHOST_kEventKeyDown events, just set the 'eventstate'.
1222          */
1223         kdata.ascii = '\0';
1224         kdata.utf8_buf[0] = '\0';
1225
1226         if (win->eventstate->shift) {
1227           if ((keymodifier & KM_SHIFT) == 0) {
1228             kdata.key = GHOST_kKeyLeftShift;
1229             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
1230           }
1231         }
1232 #ifdef USE_WIN_ACTIVATE
1233         else {
1234           if (keymodifier & KM_SHIFT) {
1235             win->eventstate->shift = KM_MOD_FIRST;
1236           }
1237         }
1238 #endif
1239         if (win->eventstate->ctrl) {
1240           if ((keymodifier & KM_CTRL) == 0) {
1241             kdata.key = GHOST_kKeyLeftControl;
1242             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
1243           }
1244         }
1245 #ifdef USE_WIN_ACTIVATE
1246         else {
1247           if (keymodifier & KM_CTRL) {
1248             win->eventstate->ctrl = KM_MOD_FIRST;
1249           }
1250         }
1251 #endif
1252         if (win->eventstate->alt) {
1253           if ((keymodifier & KM_ALT) == 0) {
1254             kdata.key = GHOST_kKeyLeftAlt;
1255             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
1256           }
1257         }
1258 #ifdef USE_WIN_ACTIVATE
1259         else {
1260           if (keymodifier & KM_ALT) {
1261             win->eventstate->alt = KM_MOD_FIRST;
1262           }
1263         }
1264 #endif
1265         if (win->eventstate->oskey) {
1266           if ((keymodifier & KM_OSKEY) == 0) {
1267             kdata.key = GHOST_kKeyOS;
1268             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
1269           }
1270         }
1271 #ifdef USE_WIN_ACTIVATE
1272         else {
1273           if (keymodifier & KM_OSKEY) {
1274             win->eventstate->oskey = KM_MOD_FIRST;
1275           }
1276         }
1277 #endif
1278
1279 #undef USE_WIN_ACTIVATE
1280
1281         /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
1282         win->eventstate->keymodifier = 0;
1283
1284         /* entering window, update mouse pos. but no event */
1285         wm_get_cursor_position(win, &wx, &wy);
1286
1287         win->eventstate->x = wx;
1288         win->eventstate->y = wy;
1289
1290         win->addmousemove = 1; /* enables highlighted buttons */
1291
1292         wm_window_make_drawable(wm, win);
1293
1294         /* window might be focused by mouse click in configuration of window manager
1295          * when focus is not following mouse
1296          * click could have been done on a button and depending on window manager settings
1297          * click would be passed to blender or not, but in any case button under cursor
1298          * should be activated, so at max next click on button without moving mouse
1299          * would trigger it's handle function
1300          * currently it seems to be common practice to generate new event for, but probably
1301          * we'll need utility function for this? (sergey)
1302          */
1303         wm_event_init_from_window(win, &event);
1304         event.type = MOUSEMOVE;
1305         event.prevx = event.x;
1306         event.prevy = event.y;
1307
1308         wm_event_add(win, &event);
1309
1310         break;
1311       }
1312       case GHOST_kEventWindowClose: {
1313         wm_window_close(C, wm, win);
1314         break;
1315       }
1316       case GHOST_kEventWindowUpdate: {
1317         if (G.debug & G_DEBUG_EVENTS) {
1318           printf("%s: ghost redraw %d\n", __func__, win->winid);
1319         }
1320
1321         wm_window_make_drawable(wm, win);
1322         WM_event_add_notifier(C, NC_WINDOW, NULL);
1323
1324         break;
1325       }
1326       case GHOST_kEventWindowSize:
1327       case GHOST_kEventWindowMove: {
1328         GHOST_TWindowState state;
1329         state = GHOST_GetWindowState(win->ghostwin);
1330         win->windowstate = state;
1331
1332         WM_window_set_dpi(win);
1333
1334         /* win32: gives undefined window size when minimized */
1335         if (state != GHOST_kWindowStateMinimized) {
1336           GHOST_RectangleHandle client_rect;
1337           int l, t, r, b, scr_w, scr_h;
1338           int sizex, sizey, posx, posy;
1339
1340           client_rect = GHOST_GetClientBounds(win->ghostwin);
1341           GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
1342
1343           GHOST_DisposeRectangle(client_rect);
1344
1345           wm_get_desktopsize(&scr_w, &scr_h);
1346           sizex = r - l;
1347           sizey = b - t;
1348           posx = l;
1349           posy = scr_h - t - win->sizey;
1350
1351           /*
1352            * Ghost sometimes send size or move events when the window hasn't changed.
1353            * One case of this is using compiz on linux. To alleviate the problem
1354            * we ignore all such event here.
1355            *
1356            * It might be good to eventually do that at Ghost level, but that is for
1357            * another time.
1358            */
1359           if (win->sizex != sizex || win->sizey != sizey || win->posx != posx ||
1360               win->posy != posy) {
1361             const bScreen *screen = WM_window_get_active_screen(win);
1362
1363             win->sizex = sizex;
1364             win->sizey = sizey;
1365             win->posx = posx;
1366             win->posy = posy;
1367
1368             /* debug prints */
1369             if (G.debug & G_DEBUG_EVENTS) {
1370               const char *state_str;
1371               state = GHOST_GetWindowState(win->ghostwin);
1372
1373               if (state == GHOST_kWindowStateNormal) {
1374                 state_str = "normal";
1375               }
1376               else if (state == GHOST_kWindowStateMinimized) {
1377                 state_str = "minimized";
1378               }
1379               else if (state == GHOST_kWindowStateMaximized) {
1380                 state_str = "maximized";
1381               }
1382               else if (state == GHOST_kWindowStateFullScreen) {
1383                 state_str = "fullscreen";
1384               }
1385               else {
1386                 state_str = "<unknown>";
1387               }
1388
1389               printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
1390
1391               if (type != GHOST_kEventWindowSize) {
1392                 printf("win move event pos %d %d size %d %d\n",
1393                        win->posx,
1394                        win->posy,
1395                        win->sizex,
1396                        win->sizey);
1397               }
1398             }
1399
1400             wm_window_make_drawable(wm, win);
1401             BKE_icon_changed(screen->id.icon_id);
1402             WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1403             WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1404
1405 #if defined(__APPLE__) || defined(WIN32)
1406             /* OSX and Win32 don't return to the mainloop while resize */
1407             wm_window_timer(C);
1408             wm_event_do_handlers(C);
1409             wm_event_do_notifiers(C);
1410             wm_draw_update(C);
1411 #endif
1412           }
1413         }
1414         break;
1415       }
1416
1417       case GHOST_kEventWindowDPIHintChanged: {
1418         WM_window_set_dpi(win);
1419         /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
1420         BLF_cache_clear();
1421
1422         WM_main_add_notifier(NC_WINDOW, NULL);             /* full redraw */
1423         WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); /* refresh region sizes */
1424         break;
1425       }
1426
1427       case GHOST_kEventOpenMainFile: {
1428         PointerRNA props_ptr;
1429         const char *path = GHOST_GetEventData(evt);
1430
1431         if (path) {
1432           wmOperatorType *ot = WM_operatortype_find("WM_OT_open_mainfile", false);
1433           /* operator needs a valid window in context, ensures
1434            * it is correctly set */
1435           CTX_wm_window_set(C, win);
1436
1437           WM_operator_properties_create_ptr(&props_ptr, ot);
1438           RNA_string_set(&props_ptr, "filepath", path);
1439           WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr);
1440           WM_operator_properties_free(&props_ptr);
1441
1442           CTX_wm_window_set(C, NULL);
1443         }
1444         break;
1445       }
1446       case GHOST_kEventDraggingDropDone: {
1447         wmEvent event;
1448         GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
1449         int wx, wy;
1450
1451         /* entering window, update mouse pos */
1452         wm_get_cursor_position(win, &wx, &wy);
1453         win->eventstate->x = wx;
1454         win->eventstate->y = wy;
1455
1456         wm_event_init_from_window(win, &event); /* copy last state, like mouse coords */
1457
1458         /* activate region */
1459         event.type = MOUSEMOVE;
1460         event.prevx = event.x;
1461         event.prevy = event.y;
1462
1463         /* No context change! C->wm->windrawable is drawable, or for area queues. */
1464         wm->winactive = win;
1465
1466         win->active = 1;
1467
1468         wm_event_add(win, &event);
1469
1470         /* make blender drop event with custom data pointing to wm drags */
1471         event.type = EVT_DROP;
1472         event.val = KM_RELEASE;
1473         event.custom = EVT_DATA_DRAGDROP;
1474         event.customdata = &wm->drags;
1475         event.customdatafree = 1;
1476
1477         wm_event_add(win, &event);
1478
1479         /* printf("Drop detected\n"); */
1480
1481         /* add drag data to wm for paths: */
1482
1483         if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
1484           GHOST_TStringArray *stra = ddd->data;
1485           int a, icon;
1486
1487           for (a = 0; a < stra->count; a++) {
1488             printf("drop file %s\n", stra->strings[a]);
1489             /* try to get icon type from extension */
1490             icon = ED_file_extension_icon((char *)stra->strings[a]);
1491
1492             WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0, WM_DRAG_NOP);
1493             /* void poin should point to string, it makes a copy */
1494             break; /* only one drop element supported now */
1495           }
1496         }
1497
1498         break;
1499       }
1500       case GHOST_kEventNativeResolutionChange: {
1501         // only update if the actual pixel size changes
1502         float prev_pixelsize = U.pixelsize;
1503         WM_window_set_dpi(win);
1504
1505         if (U.pixelsize != prev_pixelsize) {
1506           BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id);
1507
1508           // close all popups since they are positioned with the pixel
1509           // size baked in and it's difficult to correct them
1510           CTX_wm_window_set(C, win);
1511           UI_popup_handlers_remove_all(C, &win->modalhandlers);
1512           CTX_wm_window_set(C, NULL);
1513
1514           wm_window_make_drawable(wm, win);
1515
1516           WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1517           WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1518         }
1519
1520         break;
1521       }
1522       case GHOST_kEventTrackpad: {
1523         GHOST_TEventTrackpadData *pd = data;
1524
1525         wm_cursor_position_from_ghost(win, &pd->x, &pd->y);
1526         wm_event_add_ghostevent(wm, win, type, data);
1527         break;
1528       }
1529       case GHOST_kEventCursorMove: {
1530         GHOST_TEventCursorData *cd = data;
1531
1532         wm_cursor_position_from_ghost(win, &cd->x, &cd->y);
1533         wm_event_add_ghostevent(wm, win, type, data);
1534         break;
1535       }
1536       default:
1537         wm_event_add_ghostevent(wm, win, type, data);
1538         break;
1539     }
1540   }
1541   return 1;
1542 }
1543
1544 /**
1545  * This timer system only gives maximum 1 timer event per redraw cycle,
1546  * to prevent queues to get overloaded.
1547  * Timer handlers should check for delta to decide if they just update, or follow real time.
1548  * Timer handlers can also set duration to match frames passed
1549  */
1550 static int wm_window_timer(const bContext *C)
1551 {
1552   wmWindowManager *wm = CTX_wm_manager(C);
1553   wmTimer *wt, *wtnext;
1554   wmWindow *win;
1555   double time = PIL_check_seconds_timer();
1556   int retval = 0;
1557
1558   for (wt = wm->timers.first; wt; wt = wtnext) {
1559     wtnext = wt->next; /* in case timer gets removed */
1560     win = wt->win;
1561
1562     if (wt->sleep == 0) {
1563       if (time > wt->ntime) {
1564         wt->delta = time - wt->ltime;
1565         wt->duration += wt->delta;
1566         wt->ltime = time;
1567         wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep);
1568
1569         if (wt->event_type == TIMERJOBS) {
1570           wm_jobs_timer(C, wm, wt);
1571         }
1572         else if (wt->event_type == TIMERAUTOSAVE) {
1573           wm_autosave_timer(C, wm, wt);
1574         }
1575         else if (wt->event_type == TIMERNOTIFIER) {
1576           WM_main_add_notifier(POINTER_AS_UINT(wt->customdata), NULL);
1577         }
1578         else if (win) {
1579           wmEvent event;
1580           wm_event_init_from_window(win, &event);
1581
1582           event.type = wt->event_type;
1583           event.val = KM_NOTHING;
1584           event.keymodifier = 0;
1585           event.custom = EVT_DATA_TIMER;
1586           event.customdata = wt;
1587           wm_event_add(win, &event);
1588
1589           retval = 1;
1590         }
1591       }
1592     }
1593   }
1594   return retval;
1595 }
1596
1597 void wm_window_process_events(const bContext *C)
1598 {
1599   int hasevent;
1600
1601   BLI_assert(BLI_thread_is_main());
1602
1603   hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
1604
1605   if (hasevent) {
1606     GHOST_DispatchEvents(g_system);
1607   }
1608   hasevent |= wm_window_timer(C);
1609
1610   /* no event, we sleep 5 milliseconds */
1611   if (hasevent == 0) {
1612     PIL_sleep_ms(5);
1613   }
1614 }
1615
1616 /* -------------------------------------------------------------------- */
1617 /** \name Ghost Init/Exit
1618  * \{ */
1619
1620 /**
1621  * \note #bContext can be null in background mode because we don't
1622  * need to event handling.
1623  */
1624 void wm_ghost_init(bContext *C)
1625 {
1626   if (!g_system) {
1627     GHOST_EventConsumerHandle consumer;
1628
1629     if (C != NULL) {
1630       consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
1631     }
1632
1633     g_system = GHOST_CreateSystem();
1634
1635     if (C != NULL) {
1636       GHOST_AddEventConsumer(g_system, consumer);
1637     }
1638
1639     if (wm_init_state.native_pixels) {
1640       GHOST_UseNativePixels();
1641     }
1642
1643     GHOST_UseWindowFocus(wm_init_state.window_focus);
1644
1645     WM_init_tablet_api();
1646   }
1647 }
1648
1649 void wm_ghost_exit(void)
1650 {
1651   if (g_system) {
1652     GHOST_DisposeSystem(g_system);
1653   }
1654   g_system = NULL;
1655 }
1656
1657 /** \} */
1658
1659 /* -------------------------------------------------------------------- */
1660 /** \name Event Timer
1661  * \{ */
1662
1663 /* to (de)activate running timers temporary */
1664 void WM_event_timer_sleep(wmWindowManager *wm,
1665                           wmWindow *UNUSED(win),
1666                           wmTimer *timer,
1667                           bool do_sleep)
1668 {
1669   wmTimer *wt;
1670
1671   for (wt = wm->timers.first; wt; wt = wt->next) {
1672     if (wt == timer) {
1673       break;
1674     }
1675   }
1676
1677   if (wt) {
1678     wt->sleep = do_sleep;
1679   }
1680 }
1681
1682 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
1683 {
1684   wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1685
1686   wt->event_type = event_type;
1687   wt->ltime = PIL_check_seconds_timer();
1688   wt->ntime = wt->ltime + timestep;
1689   wt->stime = wt->ltime;
1690   wt->timestep = timestep;
1691   wt->win = win;
1692
1693   BLI_addtail(&wm->timers, wt);
1694
1695   return wt;
1696 }
1697
1698 wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm,
1699                                      wmWindow *win,
1700                                      unsigned int type,
1701                                      double timestep)
1702 {
1703   wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1704
1705   wt->event_type = TIMERNOTIFIER;
1706   wt->ltime = PIL_check_seconds_timer();
1707   wt->ntime = wt->ltime + timestep;
1708   wt->stime = wt->ltime;
1709   wt->timestep = timestep;
1710   wt->win = win;
1711   wt->customdata = POINTER_FROM_UINT(type);
1712   wt->flags |= WM_TIMER_NO_FREE_CUSTOM_DATA;
1713
1714   BLI_addtail(&wm->timers, wt);
1715
1716   return wt;
1717 }
1718
1719 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
1720 {
1721   wmTimer *wt;
1722
1723   /* extra security check */
1724   for (wt = wm->timers.first; wt; wt = wt->next) {
1725     if (wt == timer) {
1726       break;
1727     }
1728   }
1729   if (wt) {
1730     wmWindow *win;
1731
1732     if (wm->reports.reporttimer == wt) {
1733       wm->reports.reporttimer = NULL;
1734     }
1735
1736     BLI_remlink(&wm->timers, wt);
1737     if (wt->customdata != NULL && (wt->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
1738       MEM_freeN(wt->customdata);
1739     }
1740     MEM_freeN(wt);
1741
1742     /* there might be events in queue with this timer as customdata */
1743     for (win = wm->windows.first; win; win = win->next) {
1744       wmEvent *event;
1745       for (event = win->queue.first; event; event = event->next) {
1746         if (event->customdata == wt) {
1747           event->customdata = NULL;
1748           event->type = EVENT_NONE; /* timer users customdata, dont want NULL == NULL */
1749         }
1750       }
1751     }
1752   }
1753 }
1754
1755 void WM_event_remove_timer_notifier(wmWindowManager *wm, wmWindow *win, wmTimer *timer)
1756 {
1757   timer->customdata = NULL;
1758   WM_event_remove_timer(wm, win, timer);
1759 }
1760
1761 /** \} */
1762
1763 /* -------------------------------------------------------------------- */
1764 /** \name Clipboard
1765  * \{ */
1766
1767 static char *wm_clipboard_text_get_ex(bool selection, int *r_len, bool firstline)
1768 {
1769   char *p, *p2, *buf, *newbuf;
1770
1771   if (G.background) {
1772     *r_len = 0;
1773     return NULL;
1774   }
1775
1776   buf = (char *)GHOST_getClipboard(selection);
1777   if (!buf) {
1778     *r_len = 0;
1779     return NULL;
1780   }
1781
1782   /* always convert from \r\n to \n */
1783   p2 = newbuf = MEM_mallocN(strlen(buf) + 1, __func__);
1784
1785   if (firstline) {
1786     /* will return an over-alloc'ed value in the case there are newlines */
1787     for (p = buf; *p; p++) {
1788       if ((*p != '\n') && (*p != '\r')) {
1789         *(p2++) = *p;
1790       }
1791       else {
1792         break;
1793       }
1794     }
1795   }
1796   else {
1797     for (p = buf; *p; p++) {
1798       if (*p != '\r') {
1799         *(p2++) = *p;
1800       }
1801     }
1802   }
1803
1804   *p2 = '\0';
1805
1806   free(buf); /* ghost uses regular malloc */
1807
1808   *r_len = (p2 - newbuf);
1809
1810   return newbuf;
1811 }
1812
1813 /**
1814  * Return text from the clipboard.
1815  *
1816  * \note Caller needs to check for valid utf8 if this is a requirement.
1817  */
1818 char *WM_clipboard_text_get(bool selection, int *r_len)
1819 {
1820   return wm_clipboard_text_get_ex(selection, r_len, false);
1821 }
1822
1823 /**
1824  * Convenience function for pasting to areas of Blender which don't support newlines.
1825  */
1826 char *WM_clipboard_text_get_firstline(bool selection, int *r_len)
1827 {
1828   return wm_clipboard_text_get_ex(selection, r_len, true);
1829 }
1830
1831 void WM_clipboard_text_set(const char *buf, bool selection)
1832 {
1833   if (!G.background) {
1834 #ifdef _WIN32
1835     /* do conversion from \n to \r\n on Windows */
1836     const char *p;
1837     char *p2, *newbuf;
1838     int newlen = 0;
1839
1840     for (p = buf; *p; p++) {
1841       if (*p == '\n') {
1842         newlen += 2;
1843       }
1844       else {
1845         newlen++;
1846       }
1847     }
1848
1849     newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
1850
1851     for (p = buf, p2 = newbuf; *p; p++, p2++) {
1852       if (*p == '\n') {
1853         *(p2++) = '\r';
1854         *p2 = '\n';
1855       }
1856       else {
1857         *p2 = *p;
1858       }
1859     }
1860     *p2 = '\0';
1861
1862     GHOST_putClipboard((GHOST_TInt8 *)newbuf, selection);
1863     MEM_freeN(newbuf);
1864 #else
1865     GHOST_putClipboard((GHOST_TInt8 *)buf, selection);
1866 #endif
1867   }
1868 }
1869
1870 /** \} */
1871
1872 /* -------------------------------------------------------------------- */
1873 /** \name Progress Bar
1874  * \{ */
1875
1876 void WM_progress_set(wmWindow *win, float progress)
1877 {
1878   GHOST_SetProgressBar(win->ghostwin, progress);
1879 }
1880
1881 void WM_progress_clear(wmWindow *win)
1882 {
1883   GHOST_EndProgressBar(win->ghostwin);
1884 }
1885
1886 /** \} */
1887
1888 /* -------------------------------------------------------------------- */
1889 /** \name Window Position/Size (internal)
1890  * \{ */
1891
1892 void wm_window_get_position(wmWindow *win, int *r_pos_x, int *r_pos_y)
1893 {
1894   *r_pos_x = win->posx;
1895   *r_pos_y = win->posy;
1896 }
1897
1898 void wm_window_set_size(wmWindow *win, int width, int height)
1899 {
1900   GHOST_SetClientSize(win->ghostwin, width, height);
1901 }
1902
1903 /** \} */
1904
1905 /* -------------------------------------------------------------------- */
1906 /** \name Window Depth (Raise/Lower)
1907  * \{ */
1908
1909 void wm_window_lower(wmWindow *win)
1910 {
1911   GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
1912 }
1913
1914 void wm_window_raise(wmWindow *win)
1915 {
1916   /* Restore window if minimized */
1917   if (GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateMinimized) {
1918     GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateNormal);
1919   }
1920   GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
1921 }
1922
1923 /** \} */
1924
1925 /* -------------------------------------------------------------------- */
1926 /** \name Window Buffers
1927  * \{ */
1928
1929 void wm_window_swap_buffers(wmWindow *win)
1930 {
1931   GHOST_SwapWindowBuffers(win->ghostwin);
1932 }
1933
1934 void wm_window_set_swap_interval(wmWindow *win, int interval)
1935 {
1936   GHOST_SetSwapInterval(win->ghostwin, interval);
1937 }
1938
1939 bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut)
1940 {
1941   return GHOST_GetSwapInterval(win->ghostwin, intervalOut);
1942 }
1943
1944 /** \} */
1945
1946 /* -------------------------------------------------------------------- */
1947 /** \name Window Screen Shot Utility
1948  *
1949  * Include here since it can involve low level buffer switching.
1950  *
1951  * \{ */
1952
1953 uint *WM_window_pixels_read(wmWindowManager *wm, wmWindow *win, int r_size[2])
1954 {
1955   bool setup_context = wm->windrawable != win;
1956
1957   if (setup_context) {
1958     GHOST_ActivateWindowDrawingContext(win->ghostwin);
1959     GPU_context_active_set(win->gpuctx);
1960   }
1961
1962   r_size[0] = WM_window_pixels_x(win);
1963   r_size[1] = WM_window_pixels_y(win);
1964   const uint rect_len = r_size[0] * r_size[1];
1965   uint *rect = MEM_mallocN(sizeof(*rect) * rect_len, __func__);
1966
1967   glReadBuffer(GL_FRONT);
1968   glReadPixels(0, 0, r_size[0], r_size[1], GL_RGBA, GL_UNSIGNED_BYTE, rect);
1969   glFinish();
1970   glReadBuffer(GL_BACK);
1971
1972   if (setup_context) {
1973     if (wm->windrawable) {
1974       GHOST_ActivateWindowDrawingContext(wm->windrawable->ghostwin);
1975       GPU_context_active_set(wm->windrawable->gpuctx);
1976     }
1977   }
1978
1979   /* Clear alpha, it is not set to a meaningful value in OpenGL. */
1980   uchar *cp = (uchar *)rect;
1981   uint i;
1982   for (i = 0, cp += 3; i < rect_len; i++, cp += 4) {
1983     *cp = 0xff;
1984   }
1985   return (uint *)rect;
1986 }
1987
1988 /** \} */
1989
1990 /* -------------------------------------------------------------------- */
1991 /** \name Initial Window State API
1992  * \{ */
1993
1994 /* called whem no ghost system was initialized */
1995 void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
1996 {
1997   wm_init_state.start_x = stax; /* left hand pos */
1998   wm_init_state.start_y = stay; /* bottom pos */
1999   wm_init_state.size_x = sizx < 640 ? 640 : sizx;
2000   wm_init_state.size_y = sizy < 480 ? 480 : sizy;
2001   wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
2002 }
2003
2004 /* for borderless and border windows set from command-line */
2005 void WM_init_state_fullscreen_set(void)
2006 {
2007   wm_init_state.windowstate = GHOST_kWindowStateFullScreen;
2008   wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2009 }
2010
2011 void WM_init_state_normal_set(void)
2012 {
2013   wm_init_state.windowstate = GHOST_kWindowStateNormal;
2014   wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2015 }
2016
2017 void WM_init_state_maximized_set(void)
2018 {
2019   wm_init_state.windowstate = GHOST_kWindowStateMaximized;
2020   wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2021 }
2022
2023 void WM_init_window_focus_set(bool do_it)
2024 {
2025   wm_init_state.window_focus = do_it;
2026 }
2027
2028 void WM_init_native_pixels(bool do_it)
2029 {
2030   wm_init_state.native_pixels = do_it;
2031 }
2032
2033 /** \} */
2034
2035 /* -------------------------------------------------------------------- */
2036 /** \name Cursor API
2037  * \{ */
2038
2039 void WM_init_tablet_api(void)
2040 {
2041   if (g_system) {
2042     switch (U.tablet_api) {
2043       case USER_TABLET_NATIVE:
2044         GHOST_SetTabletAPI(g_system, GHOST_kTabletNative);
2045         break;
2046       case USER_TABLET_WINTAB:
2047         GHOST_SetTabletAPI(g_system, GHOST_kTabletWintab);
2048         break;
2049       case USER_TABLET_AUTOMATIC:
2050       default:
2051         GHOST_SetTabletAPI(g_system, GHOST_kTabletAutomatic);
2052         break;
2053     }
2054   }
2055 }
2056
2057 /* This function requires access to the GHOST_SystemHandle (g_system) */
2058 void WM_cursor_warp(wmWindow *win, int x, int y)
2059 {
2060   if (win && win->ghostwin) {
2061     int oldx = x, oldy = y;
2062
2063     wm_cursor_position_to_ghost(win, &x, &y);
2064     GHOST_SetCursorPosition(g_system, x, y);
2065
2066     win->eventstate->prevx = oldx;
2067     win->eventstate->prevy = oldy;
2068
2069     win->eventstate->x = oldx;
2070     win->eventstate->y = oldy;
2071   }
2072 }
2073
2074 /**
2075  * Set x, y to values we can actually position the cursor to.
2076  */
2077 void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y)
2078 {
2079   float f = GHOST_GetNativePixelSize(win->ghostwin);
2080   if (f != 1.0f) {
2081     *x = (int)(*x / f) * f;
2082     *y = (int)(*y / f) * f;
2083   }
2084 }
2085
2086 /**
2087  * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event
2088  */
2089 float WM_cursor_pressure(const struct wmWindow *win)
2090 {
2091   const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin);
2092   /* if there's tablet data from an active tablet device then add it */
2093   if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2094     return wm_pressure_curve(td->Pressure);
2095   }
2096   else {
2097     return -1.0f;
2098   }
2099 }
2100
2101 /** \} */
2102
2103 /* -------------------------------------------------------------------- */
2104 /** \name Window Size (public)
2105  * \{ */
2106
2107 /**
2108  * Support for native pixel size
2109  *
2110  * \note macOS retina opens window in size X, but it has up to 2 x more pixels.
2111  */
2112 int WM_window_pixels_x(const wmWindow *win)
2113 {
2114   float f = GHOST_GetNativePixelSize(win->ghostwin);
2115
2116   return (int)(f * (float)win->sizex);
2117 }
2118 int WM_window_pixels_y(const wmWindow *win)
2119 {
2120   float f = GHOST_GetNativePixelSize(win->ghostwin);
2121
2122   return (int)(f * (float)win->sizey);
2123 }
2124
2125 /**
2126  * Get boundaries usable by all window contents, including global areas.
2127  */
2128 void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
2129 {
2130   BLI_rcti_init(r_rect, 0, WM_window_pixels_x(win), 0, WM_window_pixels_y(win));
2131 }
2132 /**
2133  * Get boundaries usable by screen-layouts, excluding global areas.
2134  * \note Depends on U.dpi_fac. Should that be outdated, call #WM_window_set_dpi first.
2135  */
2136 void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
2137 {
2138   rcti window_rect, screen_rect;
2139
2140   WM_window_rect_calc(win, &window_rect);
2141   screen_rect = window_rect;
2142
2143   /* Subtract global areas from screen rectangle. */
2144   for (ScrArea *global_area = win->global_areas.areabase.first; global_area;
2145        global_area = global_area->next) {
2146     int height = ED_area_global_size_y(global_area) - 1;
2147
2148     if (global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) {
2149       continue;
2150     }
2151
2152     switch (global_area->global->align) {
2153       case GLOBAL_AREA_ALIGN_TOP:
2154         if ((screen_rect.ymax - height) > window_rect.ymin) {
2155           height += U.pixelsize;
2156         }
2157         if (screen_rect.ymax < (window_rect.ymax - 1)) {
2158           height += U.pixelsize;
2159         }
2160         screen_rect.ymax -= height;
2161         break;
2162       case GLOBAL_AREA_ALIGN_BOTTOM:
2163         if (screen_rect.ymin > window_rect.ymin) {
2164           height += U.pixelsize;
2165         }
2166         if ((screen_rect.ymin + height) < (window_rect.ymax - 1)) {
2167           height += U.pixelsize;
2168         }
2169         screen_rect.ymin += height;
2170         break;
2171       default:
2172         BLI_assert(0);
2173         break;
2174     }
2175   }
2176
2177   BLI_assert(screen_rect.xmin < screen_rect.xmax);
2178   BLI_assert(screen_rect.ymin < screen_rect.ymax);
2179   *r_rect = screen_rect;
2180 }
2181
2182 bool WM_window_is_fullscreen(wmWindow *win)
2183 {
2184   return win->windowstate == GHOST_kWindowStateFullScreen;
2185 }
2186
2187 /** \} */
2188
2189 /* -------------------------------------------------------------------- */
2190 /** \name Window Screen/Scene/WorkSpaceViewLayer API
2191  * \{ */
2192
2193 /**
2194  * Some editor data may need to be synced with scene data (3D View camera and layers).
2195  * This function ensures data is synced for editors
2196  * in visible workspaces and their visible layouts.
2197  */
2198 void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
2199 {
2200   for (wmWindow *win = win_lb->first; win; win = win->next) {
2201     if (WM_window_get_active_scene(win) == scene) {
2202       ED_workspace_scene_data_sync(win->workspace_hook, scene);
2203     }
2204   }
2205 }
2206
2207 Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
2208 {
2209   for (wmWindow *win = wm->windows.first; win; win = win->next) {
2210     if (WM_window_get_active_screen(win) == screen) {
2211       return WM_window_get_active_scene(win);
2212     }
2213   }
2214
2215   return NULL;
2216 }
2217
2218 WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
2219 {
2220   for (wmWindow *win = wm->windows.first; win; win = win->next) {
2221     if (WM_window_get_active_screen(win) == screen) {
2222       return WM_window_get_active_workspace(win);
2223     }
2224   }
2225   return NULL;
2226 }
2227
2228 Scene *WM_window_get_active_scene(const wmWindow *win)
2229 {
2230   return win->scene;
2231 }
2232
2233 /**
2234  * \warning Only call outside of area/region loops
2235  */
2236 void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene)
2237 {
2238   wmWindowManager *wm = CTX_wm_manager(C);
2239   wmWindow *win_parent = (win->parent) ? win->parent : win;
2240   bool changed = false;
2241
2242   /* Set scene in parent and its child windows. */
2243   if (win_parent->scene != scene) {
2244     ED_screen_scene_change(C, win_parent, scene);
2245     changed = true;
2246   }
2247
2248   for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
2249     if (win_child->parent == win_parent && win_child->scene != scene) {
2250       ED_screen_scene_change(C, win_child, scene);
2251       changed = true;
2252     }
2253   }
2254
2255   if (changed) {
2256     /* Update depsgraph and renderers for scene change. */
2257     ViewLayer *view_layer = WM_window_get_active_view_layer(win_parent);
2258     ED_scene_change_update(bmain, scene, view_layer);
2259
2260     /* Complete redraw. */
2261     WM_event_add_notifier(C, NC_WINDOW, NULL);
2262   }
2263 }
2264
2265 ViewLayer *WM_window_get_active_view_layer(const wmWindow *win)
2266 {
2267   Scene *scene = WM_window_get_active_scene(win);
2268   if (scene == NULL) {
2269     return NULL;
2270   }
2271
2272   ViewLayer *view_layer = BKE_view_layer_find(scene, win->view_layer_name);
2273   if (view_layer) {
2274     return view_layer;
2275   }
2276
2277   view_layer = BKE_view_layer_default_view(scene);
2278   if (view_layer) {
2279     WM_window_set_active_view_layer((wmWindow *)win, view_layer);
2280   }
2281
2282   return view_layer;
2283 }
2284
2285 void WM_window_set_active_view_layer(wmWindow *win, ViewLayer *view_layer)
2286 {
2287   BLI_assert(BKE_view_layer_find(WM_window_get_active_scene(win), view_layer->name) != NULL);
2288   Main *bmain = G_MAIN;
2289
2290   wmWindowManager *wm = bmain->wm.first;
2291   wmWindow *win_parent = (win->parent) ? win->parent : win;
2292
2293   /* Set view layer in parent and child windows. */
2294   for (wmWindow *win_iter = wm->windows.first; win_iter; win_iter = win_iter->next) {
2295     if ((win_iter == win_parent) || (win_iter->parent == win_parent)) {
2296       STRNCPY(win_iter->view_layer_name, view_layer->name);
2297       bScreen *screen = BKE_workspace_active_screen_get(win_iter->workspace_hook);
2298       ED_render_view_layer_changed(bmain, screen);
2299     }
2300   }
2301 }
2302
2303 void WM_window_ensure_active_view_layer(wmWindow *win)
2304 {
2305   /* Update layer name is correct after scene changes, load without UI, etc. */
2306   Scene *scene = WM_window_get_active_scene(win);
2307
2308   if (scene && BKE_view_layer_find(scene, win->view_layer_name) == NULL) {
2309     ViewLayer *view_layer = BKE_view_layer_default_view(scene);
2310     STRNCPY(win->view_layer_name, view_layer->name);
2311   }
2312 }
2313
2314 WorkSpace *WM_window_get_active_workspace(const wmWindow *win)
2315 {
2316   return BKE_workspace_active_get(win->workspace_hook);
2317 }
2318
2319 void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace)
2320 {
2321   wmWindowManager *wm = CTX_wm_manager(C);
2322   wmWindow *win_parent = (win->parent) ? win->parent : win;
2323
2324   ED_workspace_change(workspace, C, wm, win);
2325
2326   for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
2327     if (win_child->parent == win_parent) {
2328       bScreen *screen = WM_window_get_active_screen(win_child);
2329       /* Don't change temporary screens, they only serve a single purpose. */
2330       if (screen->temp) {
2331         continue;
2332       }
2333       ED_workspace_change(workspace, C, wm, win_child);
2334     }
2335   }
2336 }
2337
2338 WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win)
2339 {
2340   const WorkSpace *workspace = WM_window_get_active_workspace(win);
2341   return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook) : NULL);
2342 }
2343 void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout)
2344 {
2345   BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout);
2346 }
2347
2348 /**
2349  * Get the active screen of the active workspace in \a win.
2350  */
2351 bScreen *WM_window_get_active_screen(const wmWindow *win)
2352 {
2353   const WorkSpace *workspace = WM_window_get_active_workspace(win);
2354   /* May be NULL in rare cases like closing Blender */
2355   return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL);
2356 }
2357 void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
2358 {
2359   BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen);
2360 }
2361
2362 bool WM_window_is_temp_screen(const wmWindow *win)
2363 {
2364   const bScreen *screen = WM_window_get_active_screen(win);
2365   return (screen && screen->temp != 0);
2366 }
2367
2368 /** \} */
2369
2370 /* -------------------------------------------------------------------- */
2371 /** \name Window IME API
2372  * \{ */
2373
2374 #ifdef WITH_INPUT_IME
2375 /**
2376  * \note Keep in mind #wm_window_IME_begin is also used to reposition the IME window.
2377  */
2378 void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete)
2379 {
2380   BLI_assert(win);
2381
2382   GHOST_BeginIME(win->ghostwin, x, win->sizey - y, w, h, complete);
2383 }
2384
2385 void wm_window_IME_end(wmWindow *win)
2386 {
2387   BLI_assert(win && win->ime_data);
2388
2389   GHOST_EndIME(win->ghostwin);
2390   win->ime_data = NULL;
2391 }
2392 #endif /* WITH_INPUT_IME */
2393
2394 /** \} */
2395
2396 /* -------------------------------------------------------------------- */
2397 /** \name Direct OpenGL Context Management
2398  * \{ */
2399
2400 void *WM_opengl_context_create(void)
2401 {
2402   /* On Windows there is a problem creating contexts that share lists
2403    * from one context that is current in another thread.
2404    * So we should call this function only on the main thread.
2405    */
2406   BLI_assert(BLI_thread_is_main());
2407   BLI_assert(GPU_framebuffer_active_get() == NULL);
2408   return GHOST_CreateOpenGLContext(g_system);
2409 }
2410
2411 void WM_opengl_context_dispose(void *context)
2412 {
2413   BLI_assert(GPU_framebuffer_active_get() == NULL);
2414   GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context);
2415 }
2416
2417 void WM_opengl_context_activate(void *context)
2418 {
2419   BLI_assert(GPU_framebuffer_active_get() == NULL);
2420   GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context);
2421 }
2422
2423 void WM_opengl_context_release(void *context)
2424 {
2425   BLI_assert(GPU_framebuffer_active_get() == NULL);
2426   GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
2427 }
2428
2429 /** \} */