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