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