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