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