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