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