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