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