merge with/from trunk at r35190
[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 "BKE_blender.h"
52 #include "BKE_context.h"
53 #include "BKE_library.h"
54 #include "BKE_global.h"
55 #include "BKE_main.h"
56
57
58 #include "BIF_gl.h"
59
60 #include "WM_api.h"
61 #include "WM_types.h"
62 #include "wm.h"
63 #include "wm_draw.h"
64 #include "wm_window.h"
65 #include "wm_subwindow.h"
66 #include "wm_event_system.h"
67
68 #include "ED_screen.h"
69 #include "ED_fileselect.h"
70
71 #include "PIL_time.h"
72
73 #include "GPU_draw.h"
74 #include "GPU_extensions.h"
75
76 /* the global to talk to ghost */
77 GHOST_SystemHandle g_system= NULL;
78
79 /* set by commandline */
80 static int prefsizx= 0, prefsizy= 0, prefstax= 0, prefstay= 0, initialstate= GHOST_kWindowStateNormal;
81 static unsigned short useprefsize= 0;
82
83 /* ******** win open & close ************ */
84
85 /* XXX this one should correctly check for apple top header...
86  done for Cocoa : returns window contents (and not frame) max size*/
87 void wm_get_screensize(int *width_r, int *height_r)
88 {
89         unsigned int uiwidth;
90         unsigned int uiheight;
91         
92         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
93         *width_r= uiwidth;
94         *height_r= uiheight;
95 }
96
97 /* keeps offset and size within monitor bounds */
98 /* XXX solve dual screen... */
99 static void wm_window_check_position(rcti *rect)
100 {
101         int width, height, d;
102         
103         wm_get_screensize(&width, &height);
104         
105 #if defined(__APPLE__) && !defined(GHOST_COCOA)
106         height -= 70;
107 #endif
108         
109         if(rect->xmin < 0) {
110                 d= rect->xmin;
111                 rect->xmax -= d;
112                 rect->xmin -= d;
113         }
114         if(rect->ymin < 0) {
115                 d= rect->ymin;
116                 rect->ymax -= d;
117                 rect->ymin -= d;
118         }
119         if(rect->xmax > width) {
120                 d= rect->xmax - width;
121                 rect->xmax -= d;
122                 rect->xmin -= d;
123         }
124         if(rect->ymax > height) {
125                 d= rect->ymax - height;
126                 rect->ymax -= d;
127                 rect->ymin -= d;
128         }
129         
130         if(rect->xmin < 0) rect->xmin= 0;
131         if(rect->ymin < 0) rect->ymin= 0;
132 }
133
134
135 static void wm_ghostwindow_destroy(wmWindow *win) 
136 {
137         if(win->ghostwin) {
138                 GHOST_DisposeWindow(g_system, win->ghostwin);
139                 win->ghostwin= NULL;
140         }
141 }
142
143 /* including window itself, C can be NULL. 
144    ED_screen_exit should have been called */
145 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
146 {
147         wmTimer *wt, *wtnext;
148         
149         /* update context */
150         if(C) {
151                 WM_event_remove_handlers(C, &win->handlers);
152                 WM_event_remove_handlers(C, &win->modalhandlers);
153
154                 if(CTX_wm_window(C)==win)
155                         CTX_wm_window_set(C, NULL);
156         }       
157
158         /* always set drawable and active to NULL, prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
159         wm->windrawable= NULL;
160         wm->winactive= NULL;
161
162         /* end running jobs, a job end also removes its timer */
163         for(wt= wm->timers.first; wt; wt= wtnext) {
164                 wtnext= wt->next;
165                 if(wt->win==win && wt->event_type==TIMERJOBS)
166                         wm_jobs_timer_ended(wm, wt);
167         }
168         
169         /* timer removing, need to call this api function */
170         for(wt= wm->timers.first; wt; wt=wtnext) {
171                 wtnext= wt->next;
172                 if(wt->win==win)
173                         WM_event_remove_timer(wm, win, wt);
174         }
175
176         if(win->eventstate) MEM_freeN(win->eventstate);
177         
178         wm_event_free_all(win);
179         wm_subwindows_free(win);
180         
181         if(win->drawdata)
182                 MEM_freeN(win->drawdata);
183         
184         wm_ghostwindow_destroy(win);
185         
186         MEM_freeN(win);
187 }
188
189 static int find_free_winid(wmWindowManager *wm)
190 {
191         wmWindow *win;
192         int id= 1;
193         
194         for(win= wm->windows.first; win; win= win->next)
195                 if(id <= win->winid)
196                         id= win->winid+1;
197         
198         return id;
199 }
200
201 /* dont change context itself */
202 wmWindow *wm_window_new(bContext *C)
203 {
204         wmWindowManager *wm= CTX_wm_manager(C);
205         wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
206         
207         BLI_addtail(&wm->windows, win);
208         win->winid= find_free_winid(wm);
209
210         return win;
211 }
212
213
214 /* part of wm_window.c api */
215 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
216 {
217         wmWindow *win= wm_window_new(C);
218         
219         win->posx= winorig->posx+10;
220         win->posy= winorig->posy;
221         win->sizex= winorig->sizex;
222         win->sizey= winorig->sizey;
223         
224         /* duplicate assigns to window */
225         win->screen= ED_screen_duplicate(win, winorig->screen);
226         BLI_strncpy(win->screenname, win->screen->id.name+2, sizeof(win->screenname));
227         win->screen->winid= win->winid;
228
229         win->screen->do_refresh= 1;
230         win->screen->do_draw= 1;
231
232         win->drawmethod= -1;
233         win->drawdata= NULL;
234         
235         return win;
236 }
237
238 /* this is event from ghost, or exit-blender op */
239 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
240 {
241         BLI_remlink(&wm->windows, win);
242         
243         wm_draw_window_clear(win);
244         CTX_wm_window_set(C, win);      /* needed by handlers */
245         WM_event_remove_handlers(C, &win->handlers);
246         WM_event_remove_handlers(C, &win->modalhandlers);
247         ED_screen_exit(C, win, win->screen); 
248         
249         /* if temp screen, delete it */
250         if(win->screen->temp) {
251                 Main *bmain= CTX_data_main(C);
252                 free_libblock(&bmain->screen, win->screen);
253         }
254         
255         wm_window_free(C, wm, win);
256         
257         /* check remaining windows */
258         if(wm->windows.first) {
259                 for(win= wm->windows.first; win; win= win->next)
260                         if(win->screen->temp == 0)
261                                 break;
262                 /* in this case we close all */
263                 if(win==NULL)
264                         WM_exit(C);
265         }
266         else
267                 WM_exit(C);
268 }
269
270 void wm_window_title(wmWindowManager *wm, wmWindow *win)
271 {
272         /* handle the 'temp' window */
273         if(win->screen && win->screen->temp) {
274                 GHOST_SetTitle(win->ghostwin, "Blender");
275         }
276         else {
277                 
278                 /* this is set to 1 if you don't have startup.blend open */
279                 if(G.save_over && G.main->name[0]) {
280                         char str[sizeof(G.main->name) + 12];
281                         BLI_snprintf(str, sizeof(str), "Blender%s [%s]", wm->file_saved ? "":"*", G.main->name);
282                         GHOST_SetTitle(win->ghostwin, str);
283                 }
284                 else
285                         GHOST_SetTitle(win->ghostwin, "Blender");
286
287                 /* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
288                  and to give hint of unsaved changes for a user warning mechanism
289                  in case of OS application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end) */
290                 GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8)!wm->file_saved);
291                 
292 #if defined(__APPLE__) && !defined(GHOST_COCOA)
293                 if(wm->file_saved)
294                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
295                 else
296                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
297 #endif
298         }
299 }
300
301 /* belongs to below */
302 static void wm_window_add_ghostwindow(bContext *C, const char *title, wmWindow *win)
303 {
304         GHOST_WindowHandle ghostwin;
305         int scr_w, scr_h, posy;
306         GHOST_TWindowState initial_state;
307
308         /* when there is no window open uses the initial state */
309         if(!CTX_wm_window(C))
310                 initial_state= initialstate;
311         else
312                 initial_state= GHOST_kWindowStateNormal;
313         
314         wm_get_screensize(&scr_w, &scr_h);
315         posy= (scr_h - win->posy - win->sizey);
316         
317 #if defined(__APPLE__) && !defined(GHOST_COCOA)
318         {
319                 extern int macPrefState; /* creator.c */
320                 initial_state += macPrefState;
321         }
322 #endif
323         /* Disable AA for now, as GL_SELECT (used for border, lasso, ... select)
324          doesn't work well when AA is initialized, even if not used. */
325         ghostwin= GHOST_CreateWindow(g_system, title, 
326                                                                  win->posx, posy, win->sizex, win->sizey, 
327                                                                  initial_state, 
328                                                                  GHOST_kDrawingContextTypeOpenGL,
329                                                                  0 /* no stereo */,
330                                                                  0 /* no AA */);
331         
332         if (ghostwin) {
333                 /* needed so we can detect the graphics card below */
334                 GPU_extensions_init();
335                 
336                 /* set the state*/
337                 GHOST_SetWindowState(ghostwin, initial_state);
338
339                 win->ghostwin= ghostwin;
340                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
341                 
342                 if(win->eventstate==NULL)
343                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
344                 
345                 /* until screens get drawn, make it nice grey */
346                 glClearColor(.55, .55, .55, 0.0);
347                 /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
348                 if(!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
349                         glClear(GL_COLOR_BUFFER_BIT);
350                 }
351
352                 wm_window_swap_buffers(win);
353                 
354                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
355                 
356                 /* standard state vars for window */
357                 glEnable(GL_SCISSOR_TEST);
358                 
359                 GPU_state_init();
360         }
361 }
362
363 /* for wmWindows without ghostwin, open these and clear */
364 /* window size is read from window, if 0 it uses prefsize */
365 /* called in WM_check, also inits stuff after file read */
366 void wm_window_add_ghostwindows(bContext* C, wmWindowManager *wm)
367 {
368         wmKeyMap *keymap;
369         wmWindow *win;
370         
371         /* no commandline prefsize? then we set this.
372          * Note that these values will be used only
373          * when there is no startup.blend yet.
374          */
375         if (!prefsizx) {
376                 wm_get_screensize(&prefsizx, &prefsizy);
377                 
378 #if defined(__APPLE__) && !defined(GHOST_COCOA)
379 //Cocoa provides functions to get correct max window size
380                 {
381                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
382                         
383                         wm_set_apple_prefsize(prefsizx, prefsizy);
384                 }
385 #else
386                 prefstax= 0;
387                 prefstay= 0;
388                 
389 #endif
390         }
391         
392         for(win= wm->windows.first; win; win= win->next) {
393                 if(win->ghostwin==NULL) {
394                         if(win->sizex==0 || useprefsize) {
395                                 win->posx= prefstax;
396                                 win->posy= prefstay;
397                                 win->sizex= prefsizx;
398                                 win->sizey= prefsizy;
399                                 win->windowstate= initialstate;
400                                 useprefsize= 0;
401                         }
402                         wm_window_add_ghostwindow(C, "Blender", win);
403                 }
404                 /* happens after fileread */
405                 if(win->eventstate==NULL)
406                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
407
408                 /* add keymap handlers (1 handler for all keys in map!) */
409                 keymap= WM_keymap_find(wm->defaultconf, "Window", 0, 0);
410                 WM_event_add_keymap_handler(&win->handlers, keymap);
411                 
412                 keymap= WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
413                 WM_event_add_keymap_handler(&win->handlers, keymap);
414
415                 keymap= WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
416                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
417                 
418                 /* add drop boxes */
419                 {
420                         ListBase *lb= WM_dropboxmap_find("Window", 0, 0);
421                         WM_event_add_dropbox_handler(&win->handlers, lb);
422                 }
423                 wm_window_title(wm, win);
424         }
425 }
426
427 /* new window, no screen yet, but we open ghostwindow for it */
428 /* also gets the window level handlers */
429 /* area-rip calls this */
430 wmWindow *WM_window_open(bContext *C, rcti *rect)
431 {
432         wmWindow *win= wm_window_new(C);
433         
434         win->posx= rect->xmin;
435         win->posy= rect->ymin;
436         win->sizex= rect->xmax - rect->xmin;
437         win->sizey= rect->ymax - rect->ymin;
438
439         win->drawmethod= -1;
440         win->drawdata= NULL;
441         
442         WM_check(C);
443         
444         return win;
445 }
446
447 /* uses screen->temp tag to define what to do, currently it limits
448    to only one "temp" window for render out, preferences, filewindow, etc */
449 /* type is #define in WM_api.h */
450
451 void WM_window_open_temp(bContext *C, rcti *position, int type)
452 {
453         wmWindow *win;
454         ScrArea *sa;
455         
456         /* changes rect to fit within desktop */
457         wm_window_check_position(position);
458         
459         /* test if we have a temp screen already */
460         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next)
461                 if(win->screen->temp)
462                         break;
463         
464         /* add new window? */
465         if(win==NULL) {
466                 win= wm_window_new(C);
467                 
468                 win->posx= position->xmin;
469                 win->posy= position->ymin;
470         }
471         
472         win->sizex= position->xmax - position->xmin;
473         win->sizey= position->ymax - position->ymin;
474         
475         if(win->ghostwin) {
476                 wm_window_set_size(win, win->sizex, win->sizey) ;
477                 wm_window_raise(win);
478         }
479         
480         /* add new screen? */
481         if(win->screen==NULL)
482                 win->screen= ED_screen_add(win, CTX_data_scene(C), "temp");
483         win->screen->temp = 1; 
484         
485         /* make window active, and validate/resize */
486         CTX_wm_window_set(C, win);
487         WM_check(C);
488         
489         /* ensure it shows the right spacetype editor */
490         sa= win->screen->areabase.first;
491         CTX_wm_area_set(C, sa);
492         
493         if(type==WM_WINDOW_RENDER) {
494                 ED_area_newspace(C, sa, SPACE_IMAGE);
495         }
496         else {
497                 ED_area_newspace(C, sa, SPACE_USERPREF);
498         }
499         
500         ED_screen_set(C, win->screen);
501         
502         if(sa->spacetype==SPACE_IMAGE)
503                 GHOST_SetTitle(win->ghostwin, "Blender Render");
504         else if(ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
505                 GHOST_SetTitle(win->ghostwin, "Blender User Preferences");
506         else if(sa->spacetype==SPACE_FILE)
507                 GHOST_SetTitle(win->ghostwin, "Blender File View");
508         else
509                 GHOST_SetTitle(win->ghostwin, "Blender");
510 }
511
512
513 /* ****************** Operators ****************** */
514
515 /* operator callback */
516 int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
517 {
518         wm_window_copy(C, CTX_wm_window(C));
519         WM_check(C);
520         
521         WM_event_add_notifier(C, NC_WINDOW|NA_ADDED, NULL);
522         
523         return OPERATOR_FINISHED;
524 }
525
526
527 /* fullscreen operator callback */
528 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
529 {
530         wmWindow *window= CTX_wm_window(C);
531         GHOST_TWindowState state = GHOST_GetWindowState(window->ghostwin);
532         if(state!=GHOST_kWindowStateFullScreen)
533                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
534         else
535                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
536
537         return OPERATOR_FINISHED;
538         
539 }
540
541
542 /* ************ events *************** */
543
544 typedef enum
545 {
546         SHIFT = 's',
547         CONTROL = 'c',
548         ALT = 'a',
549         OS = 'C'
550 } modifierKeyType;
551
552 /* check if specified modifier key type is pressed */
553 static int query_qual(modifierKeyType qual) 
554 {
555         GHOST_TModifierKeyMask left, right;
556         int val= 0;
557         
558         switch(qual) {
559                 case SHIFT:
560                         left= GHOST_kModifierKeyLeftShift;
561                         right= GHOST_kModifierKeyRightShift;
562                         break;
563                 case CONTROL:
564                         left= GHOST_kModifierKeyLeftControl;
565                         right= GHOST_kModifierKeyRightControl;
566                         break;
567                 case OS:
568                         left= right= GHOST_kModifierKeyOS;
569                         break;
570                 case ALT:
571                 default:
572                         left= GHOST_kModifierKeyLeftAlt;
573                         right= GHOST_kModifierKeyRightAlt;
574                         break;
575         }
576         
577         GHOST_GetModifierKeyState(g_system, left, &val);
578         if (!val)
579                 GHOST_GetModifierKeyState(g_system, right, &val);
580         
581         return val;
582 }
583
584 void wm_window_make_drawable(bContext *C, wmWindow *win) 
585 {
586         wmWindowManager *wm= CTX_wm_manager(C);
587
588         if (win != wm->windrawable && win->ghostwin) {
589 //              win->lmbut= 0;  /* keeps hanging when mousepressed while other window opened */
590                 
591                 wm->windrawable= win;
592                 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
593                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
594         }
595 }
596
597 /* called by ghost, here we handle events for windows themselves or send to event system */
598 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private) 
599 {
600         bContext *C= private;
601         wmWindowManager *wm= CTX_wm_manager(C);
602         GHOST_TEventType type= GHOST_GetEventType(evt);
603         int time= GHOST_GetEventTime(evt);
604         
605         if (type == GHOST_kEventQuit) {
606                 WM_exit(C);
607         } else {
608                 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
609                 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
610                 wmWindow *win;
611                 
612                 if (!ghostwin) {
613                         // XXX - should be checked, why are we getting an event here, and
614                         //      what is it?
615                         
616                         return 1;
617                 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
618                         // XXX - should be checked, why are we getting an event here, and
619                         //      what is it?
620                         
621                         return 1;
622                 } else {
623                         win= GHOST_GetWindowUserData(ghostwin);
624                 }
625                 
626                 switch(type) {
627                         case GHOST_kEventWindowDeactivate:
628                                 wm_event_add_ghostevent(wm, win, type, time, data);
629                                 win->active= 0; /* XXX */
630                                 break;
631                         case GHOST_kEventWindowActivate: 
632                         {
633                                 GHOST_TEventKeyData kdata;
634                                 int cx, cy, wx, wy;
635                                 
636                                 wm->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
637                                 
638                                 win->active= 1;
639 //                              window_handle(win, INPUTCHANGE, win->active);
640                                 
641                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
642                                 kdata.ascii= 0;
643                                 if (win->eventstate->shift && !query_qual(SHIFT)) {
644                                         kdata.key= GHOST_kKeyLeftShift;
645                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
646                                 }
647                                 if (win->eventstate->ctrl && !query_qual(CONTROL)) {
648                                         kdata.key= GHOST_kKeyLeftControl;
649                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
650                                 }
651                                 if (win->eventstate->alt && !query_qual(ALT)) {
652                                         kdata.key= GHOST_kKeyLeftAlt;
653                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
654                                 }
655                                 if (win->eventstate->oskey && !query_qual(OS)) {
656                                         kdata.key= GHOST_kKeyOS;
657                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
658                                 }
659                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
660                                 win->eventstate->keymodifier= 0;
661                                 
662                                 /* entering window, update mouse pos. but no event */
663                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
664                                 
665                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
666                                 win->eventstate->x= cx;
667
668 #if defined(__APPLE__) && defined(GHOST_COCOA)
669                                 //Cocoa already uses coordinates with y=0 at bottom
670                                 win->eventstate->y= cy;
671 #else
672                                 win->eventstate->y= (win->sizey-1) - cy;
673 #endif
674                                 
675                                 win->addmousemove= 1;   /* enables highlighted buttons */
676                                 
677                                 wm_window_make_drawable(C, win);
678                                 break;
679                         }
680                         case GHOST_kEventWindowClose: {
681                                 wm_window_close(C, wm, win);
682                                 break;
683                         }
684                         case GHOST_kEventWindowUpdate: {
685                                 if(G.f & G_DEBUG) printf("ghost redraw\n");
686                                 
687                                 wm_window_make_drawable(C, win);
688                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
689
690                                 break;
691                         }
692                         case GHOST_kEventWindowSize:
693                         case GHOST_kEventWindowMove: {
694                                 GHOST_TWindowState state;
695                                 state = GHOST_GetWindowState(win->ghostwin);
696
697                                  /* win32: gives undefined window size when minimized */
698                                 if(state!=GHOST_kWindowStateMinimized) {
699                                         GHOST_RectangleHandle client_rect;
700                                         int l, t, r, b, scr_w, scr_h;
701                                         int sizex, sizey, posx, posy;
702                                         
703                                         client_rect= GHOST_GetClientBounds(win->ghostwin);
704                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
705                                         
706                                         GHOST_DisposeRectangle(client_rect);
707                                         
708                                         wm_get_screensize(&scr_w, &scr_h);
709                                         sizex= r-l;
710                                         sizey= b-t;
711                                         posx= l;
712                                         posy= scr_h - t - win->sizey;
713
714                                         /*
715                                          * Ghost sometimes send size or move events when the window hasn't changed.
716                                          * One case of this is using compiz on linux. To alleviate the problem
717                                          * we ignore all such event here.
718                                          * 
719                                          * It might be good to eventually do that at Ghost level, but that is for 
720                                          * another time.
721                                          */
722                                         if (win->sizex != sizex ||
723                                                         win->sizey != sizey ||
724                                                         win->posx != posx ||
725                                                         win->posy != posy)
726                                         {
727                                                 win->sizex= sizex;
728                                                 win->sizey= sizey;
729                                                 win->posx= posx;
730                                                 win->posy= posy;
731
732                                                 /* debug prints */
733                                                 if(0) {
734                                                         state = GHOST_GetWindowState(win->ghostwin);
735         
736                                                         if(state==GHOST_kWindowStateNormal) {
737                                                                 if(G.f & G_DEBUG) printf("window state: normal\n");
738                                                         }
739                                                         else if(state==GHOST_kWindowStateMinimized) {
740                                                                 if(G.f & G_DEBUG) printf("window state: minimized\n");
741                                                         }
742                                                         else if(state==GHOST_kWindowStateMaximized) {
743                                                                 if(G.f & G_DEBUG) printf("window state: maximized\n");
744                                                         }
745                                                         else if(state==GHOST_kWindowStateFullScreen) {
746                                                                 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
747                                                         }
748                                                         
749                                                         if(type!=GHOST_kEventWindowSize) {
750                                                                 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
751                                                         }
752                                                         
753                                                 }
754                                         
755                                                 wm_window_make_drawable(C, win);
756                                                 wm_draw_window_clear(win);
757                                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
758                                                 WM_event_add_notifier(C, NC_WINDOW|NA_EDITED, NULL);
759                                         }
760                                 }
761                                 break;
762                         }
763                                 
764                         case GHOST_kEventOpenMainFile:
765                         {
766                                 PointerRNA props_ptr;
767                                 wmWindow *oldWindow;
768                                 char *path = GHOST_GetEventData(evt);
769                                 
770                                 if (path) {
771                                         /* operator needs a valid window in context, ensures
772                                          it is correctly set */
773                                         oldWindow = CTX_wm_window(C);
774                                         CTX_wm_window_set(C, win);
775                                         
776                                         WM_operator_properties_create(&props_ptr, "WM_OT_open_mainfile");
777                                         RNA_string_set(&props_ptr, "filepath", path);
778                                         WM_operator_name_call(C, "WM_OT_open_mainfile", WM_OP_EXEC_DEFAULT, &props_ptr);
779                                         WM_operator_properties_free(&props_ptr);
780                                         
781                                         CTX_wm_window_set(C, oldWindow);
782                                 }
783                                 break;
784                         }
785                         case GHOST_kEventDraggingDropDone:
786                         {
787                                 wmEvent event;
788                                 GHOST_TEventDragnDropData *ddd= GHOST_GetEventData(evt);
789                                 int cx, cy, wx, wy;
790
791                                 
792                                 /* entering window, update mouse pos */
793                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
794                                 
795                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
796                                 win->eventstate->x= cx;
797                                 
798 #if defined(__APPLE__) && defined(GHOST_COCOA)
799                                 //Cocoa already uses coordinates with y=0 at bottom
800                                 win->eventstate->y= cy;
801 #else
802                                 win->eventstate->y= (win->sizey-1) - cy;
803 #endif
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= 0;
909         
910         hasevent = GHOST_ProcessEvents(g_system, 0);    /* 0 is no wait */
911         
912         if(hasevent)
913                 GHOST_DispatchEvents(g_system);
914         
915         hasevent |= wm_window_timer(C);
916
917         /* no event, we sleep 5 milliseconds */
918         if(hasevent==0)
919                 PIL_sleep_ms(5);
920 }
921
922 void wm_window_process_events_nosleep(void) 
923 {
924         if(GHOST_ProcessEvents(g_system, 0))
925                 GHOST_DispatchEvents(g_system);
926 }
927
928 /* exported as handle callback to bke blender.c */
929 void wm_window_testbreak(void)
930 {
931         static double ltime= 0;
932         double curtime= PIL_check_seconds_timer();
933         
934         /* only check for breaks every 50 milliseconds
935                 * if we get called more often.
936                 */
937         if ((curtime-ltime)>.05) {
938                 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
939                 
940                 if(hasevent)
941                         GHOST_DispatchEvents(g_system);
942                 
943                 ltime= curtime;
944         }
945 }
946
947 /* **************** init ********************** */
948
949 void wm_ghost_init(bContext *C)
950 {
951         if (!g_system) {
952                 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
953                 
954                 g_system= GHOST_CreateSystem();
955                 GHOST_AddEventConsumer(g_system, consumer);
956         }       
957 }
958
959 void wm_ghost_exit(void)
960 {
961         if(g_system)
962                 GHOST_DisposeSystem(g_system);
963
964         g_system= NULL;
965 }
966
967 /* **************** timer ********************** */
968
969 /* to (de)activate running timers temporary */
970 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer, int dosleep)
971 {
972         wmTimer *wt;
973         
974         for(wt= wm->timers.first; wt; wt= wt->next)
975                 if(wt==timer)
976                         break;
977
978         if(wt)
979                 wt->sleep= dosleep;
980 }
981
982 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
983 {
984         wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
985         
986         wt->event_type= event_type;
987         wt->ltime= PIL_check_seconds_timer();
988         wt->ntime= wt->ltime + timestep;
989         wt->stime= wt->ltime;
990         wt->timestep= timestep;
991         wt->win= win;
992         
993         BLI_addtail(&wm->timers, wt);
994         
995         return wt;
996 }
997
998 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
999 {
1000         wmTimer *wt;
1001         
1002         /* extra security check */
1003         for(wt= wm->timers.first; wt; wt= wt->next)
1004                 if(wt==timer)
1005                         break;
1006         if(wt) {
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 #if defined(__APPLE__) && defined(GHOST_COCOA)
1144         //Cocoa has silly exception that should be fixed at the ghost level
1145         //(ghost is an allegory for an invisible system specific code)
1146 #else
1147         *y = (win->sizey-1) - *y;
1148 #endif
1149 }
1150
1151 /* ******************* exported api ***************** */
1152
1153
1154 /* called whem no ghost system was initialized */
1155 void WM_setprefsize(int stax, int stay, int sizx, int sizy)
1156 {
1157         prefstax= stax;
1158         prefstay= stay;
1159         prefsizx= sizx;
1160         prefsizy= sizy;
1161         useprefsize= 1;
1162 }
1163
1164 /* for borderless and border windows set from command-line */
1165 void WM_setinitialstate_fullscreen(void)
1166 {
1167         initialstate= GHOST_kWindowStateFullScreen;
1168 }
1169
1170 void WM_setinitialstate_normal(void)
1171 {
1172         initialstate= GHOST_kWindowStateNormal;
1173 }
1174
1175 /* This function requires access to the GHOST_SystemHandle (g_system) */
1176 void WM_cursor_warp(wmWindow *win, int x, int y)
1177 {
1178         if (win && win->ghostwin) {
1179                 int oldx=x, oldy=y;
1180
1181 #if !defined(__APPLE__) || !defined(GHOST_COCOA)
1182                 y= win->sizey -y - 1;
1183 #endif
1184                 GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
1185                 GHOST_SetCursorPosition(g_system, x, y);
1186
1187                 win->eventstate->prevx= oldx;
1188                 win->eventstate->prevy= oldy;
1189         }
1190 }
1191