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