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