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