89696082e7a7069ae797fca586a57e3dc5f02b07
[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 (MAC OS X)
628      * and to give hint of unsaved changes for a user warning mechanism
629      * in case of OS 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, but it should return if WM didn't initialize yet.
1275      * Can happen on file read (especially full size window)  */
1276     if ((wm->initialized & WM_WINDOW_IS_INITIALIZED) == 0) {
1277       return 1;
1278     }
1279     if (!ghostwin) {
1280       /* XXX - should be checked, why are we getting an event here, and */
1281       /* what is it? */
1282       puts("<!> event has no window");
1283       return 1;
1284     }
1285     else if (!GHOST_ValidWindow(g_system, ghostwin)) {
1286       /* XXX - should be checked, why are we getting an event here, and */
1287       /* what is it? */
1288       puts("<!> event has invalid window");
1289       return 1;
1290     }
1291     else {
1292       win = GHOST_GetWindowUserData(ghostwin);
1293     }
1294
1295     switch (type) {
1296       case GHOST_kEventWindowDeactivate:
1297         wm_event_add_ghostevent(wm, win, type, time, data);
1298         win->active = 0; /* XXX */
1299
1300         /* clear modifiers for inactive windows */
1301         win->eventstate->alt = 0;
1302         win->eventstate->ctrl = 0;
1303         win->eventstate->shift = 0;
1304         win->eventstate->oskey = 0;
1305         win->eventstate->keymodifier = 0;
1306
1307         break;
1308       case GHOST_kEventWindowActivate: {
1309         GHOST_TEventKeyData kdata;
1310         wmEvent event;
1311         int wx, wy;
1312         const int keymodifier = ((query_qual(SHIFT) ? KM_SHIFT : 0) |
1313                                  (query_qual(CONTROL) ? KM_CTRL : 0) |
1314                                  (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0));
1315
1316         /* Win23/GHOST modifier bug, see T40317 */
1317 #ifndef WIN32
1318 //#  define USE_WIN_ACTIVATE
1319 #endif
1320
1321         wm->winactive =
1322             win; /* no context change! c->wm->windrawable is drawable, or for area queues */
1323
1324         win->active = 1;
1325         //              window_handle(win, INPUTCHANGE, win->active);
1326
1327         /* bad ghost support for modifier keys... so on activate we set the modifiers again */
1328
1329         /* TODO: This is not correct since a modifier may be held when a window is activated...
1330          * better solve this at ghost level. attempted fix r54450 but it caused bug [#34255]
1331          *
1332          * For now don't send GHOST_kEventKeyDown events, just set the 'eventstate'.
1333          */
1334         kdata.ascii = '\0';
1335         kdata.utf8_buf[0] = '\0';
1336
1337         if (win->eventstate->shift) {
1338           if ((keymodifier & KM_SHIFT) == 0) {
1339             kdata.key = GHOST_kKeyLeftShift;
1340             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
1341           }
1342         }
1343 #ifdef USE_WIN_ACTIVATE
1344         else {
1345           if (keymodifier & KM_SHIFT) {
1346             win->eventstate->shift = KM_MOD_FIRST;
1347           }
1348         }
1349 #endif
1350         if (win->eventstate->ctrl) {
1351           if ((keymodifier & KM_CTRL) == 0) {
1352             kdata.key = GHOST_kKeyLeftControl;
1353             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
1354           }
1355         }
1356 #ifdef USE_WIN_ACTIVATE
1357         else {
1358           if (keymodifier & KM_CTRL) {
1359             win->eventstate->ctrl = KM_MOD_FIRST;
1360           }
1361         }
1362 #endif
1363         if (win->eventstate->alt) {
1364           if ((keymodifier & KM_ALT) == 0) {
1365             kdata.key = GHOST_kKeyLeftAlt;
1366             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
1367           }
1368         }
1369 #ifdef USE_WIN_ACTIVATE
1370         else {
1371           if (keymodifier & KM_ALT) {
1372             win->eventstate->alt = KM_MOD_FIRST;
1373           }
1374         }
1375 #endif
1376         if (win->eventstate->oskey) {
1377           if ((keymodifier & KM_OSKEY) == 0) {
1378             kdata.key = GHOST_kKeyOS;
1379             wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
1380           }
1381         }
1382 #ifdef USE_WIN_ACTIVATE
1383         else {
1384           if (keymodifier & KM_OSKEY) {
1385             win->eventstate->oskey = KM_MOD_FIRST;
1386           }
1387         }
1388 #endif
1389
1390 #undef USE_WIN_ACTIVATE
1391
1392         /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
1393         win->eventstate->keymodifier = 0;
1394
1395         /* entering window, update mouse pos. but no event */
1396         wm_get_cursor_position(win, &wx, &wy);
1397
1398         win->eventstate->x = wx;
1399         win->eventstate->y = wy;
1400
1401         win->addmousemove = 1; /* enables highlighted buttons */
1402
1403         wm_window_make_drawable(wm, win);
1404
1405         /* window might be focused by mouse click in configuration of window manager
1406          * when focus is not following mouse
1407          * click could have been done on a button and depending on window manager settings
1408          * click would be passed to blender or not, but in any case button under cursor
1409          * should be activated, so at max next click on button without moving mouse
1410          * would trigger it's handle function
1411          * currently it seems to be common practice to generate new event for, but probably
1412          * we'll need utility function for this? (sergey)
1413          */
1414         wm_event_init_from_window(win, &event);
1415         event.type = MOUSEMOVE;
1416         event.prevx = event.x;
1417         event.prevy = event.y;
1418
1419         wm_event_add(win, &event);
1420
1421         break;
1422       }
1423       case GHOST_kEventWindowClose: {
1424         wm_window_close(C, wm, win);
1425         break;
1426       }
1427       case GHOST_kEventWindowUpdate: {
1428         if (G.debug & G_DEBUG_EVENTS) {
1429           printf("%s: ghost redraw %d\n", __func__, win->winid);
1430         }
1431
1432         wm_window_make_drawable(wm, win);
1433         WM_event_add_notifier(C, NC_WINDOW, NULL);
1434
1435         break;
1436       }
1437       case GHOST_kEventWindowSize:
1438       case GHOST_kEventWindowMove: {
1439         GHOST_TWindowState state;
1440         state = GHOST_GetWindowState(win->ghostwin);
1441         win->windowstate = state;
1442
1443         WM_window_set_dpi(win);
1444
1445         /* win32: gives undefined window size when minimized */
1446         if (state != GHOST_kWindowStateMinimized) {
1447           GHOST_RectangleHandle client_rect;
1448           int l, t, r, b, scr_w, scr_h;
1449           int sizex, sizey, posx, posy;
1450
1451           client_rect = GHOST_GetClientBounds(win->ghostwin);
1452           GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
1453
1454           GHOST_DisposeRectangle(client_rect);
1455
1456           wm_get_desktopsize(&scr_w, &scr_h);
1457           sizex = r - l;
1458           sizey = b - t;
1459           posx = l;
1460           posy = scr_h - t - win->sizey;
1461
1462           /*
1463            * Ghost sometimes send size or move events when the window hasn't changed.
1464            * One case of this is using compiz on linux. To alleviate the problem
1465            * we ignore all such event here.
1466            *
1467            * It might be good to eventually do that at Ghost level, but that is for
1468            * another time.
1469            */
1470           if (win->sizex != sizex || win->sizey != sizey || win->posx != posx ||
1471               win->posy != posy) {
1472             const bScreen *screen = WM_window_get_active_screen(win);
1473
1474             win->sizex = sizex;
1475             win->sizey = sizey;
1476             win->posx = posx;
1477             win->posy = posy;
1478
1479             /* debug prints */
1480             if (G.debug & G_DEBUG_EVENTS) {
1481               const char *state_str;
1482               state = GHOST_GetWindowState(win->ghostwin);
1483
1484               if (state == GHOST_kWindowStateNormal) {
1485                 state_str = "normal";
1486               }
1487               else if (state == GHOST_kWindowStateMinimized) {
1488                 state_str = "minimized";
1489               }
1490               else if (state == GHOST_kWindowStateMaximized) {
1491                 state_str = "maximized";
1492               }
1493               else if (state == GHOST_kWindowStateFullScreen) {
1494                 state_str = "fullscreen";
1495               }
1496               else {
1497                 state_str = "<unknown>";
1498               }
1499
1500               printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
1501
1502               if (type != GHOST_kEventWindowSize) {
1503                 printf("win move event pos %d %d size %d %d\n",
1504                        win->posx,
1505                        win->posy,
1506                        win->sizex,
1507                        win->sizey);
1508               }
1509             }
1510
1511             wm_window_make_drawable(wm, win);
1512             BKE_icon_changed(screen->id.icon_id);
1513             WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1514             WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1515
1516 #if defined(__APPLE__) || defined(WIN32)
1517             /* OSX and Win32 don't return to the mainloop while resize */
1518             wm_window_timer(C);
1519             wm_event_do_handlers(C);
1520             wm_event_do_notifiers(C);
1521             wm_draw_update(C);
1522 #endif
1523           }
1524         }
1525         break;
1526       }
1527
1528       case GHOST_kEventWindowDPIHintChanged: {
1529         WM_window_set_dpi(win);
1530         /* font's are stored at each DPI level, without this we can easy load 100's of fonts */
1531         BLF_cache_clear();
1532
1533         WM_main_add_notifier(NC_WINDOW, NULL);             /* full redraw */
1534         WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL); /* refresh region sizes */
1535         break;
1536       }
1537
1538       case GHOST_kEventOpenMainFile: {
1539         PointerRNA props_ptr;
1540         const char *path = GHOST_GetEventData(evt);
1541
1542         if (path) {
1543           wmOperatorType *ot = WM_operatortype_find("WM_OT_open_mainfile", false);
1544           /* operator needs a valid window in context, ensures
1545            * it is correctly set */
1546           CTX_wm_window_set(C, win);
1547
1548           WM_operator_properties_create_ptr(&props_ptr, ot);
1549           RNA_string_set(&props_ptr, "filepath", path);
1550           WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr);
1551           WM_operator_properties_free(&props_ptr);
1552
1553           CTX_wm_window_set(C, NULL);
1554         }
1555         break;
1556       }
1557       case GHOST_kEventDraggingDropDone: {
1558         wmEvent event;
1559         GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
1560         int wx, wy;
1561
1562         /* entering window, update mouse pos */
1563         wm_get_cursor_position(win, &wx, &wy);
1564         win->eventstate->x = wx;
1565         win->eventstate->y = wy;
1566
1567         wm_event_init_from_window(win, &event); /* copy last state, like mouse coords */
1568
1569         /* activate region */
1570         event.type = MOUSEMOVE;
1571         event.prevx = event.x;
1572         event.prevy = event.y;
1573
1574         wm->winactive =
1575             win; /* no context change! c->wm->windrawable is drawable, or for area queues */
1576         win->active = 1;
1577
1578         wm_event_add(win, &event);
1579
1580         /* make blender drop event with custom data pointing to wm drags */
1581         event.type = EVT_DROP;
1582         event.val = KM_RELEASE;
1583         event.custom = EVT_DATA_DRAGDROP;
1584         event.customdata = &wm->drags;
1585         event.customdatafree = 1;
1586
1587         wm_event_add(win, &event);
1588
1589         /* printf("Drop detected\n"); */
1590
1591         /* add drag data to wm for paths: */
1592
1593         if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
1594           GHOST_TStringArray *stra = ddd->data;
1595           int a, icon;
1596
1597           for (a = 0; a < stra->count; a++) {
1598             printf("drop file %s\n", stra->strings[a]);
1599             /* try to get icon type from extension */
1600             icon = ED_file_extension_icon((char *)stra->strings[a]);
1601
1602             WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0, WM_DRAG_NOP);
1603             /* void poin should point to string, it makes a copy */
1604             break; /* only one drop element supported now */
1605           }
1606         }
1607
1608         break;
1609       }
1610       case GHOST_kEventNativeResolutionChange: {
1611         // only update if the actual pixel size changes
1612         float prev_pixelsize = U.pixelsize;
1613         WM_window_set_dpi(win);
1614
1615         if (U.pixelsize != prev_pixelsize) {
1616           BKE_icon_changed(WM_window_get_active_screen(win)->id.icon_id);
1617
1618           // close all popups since they are positioned with the pixel
1619           // size baked in and it's difficult to correct them
1620           CTX_wm_window_set(C, win);
1621           UI_popup_handlers_remove_all(C, &win->modalhandlers);
1622           CTX_wm_window_set(C, NULL);
1623
1624           wm_window_make_drawable(wm, win);
1625
1626           WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1627           WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1628         }
1629
1630         break;
1631       }
1632       case GHOST_kEventTrackpad: {
1633         GHOST_TEventTrackpadData *pd = data;
1634
1635         wm_cursor_position_from_ghost(win, &pd->x, &pd->y);
1636         wm_event_add_ghostevent(wm, win, type, time, data);
1637         break;
1638       }
1639       case GHOST_kEventCursorMove: {
1640         GHOST_TEventCursorData *cd = data;
1641
1642         wm_cursor_position_from_ghost(win, &cd->x, &cd->y);
1643         wm_event_add_ghostevent(wm, win, type, time, data);
1644         break;
1645       }
1646       default:
1647         wm_event_add_ghostevent(wm, win, type, time, data);
1648         break;
1649     }
1650   }
1651   return 1;
1652 }
1653
1654 /**
1655  * This timer system only gives maximum 1 timer event per redraw cycle,
1656  * to prevent queues to get overloaded.
1657  * Timer handlers should check for delta to decide if they just update, or follow real time.
1658  * Timer handlers can also set duration to match frames passed
1659  */
1660 static int wm_window_timer(const bContext *C)
1661 {
1662   wmWindowManager *wm = CTX_wm_manager(C);
1663   wmTimer *wt, *wtnext;
1664   wmWindow *win;
1665   double time = PIL_check_seconds_timer();
1666   int retval = 0;
1667
1668   for (wt = wm->timers.first; wt; wt = wtnext) {
1669     wtnext = wt->next; /* in case timer gets removed */
1670     win = wt->win;
1671
1672     if (wt->sleep == 0) {
1673       if (time > wt->ntime) {
1674         wt->delta = time - wt->ltime;
1675         wt->duration += wt->delta;
1676         wt->ltime = time;
1677         wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep);
1678
1679         if (wt->event_type == TIMERJOBS) {
1680           wm_jobs_timer(C, wm, wt);
1681         }
1682         else if (wt->event_type == TIMERAUTOSAVE) {
1683           wm_autosave_timer(C, wm, wt);
1684         }
1685         else if (wt->event_type == TIMERNOTIFIER) {
1686           WM_main_add_notifier(POINTER_AS_UINT(wt->customdata), NULL);
1687         }
1688         else if (win) {
1689           wmEvent event;
1690           wm_event_init_from_window(win, &event);
1691
1692           event.type = wt->event_type;
1693           event.val = KM_NOTHING;
1694           event.keymodifier = 0;
1695           event.custom = EVT_DATA_TIMER;
1696           event.customdata = wt;
1697           wm_event_add(win, &event);
1698
1699           retval = 1;
1700         }
1701       }
1702     }
1703   }
1704   return retval;
1705 }
1706
1707 void wm_window_process_events(const bContext *C)
1708 {
1709   int hasevent;
1710
1711   BLI_assert(BLI_thread_is_main());
1712
1713   hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
1714
1715   if (hasevent) {
1716     GHOST_DispatchEvents(g_system);
1717   }
1718   hasevent |= wm_window_timer(C);
1719
1720   /* no event, we sleep 5 milliseconds */
1721   if (hasevent == 0) {
1722     PIL_sleep_ms(5);
1723   }
1724 }
1725
1726 /* **************** init ********************** */
1727
1728 /* bContext can be null in background mode because we don't
1729  * need to event handling. */
1730 void wm_ghost_init(bContext *C)
1731 {
1732   if (!g_system) {
1733     GHOST_EventConsumerHandle consumer;
1734
1735     if (C != NULL) {
1736       consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
1737     }
1738
1739     g_system = GHOST_CreateSystem();
1740
1741     if (C != NULL) {
1742       GHOST_AddEventConsumer(g_system, consumer);
1743     }
1744
1745     if (wm_init_state.native_pixels) {
1746       GHOST_UseNativePixels();
1747     }
1748
1749     GHOST_UseWindowFocus(wm_init_state.window_focus);
1750
1751     WM_init_tablet_api();
1752   }
1753 }
1754
1755 void wm_ghost_exit(void)
1756 {
1757   if (g_system) {
1758     GHOST_DisposeSystem(g_system);
1759   }
1760   g_system = NULL;
1761 }
1762
1763 /* **************** timer ********************** */
1764
1765 /* to (de)activate running timers temporary */
1766 void WM_event_timer_sleep(wmWindowManager *wm,
1767                           wmWindow *UNUSED(win),
1768                           wmTimer *timer,
1769                           bool do_sleep)
1770 {
1771   wmTimer *wt;
1772
1773   for (wt = wm->timers.first; wt; wt = wt->next) {
1774     if (wt == timer) {
1775       break;
1776     }
1777   }
1778
1779   if (wt) {
1780     wt->sleep = do_sleep;
1781   }
1782 }
1783
1784 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
1785 {
1786   wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1787
1788   wt->event_type = event_type;
1789   wt->ltime = PIL_check_seconds_timer();
1790   wt->ntime = wt->ltime + timestep;
1791   wt->stime = wt->ltime;
1792   wt->timestep = timestep;
1793   wt->win = win;
1794
1795   BLI_addtail(&wm->timers, wt);
1796
1797   return wt;
1798 }
1799
1800 wmTimer *WM_event_add_timer_notifier(wmWindowManager *wm,
1801                                      wmWindow *win,
1802                                      unsigned int type,
1803                                      double timestep)
1804 {
1805   wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1806
1807   wt->event_type = TIMERNOTIFIER;
1808   wt->ltime = PIL_check_seconds_timer();
1809   wt->ntime = wt->ltime + timestep;
1810   wt->stime = wt->ltime;
1811   wt->timestep = timestep;
1812   wt->win = win;
1813   wt->customdata = POINTER_FROM_UINT(type);
1814   wt->flags |= WM_TIMER_NO_FREE_CUSTOM_DATA;
1815
1816   BLI_addtail(&wm->timers, wt);
1817
1818   return wt;
1819 }
1820
1821 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
1822 {
1823   wmTimer *wt;
1824
1825   /* extra security check */
1826   for (wt = wm->timers.first; wt; wt = wt->next) {
1827     if (wt == timer) {
1828       break;
1829     }
1830   }
1831   if (wt) {
1832     wmWindow *win;
1833
1834     if (wm->reports.reporttimer == wt) {
1835       wm->reports.reporttimer = NULL;
1836     }
1837
1838     BLI_remlink(&wm->timers, wt);
1839     if (wt->customdata != NULL && (wt->flags & WM_TIMER_NO_FREE_CUSTOM_DATA) == 0) {
1840       MEM_freeN(wt->customdata);
1841     }
1842     MEM_freeN(wt);
1843
1844     /* there might be events in queue with this timer as customdata */
1845     for (win = wm->windows.first; win; win = win->next) {
1846       wmEvent *event;
1847       for (event = win->queue.first; event; event = event->next) {
1848         if (event->customdata == wt) {
1849           event->customdata = NULL;
1850           event->type = EVENT_NONE; /* timer users customdata, dont want NULL == NULL */
1851         }
1852       }
1853     }
1854   }
1855 }
1856
1857 void WM_event_remove_timer_notifier(wmWindowManager *wm, wmWindow *win, wmTimer *timer)
1858 {
1859   timer->customdata = NULL;
1860   WM_event_remove_timer(wm, win, timer);
1861 }
1862
1863 /* ******************* clipboard **************** */
1864
1865 static char *wm_clipboard_text_get_ex(bool selection, int *r_len, bool firstline)
1866 {
1867   char *p, *p2, *buf, *newbuf;
1868
1869   if (G.background) {
1870     *r_len = 0;
1871     return NULL;
1872   }
1873
1874   buf = (char *)GHOST_getClipboard(selection);
1875   if (!buf) {
1876     *r_len = 0;
1877     return NULL;
1878   }
1879
1880   /* always convert from \r\n to \n */
1881   p2 = newbuf = MEM_mallocN(strlen(buf) + 1, __func__);
1882
1883   if (firstline) {
1884     /* will return an over-alloc'ed value in the case there are newlines */
1885     for (p = buf; *p; p++) {
1886       if ((*p != '\n') && (*p != '\r')) {
1887         *(p2++) = *p;
1888       }
1889       else {
1890         break;
1891       }
1892     }
1893   }
1894   else {
1895     for (p = buf; *p; p++) {
1896       if (*p != '\r') {
1897         *(p2++) = *p;
1898       }
1899     }
1900   }
1901
1902   *p2 = '\0';
1903
1904   free(buf); /* ghost uses regular malloc */
1905
1906   *r_len = (p2 - newbuf);
1907
1908   return newbuf;
1909 }
1910
1911 /**
1912  * Return text from the clipboard.
1913  *
1914  * \note Caller needs to check for valid utf8 if this is a requirement.
1915  */
1916 char *WM_clipboard_text_get(bool selection, int *r_len)
1917 {
1918   return wm_clipboard_text_get_ex(selection, r_len, false);
1919 }
1920
1921 /**
1922  * Convenience function for pasting to areas of Blender which don't support newlines.
1923  */
1924 char *WM_clipboard_text_get_firstline(bool selection, int *r_len)
1925 {
1926   return wm_clipboard_text_get_ex(selection, r_len, true);
1927 }
1928
1929 void WM_clipboard_text_set(const char *buf, bool selection)
1930 {
1931   if (!G.background) {
1932 #ifdef _WIN32
1933     /* do conversion from \n to \r\n on Windows */
1934     const char *p;
1935     char *p2, *newbuf;
1936     int newlen = 0;
1937
1938     for (p = buf; *p; p++) {
1939       if (*p == '\n') {
1940         newlen += 2;
1941       }
1942       else {
1943         newlen++;
1944       }
1945     }
1946
1947     newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
1948
1949     for (p = buf, p2 = newbuf; *p; p++, p2++) {
1950       if (*p == '\n') {
1951         *(p2++) = '\r';
1952         *p2 = '\n';
1953       }
1954       else {
1955         *p2 = *p;
1956       }
1957     }
1958     *p2 = '\0';
1959
1960     GHOST_putClipboard((GHOST_TInt8 *)newbuf, selection);
1961     MEM_freeN(newbuf);
1962 #else
1963     GHOST_putClipboard((GHOST_TInt8 *)buf, selection);
1964 #endif
1965   }
1966 }
1967
1968 /* ******************* progress bar **************** */
1969
1970 void WM_progress_set(wmWindow *win, float progress)
1971 {
1972   GHOST_SetProgressBar(win->ghostwin, progress);
1973 }
1974
1975 void WM_progress_clear(wmWindow *win)
1976 {
1977   GHOST_EndProgressBar(win->ghostwin);
1978 }
1979
1980 /* ************************************ */
1981
1982 void wm_window_get_position(wmWindow *win, int *r_pos_x, int *r_pos_y)
1983 {
1984   *r_pos_x = win->posx;
1985   *r_pos_y = win->posy;
1986 }
1987
1988 void wm_window_set_size(wmWindow *win, int width, int height)
1989 {
1990   GHOST_SetClientSize(win->ghostwin, width, height);
1991 }
1992
1993 void wm_window_lower(wmWindow *win)
1994 {
1995   GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
1996 }
1997
1998 void wm_window_raise(wmWindow *win)
1999 {
2000   GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
2001 }
2002
2003 void wm_window_swap_buffers(wmWindow *win)
2004 {
2005   GHOST_SwapWindowBuffers(win->ghostwin);
2006 }
2007
2008 void wm_window_set_swap_interval(wmWindow *win, int interval)
2009 {
2010   GHOST_SetSwapInterval(win->ghostwin, interval);
2011 }
2012
2013 bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut)
2014 {
2015   return GHOST_GetSwapInterval(win->ghostwin, intervalOut);
2016 }
2017
2018 /* ******************* exported api ***************** */
2019
2020 /* called whem no ghost system was initialized */
2021 void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
2022 {
2023   wm_init_state.start_x = stax; /* left hand pos */
2024   wm_init_state.start_y = stay; /* bottom pos */
2025   wm_init_state.size_x = sizx < 640 ? 640 : sizx;
2026   wm_init_state.size_y = sizy < 480 ? 480 : sizy;
2027   wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
2028 }
2029
2030 /* for borderless and border windows set from command-line */
2031 void WM_init_state_fullscreen_set(void)
2032 {
2033   wm_init_state.windowstate = GHOST_kWindowStateFullScreen;
2034   wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2035 }
2036
2037 void WM_init_state_normal_set(void)
2038 {
2039   wm_init_state.windowstate = GHOST_kWindowStateNormal;
2040   wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2041 }
2042
2043 void WM_init_state_maximized_set(void)
2044 {
2045   wm_init_state.windowstate = GHOST_kWindowStateMaximized;
2046   wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
2047 }
2048
2049 void WM_init_window_focus_set(bool do_it)
2050 {
2051   wm_init_state.window_focus = do_it;
2052 }
2053
2054 void WM_init_native_pixels(bool do_it)
2055 {
2056   wm_init_state.native_pixels = do_it;
2057 }
2058
2059 void WM_init_tablet_api(void)
2060 {
2061   if (g_system) {
2062     switch (U.tablet_api) {
2063       case USER_TABLET_NATIVE:
2064         GHOST_SetTabletAPI(g_system, GHOST_kTabletNative);
2065         break;
2066       case USER_TABLET_WINTAB:
2067         GHOST_SetTabletAPI(g_system, GHOST_kTabletWintab);
2068         break;
2069       case USER_TABLET_AUTOMATIC:
2070       default:
2071         GHOST_SetTabletAPI(g_system, GHOST_kTabletAutomatic);
2072         break;
2073     }
2074   }
2075 }
2076
2077 /* This function requires access to the GHOST_SystemHandle (g_system) */
2078 void WM_cursor_warp(wmWindow *win, int x, int y)
2079 {
2080   if (win && win->ghostwin) {
2081     int oldx = x, oldy = y;
2082
2083     wm_cursor_position_to_ghost(win, &x, &y);
2084     GHOST_SetCursorPosition(g_system, x, y);
2085
2086     win->eventstate->prevx = oldx;
2087     win->eventstate->prevy = oldy;
2088
2089     win->eventstate->x = oldx;
2090     win->eventstate->y = oldy;
2091   }
2092 }
2093
2094 /**
2095  * Set x, y to values we can actually position the cursor to.
2096  */
2097 void WM_cursor_compatible_xy(wmWindow *win, int *x, int *y)
2098 {
2099   float f = GHOST_GetNativePixelSize(win->ghostwin);
2100   if (f != 1.0f) {
2101     *x = (int)(*x / f) * f;
2102     *y = (int)(*y / f) * f;
2103   }
2104 }
2105
2106 /**
2107  * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event
2108  */
2109 float WM_cursor_pressure(const struct wmWindow *win)
2110 {
2111   const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin);
2112   /* if there's tablet data from an active tablet device then add it */
2113   if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2114     return wm_pressure_curve(td->Pressure);
2115   }
2116   else {
2117     return -1.0f;
2118   }
2119 }
2120
2121 /* support for native pixel size */
2122 /* mac retina opens window in size X, but it has up to 2 x more pixels */
2123 int WM_window_pixels_x(const wmWindow *win)
2124 {
2125   float f = GHOST_GetNativePixelSize(win->ghostwin);
2126
2127   return (int)(f * (float)win->sizex);
2128 }
2129 int WM_window_pixels_y(const wmWindow *win)
2130 {
2131   float f = GHOST_GetNativePixelSize(win->ghostwin);
2132
2133   return (int)(f * (float)win->sizey);
2134 }
2135
2136 /**
2137  * Get boundaries usable by all window contents, including global areas.
2138  */
2139 void WM_window_rect_calc(const wmWindow *win, rcti *r_rect)
2140 {
2141   BLI_rcti_init(r_rect, 0, WM_window_pixels_x(win), 0, WM_window_pixels_y(win));
2142 }
2143 /**
2144  * Get boundaries usable by screen-layouts, excluding global areas.
2145  * \note Depends on U.dpi_fac. Should that be outdated, call #WM_window_set_dpi first.
2146  */
2147 void WM_window_screen_rect_calc(const wmWindow *win, rcti *r_rect)
2148 {
2149   rcti window_rect, screen_rect;
2150
2151   WM_window_rect_calc(win, &window_rect);
2152   screen_rect = window_rect;
2153
2154   /* Subtract global areas from screen rectangle. */
2155   for (ScrArea *global_area = win->global_areas.areabase.first; global_area;
2156        global_area = global_area->next) {
2157     int height = ED_area_global_size_y(global_area) - 1;
2158
2159     if (global_area->global->flag & GLOBAL_AREA_IS_HIDDEN) {
2160       continue;
2161     }
2162
2163     switch (global_area->global->align) {
2164       case GLOBAL_AREA_ALIGN_TOP:
2165         if ((screen_rect.ymax - height) > window_rect.ymin) {
2166           height += U.pixelsize;
2167         }
2168         if (screen_rect.ymax < (window_rect.ymax - 1)) {
2169           height += U.pixelsize;
2170         }
2171         screen_rect.ymax -= height;
2172         break;
2173       case GLOBAL_AREA_ALIGN_BOTTOM:
2174         if (screen_rect.ymin > window_rect.ymin) {
2175           height += U.pixelsize;
2176         }
2177         if ((screen_rect.ymin + height) < (window_rect.ymax - 1)) {
2178           height += U.pixelsize;
2179         }
2180         screen_rect.ymin += height;
2181         break;
2182       default:
2183         BLI_assert(0);
2184         break;
2185     }
2186   }
2187
2188   BLI_assert(screen_rect.xmin < screen_rect.xmax);
2189   BLI_assert(screen_rect.ymin < screen_rect.ymax);
2190   *r_rect = screen_rect;
2191 }
2192
2193 bool WM_window_is_fullscreen(wmWindow *win)
2194 {
2195   return win->windowstate == GHOST_kWindowStateFullScreen;
2196 }
2197
2198 /**
2199  * Some editor data may need to be synced with scene data (3D View camera and layers).
2200  * This function ensures data is synced for editors in visible workspaces and their visible layouts.
2201  */
2202 void WM_windows_scene_data_sync(const ListBase *win_lb, Scene *scene)
2203 {
2204   for (wmWindow *win = win_lb->first; win; win = win->next) {
2205     if (WM_window_get_active_scene(win) == scene) {
2206       ED_workspace_scene_data_sync(win->workspace_hook, scene);
2207     }
2208   }
2209 }
2210
2211 Scene *WM_windows_scene_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
2212 {
2213   for (wmWindow *win = wm->windows.first; win; win = win->next) {
2214     if (WM_window_get_active_screen(win) == screen) {
2215       return WM_window_get_active_scene(win);
2216     }
2217   }
2218
2219   return NULL;
2220 }
2221
2222 WorkSpace *WM_windows_workspace_get_from_screen(const wmWindowManager *wm, const bScreen *screen)
2223 {
2224   for (wmWindow *win = wm->windows.first; win; win = win->next) {
2225     if (WM_window_get_active_screen(win) == screen) {
2226       return WM_window_get_active_workspace(win);
2227     }
2228   }
2229   return NULL;
2230 }
2231
2232 Scene *WM_window_get_active_scene(const wmWindow *win)
2233 {
2234   return win->scene;
2235 }
2236
2237 /**
2238  * \warning Only call outside of area/region loops
2239  */
2240 void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene *scene)
2241 {
2242   wmWindowManager *wm = CTX_wm_manager(C);
2243   wmWindow *win_parent = (win->parent) ? win->parent : win;
2244   bool changed = false;
2245
2246   /* Set scene in parent and its child windows. */
2247   if (win_parent->scene != scene) {
2248     ED_screen_scene_change(C, win_parent, scene);
2249     changed = true;
2250   }
2251
2252   for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
2253     if (win_child->parent == win_parent && win_child->scene != scene) {
2254       ED_screen_scene_change(C, win_child, scene);
2255       changed = true;
2256     }
2257   }
2258
2259   if (changed) {
2260     /* Update depsgraph and renderers for scene change. */
2261     ViewLayer *view_layer = WM_window_get_active_view_layer(win_parent);
2262     ED_scene_change_update(bmain, scene, view_layer);
2263
2264     /* Complete redraw. */
2265     WM_event_add_notifier(C, NC_WINDOW, NULL);
2266   }
2267 }
2268
2269 ViewLayer *WM_window_get_active_view_layer(const wmWindow *win)
2270 {
2271   Scene *scene = WM_window_get_active_scene(win);
2272   if (scene == NULL) {
2273     return NULL;
2274   }
2275
2276   ViewLayer *view_layer = BKE_view_layer_find(scene, win->view_layer_name);
2277   if (view_layer) {
2278     return view_layer;
2279   }
2280
2281   view_layer = BKE_view_layer_default_view(scene);
2282   if (view_layer) {
2283     WM_window_set_active_view_layer((wmWindow *)win, view_layer);
2284   }
2285
2286   return view_layer;
2287 }
2288
2289 void WM_window_set_active_view_layer(wmWindow *win, ViewLayer *view_layer)
2290 {
2291   BLI_assert(BKE_view_layer_find(WM_window_get_active_scene(win), view_layer->name) != NULL);
2292   Main *bmain = G_MAIN;
2293
2294   wmWindowManager *wm = bmain->wm.first;
2295   wmWindow *win_parent = (win->parent) ? win->parent : win;
2296
2297   /* Set view layer in parent and child windows. */
2298   for (wmWindow *win_iter = wm->windows.first; win_iter; win_iter = win_iter->next) {
2299     if ((win_iter == win_parent) || (win_iter->parent == win_parent)) {
2300       STRNCPY(win_iter->view_layer_name, view_layer->name);
2301       bScreen *screen = BKE_workspace_active_screen_get(win_iter->workspace_hook);
2302       ED_render_view_layer_changed(bmain, screen);
2303     }
2304   }
2305 }
2306
2307 void WM_window_ensure_active_view_layer(wmWindow *win)
2308 {
2309   /* Update layer name is correct after scene changes, load without UI, etc. */
2310   Scene *scene = WM_window_get_active_scene(win);
2311
2312   if (scene && BKE_view_layer_find(scene, win->view_layer_name) == NULL) {
2313     ViewLayer *view_layer = BKE_view_layer_default_view(scene);
2314     STRNCPY(win->view_layer_name, view_layer->name);
2315   }
2316 }
2317
2318 WorkSpace *WM_window_get_active_workspace(const wmWindow *win)
2319 {
2320   return BKE_workspace_active_get(win->workspace_hook);
2321 }
2322
2323 void WM_window_set_active_workspace(bContext *C, wmWindow *win, WorkSpace *workspace)
2324 {
2325   wmWindowManager *wm = CTX_wm_manager(C);
2326   wmWindow *win_parent = (win->parent) ? win->parent : win;
2327
2328   ED_workspace_change(workspace, C, wm, win);
2329
2330   for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
2331     if (win_child->parent == win_parent) {
2332       bScreen *screen = WM_window_get_active_screen(win_child);
2333       /* Don't change temporary screens, they only serve a single purpose. */
2334       if (screen->temp) {
2335         continue;
2336       }
2337       ED_workspace_change(workspace, C, wm, win_child);
2338     }
2339   }
2340 }
2341
2342 WorkSpaceLayout *WM_window_get_active_layout(const wmWindow *win)
2343 {
2344   const WorkSpace *workspace = WM_window_get_active_workspace(win);
2345   return (LIKELY(workspace != NULL) ? BKE_workspace_active_layout_get(win->workspace_hook) : NULL);
2346 }
2347 void WM_window_set_active_layout(wmWindow *win, WorkSpace *workspace, WorkSpaceLayout *layout)
2348 {
2349   BKE_workspace_hook_layout_for_workspace_set(win->workspace_hook, workspace, layout);
2350 }
2351
2352 /**
2353  * Get the active screen of the active workspace in \a win.
2354  */
2355 bScreen *WM_window_get_active_screen(const wmWindow *win)
2356 {
2357   const WorkSpace *workspace = WM_window_get_active_workspace(win);
2358   /* May be NULL in rare cases like closing Blender */
2359   return (LIKELY(workspace != NULL) ? BKE_workspace_active_screen_get(win->workspace_hook) : NULL);
2360 }
2361 void WM_window_set_active_screen(wmWindow *win, WorkSpace *workspace, bScreen *screen)
2362 {
2363   BKE_workspace_active_screen_set(win->workspace_hook, workspace, screen);
2364 }
2365
2366 bool WM_window_is_temp_screen(const wmWindow *win)
2367 {
2368   const bScreen *screen = WM_window_get_active_screen(win);
2369   return (screen && screen->temp != 0);
2370 }
2371
2372 #ifdef WITH_INPUT_IME
2373 /* note: keep in mind wm_window_IME_begin is also used to reposition the IME window */
2374 void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete)
2375 {
2376   BLI_assert(win);
2377
2378   GHOST_BeginIME(win->ghostwin, x, win->sizey - y, w, h, complete);
2379 }
2380
2381 void wm_window_IME_end(wmWindow *win)
2382 {
2383   BLI_assert(win && win->ime_data);
2384
2385   GHOST_EndIME(win->ghostwin);
2386   win->ime_data = NULL;
2387 }
2388 #endif /* WITH_INPUT_IME */
2389
2390 /* ****** direct opengl context management ****** */
2391
2392 void *WM_opengl_context_create(void)
2393 {
2394   /* On Windows there is a problem creating contexts that share lists
2395    * from one context that is current in another thread.
2396    * So we should call this function only on the main thread.
2397    */
2398   BLI_assert(BLI_thread_is_main());
2399   BLI_assert(GPU_framebuffer_active_get() == NULL);
2400   return GHOST_CreateOpenGLContext(g_system);
2401 }
2402
2403 void WM_opengl_context_dispose(void *context)
2404 {
2405   BLI_assert(GPU_framebuffer_active_get() == NULL);
2406   GHOST_DisposeOpenGLContext(g_system, (GHOST_ContextHandle)context);
2407 }
2408
2409 void WM_opengl_context_activate(void *context)
2410 {
2411   BLI_assert(GPU_framebuffer_active_get() == NULL);
2412   GHOST_ActivateOpenGLContext((GHOST_ContextHandle)context);
2413 }
2414
2415 void WM_opengl_context_release(void *context)
2416 {
2417   BLI_assert(GPU_framebuffer_active_get() == NULL);
2418   GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
2419 }