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