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