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