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