aaa24957715a92210a032ca843cd7e5ce4264331
[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         /* Disable AA for now, as GL_SELECT (used for border, lasso, ... select)
311          doesn't work well when AA is initialized, even if not used. */
312         ghostwin= GHOST_CreateWindow(g_system, title, 
313                                                                  win->posx, posy, win->sizex, win->sizey, 
314                                                                  inital_state, 
315                                                                  GHOST_kDrawingContextTypeOpenGL,
316                                                                  0 /* no stereo */,
317                                                                  0 /* no AA */);
318         
319         if (ghostwin) {
320                 
321                 win->ghostwin= ghostwin;
322                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
323                 
324                 if(win->eventstate==NULL)
325                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
326                 
327                 /* until screens get drawn, make it nice grey */
328                 glClearColor(.55, .55, .55, 0.0);
329                 glClear(GL_COLOR_BUFFER_BIT);
330                 wm_window_swap_buffers(win);
331                 
332                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
333                 
334                 /* standard state vars for window */
335                 glEnable(GL_SCISSOR_TEST);
336                 
337                 GPU_state_init();
338         }
339 }
340
341 /* for wmWindows without ghostwin, open these and clear */
342 /* window size is read from window, if 0 it uses prefsize */
343 /* called in WM_check, also inits stuff after file read */
344 void wm_window_add_ghostwindows(wmWindowManager *wm)
345 {
346         wmKeyMap *keymap;
347         wmWindow *win;
348         
349         /* no commandline prefsize? then we set this */
350         if (!prefsizx) {
351                 wm_get_screensize(&prefsizx, &prefsizy);
352                 
353 #if defined(__APPLE__) && !defined(GHOST_COCOA)
354 //Cocoa provides functions to get correct max window size
355                 {
356                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
357                         
358                         wm_set_apple_prefsize(prefsizx, prefsizy);
359                 }
360 #else
361                 prefstax= 0;
362                 prefstay= 0;
363                 
364 #endif
365         }
366         
367         for(win= wm->windows.first; win; win= win->next) {
368                 if(win->ghostwin==NULL) {
369                         if(win->sizex==0) {
370                                 win->posx= prefstax;
371                                 win->posy= prefstay;
372                                 win->sizex= prefsizx;
373                                 win->sizey= prefsizy;
374                                 win->windowstate= 0;
375                         }
376                         wm_window_add_ghostwindow(wm, "Blender", win);
377                 }
378                 /* happens after fileread */
379                 if(win->eventstate==NULL)
380                    win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
381
382                 /* add keymap handlers (1 handler for all keys in map!) */
383                 keymap= WM_keymap_find(wm->defaultconf, "Window", 0, 0);
384                 WM_event_add_keymap_handler(&win->handlers, keymap);
385                 
386                 keymap= WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
387                 WM_event_add_keymap_handler(&win->handlers, keymap);
388
389                 keymap= WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
390                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
391                 
392                 wm_window_title(wm, win);
393         }
394 }
395
396 /* new window, no screen yet, but we open ghostwindow for it */
397 /* also gets the window level handlers */
398 /* area-rip calls this */
399 wmWindow *WM_window_open(bContext *C, rcti *rect)
400 {
401         wmWindow *win= wm_window_new(C);
402         
403         win->posx= rect->xmin;
404         win->posy= rect->ymin;
405         win->sizex= rect->xmax - rect->xmin;
406         win->sizey= rect->ymax - rect->ymin;
407
408         win->drawmethod= -1;
409         win->drawdata= NULL;
410         
411         WM_check(C);
412         
413         return win;
414 }
415
416 /* uses screen->full tag to define what to do, currently it limits
417    to only one "temp" window for render out, preferences, filewindow, etc */
418 /* type is #define in WM_api.h */
419
420 void WM_window_open_temp(bContext *C, rcti *position, int type)
421 {
422         wmWindow *win;
423         ScrArea *sa;
424         
425         /* changes rect to fit within desktop */
426         wm_window_check_position(position);
427         
428         /* test if we have a temp screen already */
429         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next)
430                 if(win->screen->full == SCREENTEMP)
431                         break;
432         
433         /* add new window? */
434         if(win==NULL) {
435                 win= wm_window_new(C);
436                 
437                 win->posx= position->xmin;
438                 win->posy= position->ymin;
439         }
440         
441         win->sizex= position->xmax - position->xmin;
442         win->sizey= position->ymax - position->ymin;
443         
444         if(win->ghostwin) {
445                 wm_window_set_size(win, win->sizex, win->sizey) ;
446                 wm_window_raise(win);
447         }
448         
449         /* add new screen? */
450         if(win->screen==NULL)
451                 win->screen= ED_screen_add(win, CTX_data_scene(C), "temp");
452         win->screen->full = SCREENTEMP; 
453         
454         /* make window active, and validate/resize */
455         CTX_wm_window_set(C, win);
456         WM_check(C);
457         
458         /* ensure it shows the right spacetype editor */
459         sa= win->screen->areabase.first;
460         CTX_wm_area_set(C, sa);
461         
462         if(type==WM_WINDOW_RENDER) {
463                 ED_area_newspace(C, sa, SPACE_IMAGE);
464         }
465         else {
466                 ED_area_newspace(C, sa, SPACE_USERPREF);
467         }
468         
469         ED_screen_set(C, win->screen);
470         
471         if(sa->spacetype==SPACE_IMAGE)
472                 GHOST_SetTitle(win->ghostwin, "Blender Render");
473         else if(ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
474                 GHOST_SetTitle(win->ghostwin, "Blender User Preferences");
475         else if(sa->spacetype==SPACE_FILE)
476                 GHOST_SetTitle(win->ghostwin, "Blender File View");
477         else
478                 GHOST_SetTitle(win->ghostwin, "Blender");
479 }
480
481
482 /* ****************** Operators ****************** */
483
484 /* operator callback */
485 int wm_window_duplicate_op(bContext *C, wmOperator *op)
486 {
487         wm_window_copy(C, CTX_wm_window(C));
488         WM_check(C);
489         
490         WM_event_add_notifier(C, NC_WINDOW|NA_ADDED, NULL);
491         
492         return OPERATOR_FINISHED;
493 }
494
495
496 /* fullscreen operator callback */
497 int wm_window_fullscreen_toggle_op(bContext *C, wmOperator *op)
498 {
499         wmWindow *window= CTX_wm_window(C);
500         GHOST_TWindowState state = GHOST_GetWindowState(window->ghostwin);
501         if(state!=GHOST_kWindowStateFullScreen)
502                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
503         else
504                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
505
506         return OPERATOR_FINISHED;
507         
508 }
509
510
511 /* ************ events *************** */
512
513 static int query_qual(char qual) 
514 {
515         GHOST_TModifierKeyMask left, right;
516         int val= 0;
517         
518         if (qual=='s') {
519                 left= GHOST_kModifierKeyLeftShift;
520                 right= GHOST_kModifierKeyRightShift;
521         } else if (qual=='c') {
522                 left= GHOST_kModifierKeyLeftControl;
523                 right= GHOST_kModifierKeyRightControl;
524         } else if (qual=='C') {
525                 left= right= GHOST_kModifierKeyCommand;
526         } else {
527                 left= GHOST_kModifierKeyLeftAlt;
528                 right= GHOST_kModifierKeyRightAlt;
529         }
530         
531         GHOST_GetModifierKeyState(g_system, left, &val);
532         if (!val)
533                 GHOST_GetModifierKeyState(g_system, right, &val);
534         
535         return val;
536 }
537
538 void wm_window_make_drawable(bContext *C, wmWindow *win) 
539 {
540         wmWindowManager *wm= CTX_wm_manager(C);
541
542         if (win != wm->windrawable && win->ghostwin) {
543 //              win->lmbut= 0;  /* keeps hanging when mousepressed while other window opened */
544                 
545                 wm->windrawable= win;
546                 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
547                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
548         }
549 }
550
551 /* called by ghost, here we handle events for windows themselves or send to event system */
552 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private) 
553 {
554         bContext *C= private;
555         wmWindowManager *wm= CTX_wm_manager(C);
556         GHOST_TEventType type= GHOST_GetEventType(evt);
557         
558         if (type == GHOST_kEventQuit) {
559                 WM_exit(C);
560         } else {
561                 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
562                 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
563                 wmWindow *win;
564                 
565                 if (!ghostwin) {
566                         // XXX - should be checked, why are we getting an event here, and
567                         //      what is it?
568                         
569                         return 1;
570                 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
571                         // XXX - should be checked, why are we getting an event here, and
572                         //      what is it?
573                         
574                         return 1;
575                 } else {
576                         win= GHOST_GetWindowUserData(ghostwin);
577                 }
578                 
579                 switch(type) {
580                         case GHOST_kEventWindowDeactivate:
581                                 wm_event_add_ghostevent(win, type, data);
582                                 win->active= 0; /* XXX */
583                                 break;
584                         case GHOST_kEventWindowActivate: 
585                         {
586                                 GHOST_TEventKeyData kdata;
587                                 int cx, cy, wx, wy;
588                                 
589                                 wm->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
590                                 
591                                 win->active= 1;
592 //                              window_handle(win, INPUTCHANGE, win->active);
593                                 
594                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
595                                 kdata.ascii= 0;
596                                 if (win->eventstate->shift && !query_qual('s')) {
597                                         kdata.key= GHOST_kKeyLeftShift;
598                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
599                                 }
600                                 if (win->eventstate->ctrl && !query_qual('c')) {
601                                         kdata.key= GHOST_kKeyLeftControl;
602                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
603                                 }
604                                 if (win->eventstate->alt && !query_qual('a')) {
605                                         kdata.key= GHOST_kKeyLeftAlt;
606                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
607                                 }
608                                 if (win->eventstate->oskey && !query_qual('C')) {
609                                         kdata.key= GHOST_kKeyCommand;
610                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
611                                 }
612                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
613                                 win->eventstate->keymodifier= 0;
614                                 
615                                 /* entering window, update mouse pos. but no event */
616                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
617                                 
618                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
619                                 win->eventstate->x= cx;
620
621 #if defined(__APPLE__) && defined(GHOST_COCOA)
622                                 //Cocoa already uses coordinates with y=0 at bottom
623                                 win->eventstate->y= cy;
624 #else
625                                 win->eventstate->y= (win->sizey-1) - cy;
626 #endif
627                                 
628                                 wm_window_make_drawable(C, win);
629                                 break;
630                         }
631                         case GHOST_kEventWindowClose: {
632                                 wm_window_close(C, wm, win);
633                                 break;
634                         }
635                         case GHOST_kEventWindowUpdate: {
636                                 if(G.f & G_DEBUG) printf("ghost redraw\n");
637                                 
638                                 wm_window_make_drawable(C, win);
639                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
640
641                                 break;
642                         }
643                         case GHOST_kEventWindowSize:
644                         case GHOST_kEventWindowMove: {
645                                 GHOST_TWindowState state;
646                                 state = GHOST_GetWindowState(win->ghostwin);
647
648                                  /* win32: gives undefined window size when minimized */
649                                 if(state!=GHOST_kWindowStateMinimized) {
650                                         GHOST_RectangleHandle client_rect;
651                                         int l, t, r, b, scr_w, scr_h;
652                                         int sizex, sizey, posx, posy;
653                                         
654                                         client_rect= GHOST_GetClientBounds(win->ghostwin);
655                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
656                                         
657                                         GHOST_DisposeRectangle(client_rect);
658                                         
659                                         wm_get_screensize(&scr_w, &scr_h);
660                                         sizex= r-l;
661                                         sizey= b-t;
662                                         posx= l;
663                                         posy= scr_h - t - win->sizey;
664
665                                         /*
666                                          * Ghost sometimes send size or move events when the window hasn't changed.
667                                          * One case of this is using compiz on linux. To alleviate the problem
668                                          * we ignore all such event here.
669                                          * 
670                                          * It might be good to eventually do that at Ghost level, but that is for 
671                                          * another time.
672                                          */
673                                         if (win->sizex != sizex ||
674                                                         win->sizey != sizey ||
675                                                         win->posx != posx ||
676                                                         win->posy != posy)
677                                         {
678                                                 win->sizex= sizex;
679                                                 win->sizey= sizey;
680                                                 win->posx= posx;
681                                                 win->posy= posy;
682         
683                                                 /* debug prints */
684                                                 if(0) {
685                                                         state = GHOST_GetWindowState(win->ghostwin);
686         
687                                                         if(state==GHOST_kWindowStateNormal) {
688                                                                 if(G.f & G_DEBUG) printf("window state: normal\n");
689                                                         }
690                                                         else if(state==GHOST_kWindowStateMinimized) {
691                                                                 if(G.f & G_DEBUG) printf("window state: minimized\n");
692                                                         }
693                                                         else if(state==GHOST_kWindowStateMaximized) {
694                                                                 if(G.f & G_DEBUG) printf("window state: maximized\n");
695                                                         }
696                                                         else if(state==GHOST_kWindowStateFullScreen) {
697                                                                 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
698                                                         }
699                                                         
700                                                         if(type!=GHOST_kEventWindowSize) {
701                                                                 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
702                                                         }
703                                                         
704                                                 }
705                                         
706                                                 wm_window_make_drawable(C, win);
707                                                 wm_draw_window_clear(win);
708                                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
709                                                 WM_event_add_notifier(C, NC_WINDOW|NA_EDITED, NULL);
710                                         }
711                                 }
712                                 break;
713                         }
714                         default:
715                                 wm_event_add_ghostevent(win, type, data);
716                                 break;
717                 }
718
719         }
720         return 1;
721 }
722
723
724 /* This timer system only gives maximum 1 timer event per redraw cycle,
725    to prevent queues to get overloaded. 
726    Timer handlers should check for delta to decide if they just
727    update, or follow real time.
728    Timer handlers can also set duration to match frames passed
729 */
730 static int wm_window_timer(const bContext *C)
731 {
732         wmWindowManager *wm= CTX_wm_manager(C);
733         wmTimer *wt, *wtnext;
734         wmWindow *win;
735         double time= PIL_check_seconds_timer();
736         int retval= 0;
737         
738         for(wt= wm->timers.first; wt; wt= wtnext) {
739                 wtnext= wt->next; /* in case timer gets removed */
740                 win= wt->win;
741
742                 if(wt->sleep==0) {
743                         if(time > wt->ntime) {
744                                 wt->delta= time - wt->ltime;
745                                 wt->duration += wt->delta;
746                                 wt->ltime= time;
747                                 wt->ntime= wt->stime + wt->timestep*ceil(wt->duration/wt->timestep);
748
749                                 if(wt->event_type == TIMERJOBS)
750                                         wm_jobs_timer(C, wm, wt);
751                                 else if(wt->event_type == TIMERAUTOSAVE)
752                                         wm_autosave_timer(C, wm, wt);
753                                 else if(win) {
754                                         wmEvent event= *(win->eventstate);
755                                         
756                                         event.type= wt->event_type;
757                                         event.custom= EVT_DATA_TIMER;
758                                         event.customdata= wt;
759                                         wm_event_add(win, &event);
760
761                                         retval= 1;
762                                 }
763                         }
764                 }
765         }
766         return retval;
767 }
768
769 void wm_window_process_events(const bContext *C) 
770 {
771         int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
772         
773         if(hasevent)
774                 GHOST_DispatchEvents(g_system);
775         
776         hasevent |= wm_window_timer(C);
777
778         /* no event, we sleep 5 milliseconds */
779         if(hasevent==0)
780                 PIL_sleep_ms(5);
781 }
782
783 void wm_window_process_events_nosleep(const bContext *C) 
784 {
785         if(GHOST_ProcessEvents(g_system, 0))
786                 GHOST_DispatchEvents(g_system);
787 }
788
789 /* exported as handle callback to bke blender.c */
790 void wm_window_testbreak(void)
791 {
792         static double ltime= 0;
793         double curtime= PIL_check_seconds_timer();
794         
795         /* only check for breaks every 50 milliseconds
796                 * if we get called more often.
797                 */
798         if ((curtime-ltime)>.05) {
799                 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
800                 
801                 if(hasevent)
802                         GHOST_DispatchEvents(g_system);
803                 
804                 ltime= curtime;
805         }
806 }
807
808 /* **************** init ********************** */
809
810 void wm_ghost_init(bContext *C)
811 {
812         if (!g_system) {
813                 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
814                 
815                 g_system= GHOST_CreateSystem();
816                 GHOST_AddEventConsumer(g_system, consumer);
817         }       
818 }
819
820 void wm_ghost_exit(void)
821 {
822         if(g_system)
823                 GHOST_DisposeSystem(g_system);
824
825         g_system= NULL;
826 }
827
828 /* **************** timer ********************** */
829
830 /* to (de)activate running timers temporary */
831 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *win, wmTimer *timer, int dosleep)
832 {
833         wmTimer *wt;
834         
835         for(wt= wm->timers.first; wt; wt= wt->next)
836                 if(wt==timer)
837                         break;
838
839         if(wt)
840                 wt->sleep= dosleep;
841 }
842
843 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
844 {
845         wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
846         
847         wt->event_type= event_type;
848         wt->ltime= PIL_check_seconds_timer();
849         wt->ntime= wt->ltime + timestep;
850         wt->stime= wt->ltime;
851         wt->timestep= timestep;
852         wt->win= win;
853         
854         BLI_addtail(&wm->timers, wt);
855         
856         return wt;
857 }
858
859 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *win, wmTimer *timer)
860 {
861         wmTimer *wt;
862         
863         /* extra security check */
864         for(wt= wm->timers.first; wt; wt= wt->next)
865                 if(wt==timer)
866                         break;
867         if(wt) {
868                 
869                 BLI_remlink(&wm->timers, wt);
870                 if(wt->customdata)
871                         MEM_freeN(wt->customdata);
872                 MEM_freeN(wt);
873         }
874 }
875
876 /* ******************* clipboard **************** */
877
878 char *WM_clipboard_text_get(int selection)
879 {
880         char *p, *p2, *buf, *newbuf;
881
882         buf= (char*)GHOST_getClipboard(selection);
883         if(!buf)
884                 return NULL;
885         
886         /* always convert from \r\n to \n */
887         newbuf= MEM_callocN(strlen(buf)+1, "WM_clipboard_text_get");
888
889         for(p= buf, p2= newbuf; *p; p++) {
890                 if(*p != '\r')
891                         *(p2++)= *p;
892         }
893         *p2= '\0';
894
895         free(buf); /* ghost uses regular malloc */
896         
897         return newbuf;
898 }
899
900 void WM_clipboard_text_set(char *buf, int selection)
901 {
902 #ifdef _WIN32
903         /* do conversion from \n to \r\n on Windows */
904         char *p, *p2, *newbuf;
905         int newlen= 0;
906         
907         for(p= buf; *p; p++) {
908                 if(*p == '\n')
909                         newlen += 2;
910                 else
911                         newlen++;
912         }
913         
914         newbuf= MEM_callocN(newlen+1, "WM_clipboard_text_set");
915
916         for(p= buf, p2= newbuf; *p; p++, p2++) {
917                 if(*p == '\n') { 
918                         *(p2++)= '\r'; *p2= '\n';
919                 }
920                 else *p2= *p;
921         }
922         *p2= '\0';
923
924         GHOST_putClipboard((GHOST_TInt8*)newbuf, selection);
925         MEM_freeN(newbuf);
926 #else
927         GHOST_putClipboard((GHOST_TInt8*)buf, selection);
928 #endif
929 }
930
931 /* ************************************ */
932
933 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
934 {
935         *posx_r= win->posx;
936         *posy_r= win->posy;
937 }
938
939 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r) 
940 {
941         *width_r= win->sizex;
942         *height_r= win->sizey;
943 }
944
945 /* exceptional case: - splash is called before events are processed
946  * this means we dont actually know the window size so get this from GHOST */
947 void wm_window_get_size_ghost(wmWindow *win, int *width_r, int *height_r)
948 {
949         GHOST_RectangleHandle bounds= GHOST_GetClientBounds(win->ghostwin);
950         *width_r= GHOST_GetWidthRectangle(bounds);
951         *height_r= GHOST_GetHeightRectangle(bounds);
952         
953         GHOST_DisposeRectangle(bounds);
954 }
955
956 void wm_window_set_size(wmWindow *win, int width, int height) 
957 {
958         GHOST_SetClientSize(win->ghostwin, width, height);
959 }
960
961 void wm_window_lower(wmWindow *win) 
962 {
963         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
964 }
965
966 void wm_window_raise(wmWindow *win) 
967 {
968         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
969 }
970
971 void wm_window_swap_buffers(wmWindow *win)
972 {
973         
974 #ifdef WIN32
975         glDisable(GL_SCISSOR_TEST);
976         GHOST_SwapWindowBuffers(win->ghostwin);
977         glEnable(GL_SCISSOR_TEST);
978 #else
979         GHOST_SwapWindowBuffers(win->ghostwin);
980 #endif
981 }
982
983 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
984 {
985         GHOST_GetCursorPosition(g_system, x, y);
986         GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
987 #if defined(__APPLE__) && defined(GHOST_COCOA)
988         //Cocoa has silly exception that should be fixed at the ghost level
989         //(ghost is an allegory for an invisible system specific code)
990 #else
991         *y = (win->sizey-1) - *y;
992 #endif
993 }
994
995 /* ******************* exported api ***************** */
996
997
998 /* called whem no ghost system was initialized */
999 void WM_setprefsize(int stax, int stay, int sizx, int sizy)
1000 {
1001         prefstax= stax;
1002         prefstay= stay;
1003         prefsizx= sizx;
1004         prefsizy= sizy;
1005 }
1006
1007 /* This function requires access to the GHOST_SystemHandle (g_system) */
1008 void WM_cursor_warp(wmWindow *win, int x, int y)
1009 {
1010         if (win && win->ghostwin) {
1011                 int oldx=x, oldy=y;
1012
1013                 y= win->sizey -y - 1;
1014                 GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
1015                 GHOST_SetCursorPosition(g_system, x, y);
1016
1017                 win->eventstate->prevx= oldx;
1018                 win->eventstate->prevy= oldy;
1019         }
1020 }
1021