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