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