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