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