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