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