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