Color management refactor
[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
31
32 #include <math.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include "DNA_listBase.h"       
38 #include "DNA_screen_types.h"
39 #include "DNA_windowmanager_types.h"
40 #include "RNA_access.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "GHOST_C-api.h"
45
46 #include "BLI_blenlib.h"
47 #include "BLI_utildefines.h"
48
49 #include "BLF_translation.h"
50
51 #include "BKE_blender.h"
52 #include "BKE_colortools.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 "BIF_gl.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
77 #include "UI_interface.h"
78
79 /* the global to talk to ghost */
80 static GHOST_SystemHandle g_system = NULL;
81
82 typedef enum WinOverrideFlag {
83         WIN_OVERRIDE_GEOM     = (1 << 0),
84         WIN_OVERRIDE_WINSTATE = (1 << 1)
85 } WinOverrideFlag;
86
87 /* set by commandline */
88 static struct WMInitStruct {
89         /* window geometry */
90         int size_x, size_y;
91         int start_x, start_y;
92
93         int windowstate;
94         WinOverrideFlag override_flag;
95 } wm_init_state = {0, 0, 0, 0, GHOST_kWindowStateNormal, 0};
96
97 /* ******** win open & close ************ */
98
99 /* XXX this one should correctly check for apple top header...
100  * done for Cocoa : returns window contents (and not frame) max size*/
101 void wm_get_screensize(int *width_r, int *height_r)
102 {
103         unsigned int uiwidth;
104         unsigned int uiheight;
105         
106         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
107         *width_r = uiwidth;
108         *height_r = uiheight;
109 }
110
111 /* keeps offset and size within monitor bounds */
112 /* XXX solve dual screen... */
113 static void wm_window_check_position(rcti *rect)
114 {
115         int width, height, d;
116         
117         wm_get_screensize(&width, &height);
118         
119 #if defined(__APPLE__) && !defined(GHOST_COCOA)
120         height -= 70;
121 #endif
122         
123         if (rect->xmin < 0) {
124                 rect->xmax -= rect->xmin;
125                 rect->xmin  = 0;
126         }
127         if (rect->ymin < 0) {
128                 rect->ymax -= rect->ymin;
129                 rect->ymin  = 0;
130         }
131         if (rect->xmax > width) {
132                 d = rect->xmax - width;
133                 rect->xmax -= d;
134                 rect->xmin -= d;
135         }
136         if (rect->ymax > height) {
137                 d = rect->ymax - height;
138                 rect->ymax -= d;
139                 rect->ymin -= d;
140         }
141         
142         if (rect->xmin < 0) rect->xmin = 0;
143         if (rect->ymin < 0) rect->ymin = 0;
144 }
145
146
147 static void wm_ghostwindow_destroy(wmWindow *win) 
148 {
149         if (win->ghostwin) {
150                 GHOST_DisposeWindow(g_system, win->ghostwin);
151                 win->ghostwin = NULL;
152         }
153 }
154
155 /* including window itself, C can be NULL. 
156  * ED_screen_exit should have been called */
157 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
158 {
159         wmTimer *wt, *wtnext;
160         
161         /* update context */
162         if (C) {
163                 WM_event_remove_handlers(C, &win->handlers);
164                 WM_event_remove_handlers(C, &win->modalhandlers);
165
166                 if (CTX_wm_window(C) == win)
167                         CTX_wm_window_set(C, NULL);
168         }       
169
170         /* always set drawable and active to NULL,
171          * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
172         wm->windrawable = NULL;
173         wm->winactive = NULL;
174
175         /* end running jobs, a job end also removes its timer */
176         for (wt = wm->timers.first; wt; wt = wtnext) {
177                 wtnext = wt->next;
178                 if (wt->win == win && wt->event_type == TIMERJOBS)
179                         wm_jobs_timer_ended(wm, wt);
180         }
181         
182         /* timer removing, need to call this api function */
183         for (wt = wm->timers.first; wt; wt = wtnext) {
184                 wtnext = wt->next;
185                 if (wt->win == win)
186                         WM_event_remove_timer(wm, win, wt);
187         }
188
189         if (win->eventstate) MEM_freeN(win->eventstate);
190         
191         wm_event_free_all(win);
192         wm_subwindows_free(win);
193         
194         if (win->drawdata)
195                 MEM_freeN(win->drawdata);
196         
197         wm_ghostwindow_destroy(win);
198         
199         MEM_freeN(win);
200 }
201
202 static int find_free_winid(wmWindowManager *wm)
203 {
204         wmWindow *win;
205         int id = 1;
206         
207         for (win = wm->windows.first; win; win = win->next)
208                 if (id <= win->winid)
209                         id = win->winid + 1;
210         
211         return id;
212 }
213
214 /* don't change context itself */
215 wmWindow *wm_window_new(bContext *C)
216 {
217         wmWindowManager *wm = CTX_wm_manager(C);
218         wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
219         
220         BLI_addtail(&wm->windows, win);
221         win->winid = find_free_winid(wm);
222
223         return win;
224 }
225
226
227 /* part of wm_window.c api */
228 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
229 {
230         wmWindow *win = wm_window_new(C);
231         
232         win->posx = winorig->posx + 10;
233         win->posy = winorig->posy;
234         win->sizex = winorig->sizex;
235         win->sizey = winorig->sizey;
236         
237         /* duplicate assigns to window */
238         win->screen = ED_screen_duplicate(win, winorig->screen);
239         BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
240         win->screen->winid = win->winid;
241
242         win->screen->do_refresh = TRUE;
243         win->screen->do_draw = TRUE;
244
245         win->drawmethod = -1;
246         win->drawdata = NULL;
247
248         BKE_color_managed_display_settings_copy(&win->display_settings, &winorig->display_settings);
249
250         return win;
251 }
252
253 /* this is event from ghost, or exit-blender op */
254 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
255 {
256         wmWindow *tmpwin;
257         bScreen *screen = win->screen;
258         
259         /* first check if we have any non-temp remaining windows */
260         if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved) {
261                 if (wm->windows.first) {
262                         for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
263                                 if (tmpwin == win)
264                                         continue;
265                                 if (tmpwin->screen->temp == 0)
266                                         break;
267                         }
268                         if (tmpwin == NULL) {
269                                 if (!GHOST_confirmQuit(win->ghostwin))
270                                         return;
271                         }
272                 }
273         }
274
275         BLI_remlink(&wm->windows, win);
276         
277         wm_draw_window_clear(win);
278         CTX_wm_window_set(C, win);  /* needed by handlers */
279         WM_event_remove_handlers(C, &win->handlers);
280         WM_event_remove_handlers(C, &win->modalhandlers);
281         ED_screen_exit(C, win, win->screen); 
282         
283         wm_window_free(C, wm, win);
284         
285         /* if temp screen, delete it after window free (it stops jobs that can access it) */
286         if (screen->temp) {
287                 Main *bmain = CTX_data_main(C);
288                 BKE_libblock_free(&bmain->screen, screen);
289         }
290         
291         /* check remaining windows */
292         if (wm->windows.first) {
293                 for (win = wm->windows.first; win; win = win->next)
294                         if (win->screen->temp == 0)
295                                 break;
296                 /* in this case we close all */
297                 if (win == NULL)
298                         WM_exit(C);
299         }
300         else
301                 WM_exit(C);
302 }
303
304 void wm_window_title(wmWindowManager *wm, wmWindow *win)
305 {
306         /* handle the 'temp' window, only set title when not set before */
307         if (win->screen && win->screen->temp) {
308                 char *title = GHOST_GetTitle(win->ghostwin);
309                 if (title == NULL || title[0] == 0)
310                         GHOST_SetTitle(win->ghostwin, "Blender");
311         }
312         else {
313                 
314                 /* this is set to 1 if you don't have startup.blend open */
315                 if (G.save_over && G.main->name[0]) {
316                         char str[sizeof(G.main->name) + 12];
317                         BLI_snprintf(str, sizeof(str), "Blender%s [%s]", wm->file_saved ? "" : "*", G.main->name);
318                         GHOST_SetTitle(win->ghostwin, str);
319                 }
320                 else
321                         GHOST_SetTitle(win->ghostwin, "Blender");
322
323                 /* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
324                  * and to give hint of unsaved changes for a user warning mechanism
325                  * in case of OS application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end) */
326                 GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8) !wm->file_saved);
327                 
328 #if defined(__APPLE__) && !defined(GHOST_COCOA)
329                 if (wm->file_saved)
330                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
331                 else
332                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
333 #endif
334         }
335 }
336
337 /* belongs to below */
338 static void wm_window_add_ghostwindow(const char *title, wmWindow *win)
339 {
340         GHOST_WindowHandle ghostwin;
341         int scr_w, scr_h, posy;
342         
343         wm_get_screensize(&scr_w, &scr_h);
344         posy = (scr_h - win->posy - win->sizey);
345         
346 #if defined(__APPLE__) && !defined(GHOST_COCOA)
347         {
348                 extern int macPrefState; /* creator.c */
349                 initial_state += macPrefState;
350         }
351 #endif
352         /* Disable AA for now, as GL_SELECT (used for border, lasso, ... select)
353          * doesn't work well when AA is initialized, even if not used. */
354         ghostwin = GHOST_CreateWindow(g_system, title,
355                                       win->posx, posy, win->sizex, win->sizey,
356                                       (GHOST_TWindowState)win->windowstate,
357                                       GHOST_kDrawingContextTypeOpenGL,
358                                       0 /* no stereo */,
359                                       0 /* no AA */);
360         
361         if (ghostwin) {
362                 /* needed so we can detect the graphics card below */
363                 GPU_extensions_init();
364                 
365                 /* set the state*/
366                 GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate);
367
368                 win->ghostwin = ghostwin;
369                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
370                 
371                 if (win->eventstate == NULL)
372                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
373                 
374                 /* until screens get drawn, make it nice grey */
375                 glClearColor(0.55, 0.55, 0.55, 0.0);
376                 /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
377                 if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
378                         glClear(GL_COLOR_BUFFER_BIT);
379                 }
380
381                 wm_window_swap_buffers(win);
382                 
383                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
384                 
385                 /* standard state vars for window */
386                 glEnable(GL_SCISSOR_TEST);
387                 
388                 GPU_state_init();
389         }
390 }
391
392 /* for wmWindows without ghostwin, open these and clear */
393 /* window size is read from window, if 0 it uses prefsize */
394 /* called in WM_check, also inits stuff after file read */
395 void wm_window_add_ghostwindows(wmWindowManager *wm)
396 {
397         wmKeyMap *keymap;
398         wmWindow *win;
399         
400         /* no commandline prefsize? then we set this.
401          * Note that these values will be used only
402          * when there is no startup.blend yet.
403          */
404         if (wm_init_state.size_x == 0) {
405                 wm_get_screensize(&wm_init_state.size_x, &wm_init_state.size_y);
406                 
407 #if defined(__APPLE__) && !defined(GHOST_COCOA)
408                 /* Cocoa provides functions to get correct max window size */
409                 {
410                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
411                         
412                         wm_set_apple_prefsize(wm_init_state.size_x, wm_init_state.size_y);
413                 }
414 #else
415                 wm_init_state.start_x = 0;
416                 wm_init_state.start_y = 0;
417                 
418 #endif
419         }
420         
421         for (win = wm->windows.first; win; win = win->next) {
422                 if (win->ghostwin == NULL) {
423                         if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
424                                 win->posx = wm_init_state.start_x;
425                                 win->posy = wm_init_state.start_y;
426                                 win->sizex = wm_init_state.size_x;
427                                 win->sizey = wm_init_state.size_y;
428                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
429                         }
430
431                         if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
432                                 win->windowstate = wm_init_state.windowstate;
433                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
434                         }
435
436                         wm_window_add_ghostwindow("Blender", win);
437                 }
438                 /* happens after fileread */
439                 if (win->eventstate == NULL)
440                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
441
442                 /* add keymap handlers (1 handler for all keys in map!) */
443                 keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0);
444                 WM_event_add_keymap_handler(&win->handlers, keymap);
445                 
446                 keymap = WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
447                 WM_event_add_keymap_handler(&win->handlers, keymap);
448
449                 keymap = WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
450                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
451                 
452                 /* add drop boxes */
453                 {
454                         ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
455                         WM_event_add_dropbox_handler(&win->handlers, lb);
456                 }
457                 wm_window_title(wm, win);
458         }
459 }
460
461 /* new window, no screen yet, but we open ghostwindow for it */
462 /* also gets the window level handlers */
463 /* area-rip calls this */
464 wmWindow *WM_window_open(bContext *C, rcti *rect)
465 {
466         wmWindow *win = wm_window_new(C);
467         
468         win->posx = rect->xmin;
469         win->posy = rect->ymin;
470         win->sizex = rect->xmax - rect->xmin;
471         win->sizey = rect->ymax - rect->ymin;
472
473         win->drawmethod = -1;
474         win->drawdata = NULL;
475         
476         WM_check(C);
477         
478         return win;
479 }
480
481 /* uses screen->temp tag to define what to do, currently it limits
482  * to only one "temp" window for render out, preferences, filewindow, etc */
483 /* type is defined in WM_api.h */
484
485 void WM_window_open_temp(bContext *C, rcti *position, int type)
486 {
487         wmWindow *win;
488         ScrArea *sa;
489         
490         /* changes rect to fit within desktop */
491         wm_window_check_position(position);
492         
493         /* test if we have a temp screen already */
494         for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
495                 if (win->screen->temp)
496                         break;
497         
498         /* add new window? */
499         if (win == NULL) {
500                 win = wm_window_new(C);
501                 
502                 win->posx = position->xmin;
503                 win->posy = position->ymin;
504         }
505         
506         win->sizex = position->xmax - position->xmin;
507         win->sizey = position->ymax - position->ymin;
508         
509         if (win->ghostwin) {
510                 wm_window_set_size(win, win->sizex, win->sizey);
511                 wm_window_raise(win);
512         }
513         
514         /* add new screen? */
515         if (win->screen == NULL)
516                 win->screen = ED_screen_add(win, CTX_data_scene(C), "temp");
517         win->screen->temp = 1; 
518         
519         /* make window active, and validate/resize */
520         CTX_wm_window_set(C, win);
521         WM_check(C);
522         
523         /* ensure it shows the right spacetype editor */
524         sa = win->screen->areabase.first;
525         CTX_wm_area_set(C, sa);
526         
527         if (type == WM_WINDOW_RENDER) {
528                 ED_area_newspace(C, sa, SPACE_IMAGE);
529         }
530         else {
531                 ED_area_newspace(C, sa, SPACE_USERPREF);
532         }
533         
534         ED_screen_set(C, win->screen);
535         
536         if (sa->spacetype == SPACE_IMAGE)
537                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender Render"));
538         else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
539                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender User Preferences"));
540         else if (sa->spacetype == SPACE_FILE)
541                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender File View"));
542         else
543                 GHOST_SetTitle(win->ghostwin, "Blender");
544 }
545
546
547 /* ****************** Operators ****************** */
548
549 /* operator callback */
550 int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
551 {
552         wm_window_copy(C, CTX_wm_window(C));
553         WM_check(C);
554         
555         WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL);
556         
557         return OPERATOR_FINISHED;
558 }
559
560
561 /* fullscreen operator callback */
562 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
563 {
564         wmWindow *window = CTX_wm_window(C);
565         GHOST_TWindowState state;
566
567         if (G.background)
568                 return OPERATOR_CANCELLED;
569
570         state = GHOST_GetWindowState(window->ghostwin);
571         if (state != GHOST_kWindowStateFullScreen)
572                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
573         else
574                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
575
576         return OPERATOR_FINISHED;
577         
578 }
579
580
581 /* ************ events *************** */
582
583 typedef enum {
584         SHIFT    = 's',
585         CONTROL  = 'c',
586         ALT      = 'a',
587         OS       = 'C'
588 } modifierKeyType;
589
590 /* check if specified modifier key type is pressed */
591 static int query_qual(modifierKeyType qual) 
592 {
593         GHOST_TModifierKeyMask left, right;
594         int val = 0;
595         
596         switch (qual) {
597                 case SHIFT:
598                         left = GHOST_kModifierKeyLeftShift;
599                         right = GHOST_kModifierKeyRightShift;
600                         break;
601                 case CONTROL:
602                         left = GHOST_kModifierKeyLeftControl;
603                         right = GHOST_kModifierKeyRightControl;
604                         break;
605                 case OS:
606                         left = right = GHOST_kModifierKeyOS;
607                         break;
608                 case ALT:
609                 default:
610                         left = GHOST_kModifierKeyLeftAlt;
611                         right = GHOST_kModifierKeyRightAlt;
612                         break;
613         }
614         
615         GHOST_GetModifierKeyState(g_system, left, &val);
616         if (!val)
617                 GHOST_GetModifierKeyState(g_system, right, &val);
618         
619         return val;
620 }
621
622 void wm_window_make_drawable(bContext *C, wmWindow *win) 
623 {
624         wmWindowManager *wm = CTX_wm_manager(C);
625
626         if (win != wm->windrawable && win->ghostwin) {
627 //              win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
628                 
629                 wm->windrawable = win;
630                 if (G.debug & G_DEBUG_EVENTS) {
631                         printf("%s: set drawable %d\n", __func__, win->winid);
632                 }
633                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
634         }
635 }
636
637 /* called by ghost, here we handle events for windows themselves or send to event system */
638 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
639 {
640         bContext *C = C_void_ptr;
641         wmWindowManager *wm = CTX_wm_manager(C);
642         GHOST_TEventType type = GHOST_GetEventType(evt);
643         int time = GHOST_GetEventTime(evt);
644         
645         if (type == GHOST_kEventQuit) {
646                 WM_exit(C);
647         }
648         else {
649                 GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt);
650                 GHOST_TEventDataPtr data = GHOST_GetEventData(evt);
651                 wmWindow *win;
652                 
653                 if (!ghostwin) {
654                         /* XXX - should be checked, why are we getting an event here, and */
655                         /* what is it? */
656                         puts("<!> event has no window");
657                         return 1;
658                 }
659                 else if (!GHOST_ValidWindow(g_system, ghostwin)) {
660                         /* XXX - should be checked, why are we getting an event here, and */
661                         /* what is it? */
662                         puts("<!> event has invalid window");                   
663                         return 1;
664                 }
665                 else {
666                         win = GHOST_GetWindowUserData(ghostwin);
667                 }
668                 
669                 switch (type) {
670                         case GHOST_kEventWindowDeactivate:
671                                 wm_event_add_ghostevent(wm, win, type, time, data);
672                                 win->active = 0; /* XXX */
673                                 break;
674                         case GHOST_kEventWindowActivate: 
675                         {
676                                 GHOST_TEventKeyData kdata;
677                                 wmEvent event;
678                                 int cx, cy, wx, wy;
679                                 
680                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
681                                 
682                                 win->active = 1;
683 //                              window_handle(win, INPUTCHANGE, win->active);
684                                 
685                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
686                                 kdata.ascii = '\0';
687                                 kdata.utf8_buf[0] = '\0';
688                                 if (win->eventstate->shift && !query_qual(SHIFT)) {
689                                         kdata.key = GHOST_kKeyLeftShift;
690                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
691                                 }
692                                 if (win->eventstate->ctrl && !query_qual(CONTROL)) {
693                                         kdata.key = GHOST_kKeyLeftControl;
694                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
695                                 }
696                                 if (win->eventstate->alt && !query_qual(ALT)) {
697                                         kdata.key = GHOST_kKeyLeftAlt;
698                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
699                                 }
700                                 if (win->eventstate->oskey && !query_qual(OS)) {
701                                         kdata.key = GHOST_kKeyOS;
702                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
703                                 }
704                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
705                                 win->eventstate->keymodifier = 0;
706                                 
707                                 /* entering window, update mouse pos. but no event */
708                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
709                                 
710                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
711                                 win->eventstate->x = cx;
712                                 win->eventstate->y = (win->sizey - 1) - cy;
713                                 
714                                 win->addmousemove = 1;   /* enables highlighted buttons */
715                                 
716                                 wm_window_make_drawable(C, win);
717
718                                 /* window might be focused by mouse click in configuration of window manager
719                                  * when focus is not following mouse
720                                  * click could have been done on a button and depending on window manager settings
721                                  * click would be passed to blender or not, but in any case button under cursor
722                                  * should be activated, so at max next click on button without moving mouse
723                                  * would trigger it's handle function
724                                  * currently it seems to be common practice to generate new event for, but probably
725                                  * we'll need utility function for this? (sergey)
726                                  */
727                                 event = *(win->eventstate);
728                                 event.type = MOUSEMOVE;
729                                 event.prevx = event.x;
730                                 event.prevy = event.y;
731
732                                 wm_event_add(win, &event);
733
734                                 break;
735                         }
736                         case GHOST_kEventWindowClose: {
737                                 wm_window_close(C, wm, win);
738                                 break;
739                         }
740                         case GHOST_kEventWindowUpdate: {
741                                 if (G.debug & G_DEBUG_EVENTS) {
742                                         printf("%s: ghost redraw %d\n", __func__, win->winid);
743                                 }
744                                 
745                                 wm_window_make_drawable(C, win);
746                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
747
748                                 break;
749                         }
750                         case GHOST_kEventWindowSize:
751                         case GHOST_kEventWindowMove: {
752                                 GHOST_TWindowState state;
753                                 state = GHOST_GetWindowState(win->ghostwin);
754                                 win->windowstate = state;
755
756                                 /* win32: gives undefined window size when minimized */
757                                 if (state != GHOST_kWindowStateMinimized) {
758                                         GHOST_RectangleHandle client_rect;
759                                         int l, t, r, b, scr_w, scr_h;
760                                         int sizex, sizey, posx, posy;
761                                         
762                                         client_rect = GHOST_GetClientBounds(win->ghostwin);
763                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
764                                         
765                                         GHOST_DisposeRectangle(client_rect);
766                                         
767                                         wm_get_screensize(&scr_w, &scr_h);
768                                         sizex = r - l;
769                                         sizey = b - t;
770                                         posx = l;
771                                         posy = scr_h - t - win->sizey;
772
773                                         /*
774                                          * Ghost sometimes send size or move events when the window hasn't changed.
775                                          * One case of this is using compiz on linux. To alleviate the problem
776                                          * we ignore all such event here.
777                                          * 
778                                          * It might be good to eventually do that at Ghost level, but that is for 
779                                          * another time.
780                                          */
781                                         if (win->sizex != sizex ||
782                                             win->sizey != sizey ||
783                                             win->posx != posx ||
784                                             win->posy != posy)
785                                         {
786                                                 win->sizex = sizex;
787                                                 win->sizey = sizey;
788                                                 win->posx = posx;
789                                                 win->posy = posy;
790
791                                                 /* debug prints */
792                                                 if (G.debug & G_DEBUG_EVENTS) {
793                                                         const char *state_str;
794                                                         state = GHOST_GetWindowState(win->ghostwin);
795
796                                                         if (state == GHOST_kWindowStateNormal) {
797                                                                 state_str = "normal";
798                                                         }
799                                                         else if (state == GHOST_kWindowStateMinimized) {
800                                                                 state_str = "minimized";
801                                                         }
802                                                         else if (state == GHOST_kWindowStateMaximized) {
803                                                                 state_str = "maximized";
804                                                         }
805                                                         else if (state == GHOST_kWindowStateFullScreen) {
806                                                                 state_str = "fullscreen";
807                                                         }
808                                                         else {
809                                                                 state_str = "<unknown>";
810                                                         }
811
812                                                         printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
813
814                                                         if (type != GHOST_kEventWindowSize) {
815                                                                 printf("win move event pos %d %d size %d %d\n",
816                                                                        win->posx, win->posy, win->sizex, win->sizey);
817                                                         }
818                                                 }
819                                         
820                                                 wm_window_make_drawable(C, win);
821                                                 wm_draw_window_clear(win);
822                                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
823                                                 WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
824                                         }
825                                 }
826                                 break;
827                         }
828                                 
829                         case GHOST_kEventOpenMainFile:
830                         {
831                                 PointerRNA props_ptr;
832                                 wmWindow *oldWindow;
833                                 char *path = GHOST_GetEventData(evt);
834                                 
835                                 if (path) {
836                                         /* operator needs a valid window in context, ensures
837                                          * it is correctly set */
838                                         oldWindow = CTX_wm_window(C);
839                                         CTX_wm_window_set(C, win);
840                                         
841                                         WM_operator_properties_create(&props_ptr, "WM_OT_open_mainfile");
842                                         RNA_string_set(&props_ptr, "filepath", path);
843                                         WM_operator_name_call(C, "WM_OT_open_mainfile", WM_OP_EXEC_DEFAULT, &props_ptr);
844                                         WM_operator_properties_free(&props_ptr);
845                                         
846                                         CTX_wm_window_set(C, oldWindow);
847                                 }
848                                 break;
849                         }
850                         case GHOST_kEventDraggingDropDone:
851                         {
852                                 wmEvent event;
853                                 GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
854                                 int cx, cy, wx, wy;
855                                 
856                                 /* entering window, update mouse pos */
857                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
858                                 
859                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
860                                 win->eventstate->x = cx;
861                                 win->eventstate->y = (win->sizey - 1) - cy;
862                                 
863                                 event = *(win->eventstate);  /* copy last state, like mouse coords */
864                                 
865                                 /* activate region */
866                                 event.type = MOUSEMOVE;
867                                 event.prevx = event.x;
868                                 event.prevy = event.y;
869                                 
870                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
871                                 win->active = 1;
872                                 
873                                 wm_event_add(win, &event);
874                                 
875                                 
876                                 /* make blender drop event with custom data pointing to wm drags */
877                                 event.type = EVT_DROP;
878                                 event.val = KM_RELEASE;
879                                 event.custom = EVT_DATA_LISTBASE;
880                                 event.customdata = &wm->drags;
881                                 event.customdatafree = 1;
882                                 
883                                 wm_event_add(win, &event);
884                                 
885                                 /* printf("Drop detected\n"); */
886                                 
887                                 /* add drag data to wm for paths: */
888                                 
889                                 if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
890                                         GHOST_TStringArray *stra = ddd->data;
891                                         int a, icon;
892                                         
893                                         for (a = 0; a < stra->count; a++) {
894                                                 printf("drop file %s\n", stra->strings[a]);
895                                                 /* try to get icon type from extension */
896                                                 icon = ED_file_extension_icon((char *)stra->strings[a]);
897                                                 
898                                                 WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0);
899                                                 /* void poin should point to string, it makes a copy */
900                                                 break; /* only one drop element supported now */
901                                         }
902                                 }
903                                 
904                                 
905                                 
906                                 break;
907                         }
908                         
909                         default:
910                                 wm_event_add_ghostevent(wm, win, type, time, data);
911                                 break;
912                 }
913
914         }
915         return 1;
916 }
917
918
919 /* This timer system only gives maximum 1 timer event per redraw cycle,
920  * to prevent queues to get overloaded.
921  * Timer handlers should check for delta to decide if they just
922  * update, or follow real time.
923  * Timer handlers can also set duration to match frames passed
924  */
925 static int wm_window_timer(const bContext *C)
926 {
927         wmWindowManager *wm = CTX_wm_manager(C);
928         wmTimer *wt, *wtnext;
929         wmWindow *win;
930         double time = PIL_check_seconds_timer();
931         int retval = 0;
932         
933         for (wt = wm->timers.first; wt; wt = wtnext) {
934                 wtnext = wt->next; /* in case timer gets removed */
935                 win = wt->win;
936
937                 if (wt->sleep == 0) {
938                         if (time > wt->ntime) {
939                                 wt->delta = time - wt->ltime;
940                                 wt->duration += wt->delta;
941                                 wt->ltime = time;
942                                 wt->ntime = wt->stime + wt->timestep *ceil(wt->duration / wt->timestep);
943
944                                 if (wt->event_type == TIMERJOBS)
945                                         wm_jobs_timer(C, wm, wt);
946                                 else if (wt->event_type == TIMERAUTOSAVE)
947                                         wm_autosave_timer(C, wm, wt);
948                                 else if (win) {
949                                         wmEvent event = *(win->eventstate);
950                                         
951                                         event.type = wt->event_type;
952                                         event.custom = EVT_DATA_TIMER;
953                                         event.customdata = wt;
954                                         wm_event_add(win, &event);
955
956                                         retval = 1;
957                                 }
958                         }
959                 }
960         }
961         return retval;
962 }
963
964 void wm_window_process_events(const bContext *C) 
965 {
966         int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
967         
968         if (hasevent)
969                 GHOST_DispatchEvents(g_system);
970         
971         hasevent |= wm_window_timer(C);
972
973         /* no event, we sleep 5 milliseconds */
974         if (hasevent == 0)
975                 PIL_sleep_ms(5);
976 }
977
978 void wm_window_process_events_nosleep(void) 
979 {
980         if (GHOST_ProcessEvents(g_system, 0))
981                 GHOST_DispatchEvents(g_system);
982 }
983
984 /* exported as handle callback to bke blender.c */
985 void wm_window_testbreak(void)
986 {
987         static double ltime = 0;
988         double curtime = PIL_check_seconds_timer();
989         
990         /* only check for breaks every 50 milliseconds
991          * if we get called more often.
992          */
993         if ((curtime - ltime) > 0.05) {
994                 int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
995                 
996                 if (hasevent)
997                         GHOST_DispatchEvents(g_system);
998                 
999                 ltime = curtime;
1000         }
1001 }
1002
1003 /* **************** init ********************** */
1004
1005 void wm_ghost_init(bContext *C)
1006 {
1007         if (!g_system) {
1008                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
1009                 
1010                 g_system = GHOST_CreateSystem();
1011                 GHOST_AddEventConsumer(g_system, consumer);
1012         }       
1013 }
1014
1015 void wm_ghost_exit(void)
1016 {
1017         if (g_system)
1018                 GHOST_DisposeSystem(g_system);
1019
1020         g_system = NULL;
1021 }
1022
1023 /* **************** timer ********************** */
1024
1025 /* to (de)activate running timers temporary */
1026 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer, int dosleep)
1027 {
1028         wmTimer *wt;
1029         
1030         for (wt = wm->timers.first; wt; wt = wt->next)
1031                 if (wt == timer)
1032                         break;
1033
1034         if (wt)
1035                 wt->sleep = dosleep;
1036 }
1037
1038 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
1039 {
1040         wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1041         
1042         wt->event_type = event_type;
1043         wt->ltime = PIL_check_seconds_timer();
1044         wt->ntime = wt->ltime + timestep;
1045         wt->stime = wt->ltime;
1046         wt->timestep = timestep;
1047         wt->win = win;
1048         
1049         BLI_addtail(&wm->timers, wt);
1050         
1051         return wt;
1052 }
1053
1054 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
1055 {
1056         wmTimer *wt;
1057         
1058         /* extra security check */
1059         for (wt = wm->timers.first; wt; wt = wt->next)
1060                 if (wt == timer)
1061                         break;
1062         if (wt) {
1063                 if (wm->reports.reporttimer == wt)
1064                         wm->reports.reporttimer = NULL;
1065                 
1066                 BLI_remlink(&wm->timers, wt);
1067                 if (wt->customdata)
1068                         MEM_freeN(wt->customdata);
1069                 MEM_freeN(wt);
1070         }
1071 }
1072
1073 /* ******************* clipboard **************** */
1074
1075 char *WM_clipboard_text_get(int selection)
1076 {
1077         char *p, *p2, *buf, *newbuf;
1078
1079         if (G.background)
1080                 return NULL;
1081
1082         buf = (char *)GHOST_getClipboard(selection);
1083         if (!buf)
1084                 return NULL;
1085         
1086         /* always convert from \r\n to \n */
1087         newbuf = MEM_callocN(strlen(buf) + 1, "WM_clipboard_text_get");
1088
1089         for (p = buf, p2 = newbuf; *p; p++) {
1090                 if (*p != '\r')
1091                         *(p2++) = *p;
1092         }
1093         *p2 = '\0';
1094
1095         free(buf); /* ghost uses regular malloc */
1096         
1097         return newbuf;
1098 }
1099
1100 void WM_clipboard_text_set(char *buf, int selection)
1101 {
1102         if (!G.background) {
1103 #ifdef _WIN32
1104                 /* do conversion from \n to \r\n on Windows */
1105                 char *p, *p2, *newbuf;
1106                 int newlen = 0;
1107                 
1108                 for (p = buf; *p; p++) {
1109                         if (*p == '\n')
1110                                 newlen += 2;
1111                         else
1112                                 newlen++;
1113                 }
1114                 
1115                 newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
1116         
1117                 for (p = buf, p2 = newbuf; *p; p++, p2++) {
1118                         if (*p == '\n') {
1119                                 *(p2++) = '\r'; *p2 = '\n';
1120                         }
1121                         else *p2 = *p;
1122                 }
1123                 *p2 = '\0';
1124         
1125                 GHOST_putClipboard((GHOST_TInt8 *)newbuf, selection);
1126                 MEM_freeN(newbuf);
1127 #else
1128                 GHOST_putClipboard((GHOST_TInt8 *)buf, selection);
1129 #endif
1130         }
1131 }
1132
1133 /* ******************* progress bar **************** */
1134
1135 void WM_progress_set(wmWindow *win, float progress)
1136 {
1137         GHOST_SetProgressBar(win->ghostwin, progress);
1138 }
1139
1140 void WM_progress_clear(wmWindow *win)
1141 {
1142         GHOST_EndProgressBar(win->ghostwin);
1143 }
1144
1145 /* ************************************ */
1146
1147 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
1148 {
1149         *posx_r = win->posx;
1150         *posy_r = win->posy;
1151 }
1152
1153 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r) 
1154 {
1155         *width_r = win->sizex;
1156         *height_r = win->sizey;
1157 }
1158
1159 /* exceptional case: - splash is called before events are processed
1160  * this means we don't actually know the window size so get this from GHOST */
1161 void wm_window_get_size_ghost(wmWindow *win, int *width_r, int *height_r)
1162 {
1163         GHOST_RectangleHandle bounds = GHOST_GetClientBounds(win->ghostwin);
1164         *width_r = GHOST_GetWidthRectangle(bounds);
1165         *height_r = GHOST_GetHeightRectangle(bounds);
1166         
1167         GHOST_DisposeRectangle(bounds);
1168 }
1169
1170 void wm_window_set_size(wmWindow *win, int width, int height) 
1171 {
1172         GHOST_SetClientSize(win->ghostwin, width, height);
1173 }
1174
1175 void wm_window_lower(wmWindow *win) 
1176 {
1177         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
1178 }
1179
1180 void wm_window_raise(wmWindow *win) 
1181 {
1182         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
1183 }
1184
1185 void wm_window_swap_buffers(wmWindow *win)
1186 {
1187         
1188 #ifdef WIN32
1189         glDisable(GL_SCISSOR_TEST);
1190         GHOST_SwapWindowBuffers(win->ghostwin);
1191         glEnable(GL_SCISSOR_TEST);
1192 #else
1193         GHOST_SwapWindowBuffers(win->ghostwin);
1194 #endif
1195 }
1196
1197 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
1198 {
1199         GHOST_GetCursorPosition(g_system, x, y);
1200         GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
1201         *y = (win->sizey - 1) - *y;
1202 }
1203
1204 /* ******************* exported api ***************** */
1205
1206
1207 /* called whem no ghost system was initialized */
1208 void WM_setprefsize(int stax, int stay, int sizx, int sizy)
1209 {
1210         wm_init_state.start_x = stax; /* left hand pos */
1211         wm_init_state.start_y = stay; /* bottom pos */
1212         wm_init_state.size_x = sizx;
1213         wm_init_state.size_y = sizy;
1214         wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
1215 }
1216
1217 /* for borderless and border windows set from command-line */
1218 void WM_setinitialstate_fullscreen(void)
1219 {
1220         wm_init_state.windowstate = GHOST_kWindowStateFullScreen;
1221         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1222 }
1223
1224 void WM_setinitialstate_normal(void)
1225 {
1226         wm_init_state.windowstate = GHOST_kWindowStateNormal;
1227         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1228 }
1229
1230 /* This function requires access to the GHOST_SystemHandle (g_system) */
1231 void WM_cursor_warp(wmWindow *win, int x, int y)
1232 {
1233         if (win && win->ghostwin) {
1234                 int oldx = x, oldy = y;
1235
1236                 y = win->sizey - y - 1;
1237
1238                 GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
1239                 GHOST_SetCursorPosition(g_system, x, y);
1240
1241                 win->eventstate->prevx = oldx;
1242                 win->eventstate->prevy = oldy;
1243         }
1244 }
1245