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