2.5: WM Compositing
[blender-staging.git] / source / blender / windowmanager / intern / wm_window.c
1 /**
2  * $Id: wm_window.c
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 <stdlib.h>
30 #include <stdio.h>
31
32 #include "DNA_listBase.h"       
33 #include "DNA_screen_types.h"
34 #include "DNA_windowmanager_types.h"
35
36 #include "MEM_guardedalloc.h"
37
38 #include "GHOST_C-api.h"
39
40 #include "BLI_blenlib.h"
41
42 #include "BKE_blender.h"
43 #include "BKE_context.h"
44 #include "BKE_global.h"
45 #include "BKE_utildefines.h"
46
47 #include "BIF_gl.h"
48
49 #include "WM_api.h"
50 #include "WM_types.h"
51 #include "wm.h"
52 #include "wm_draw.h"
53 #include "wm_window.h"
54 #include "wm_subwindow.h"
55 #include "wm_event_system.h"
56
57 #include "ED_screen.h"
58
59 #include "PIL_time.h"
60
61 #include "GPU_draw.h"
62
63 /* the global to talk to ghost */
64 GHOST_SystemHandle g_system= NULL;
65
66 /* set by commandline */
67 static int prefsizx= 0, prefsizy= 0, prefstax= 0, prefstay= 0;
68
69
70 /* ******** win open & close ************ */
71
72
73 static void wm_get_screensize(int *width_r, int *height_r) 
74 {
75         unsigned int uiwidth;
76         unsigned int uiheight;
77         
78         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
79         *width_r= uiwidth;
80         *height_r= uiheight;
81 }
82
83 static void wm_ghostwindow_destroy(wmWindow *win) 
84 {
85         if(win->ghostwin) {
86                 GHOST_DisposeWindow(g_system, win->ghostwin);
87                 win->ghostwin= NULL;
88         }
89 }
90
91 /* including window itself, C can be NULL. 
92    ED_screen_exit should have been called */
93 void wm_window_free(bContext *C, wmWindow *win)
94 {
95         /* update context */
96         if(C) {
97                 wmWindowManager *wm= CTX_wm_manager(C);
98
99                 if(wm->windrawable==win)
100                         wm->windrawable= NULL;
101                 if(wm->winactive==win)
102                         wm->winactive= NULL;
103                 if(CTX_wm_window(C)==win)
104                         CTX_wm_window_set(C, NULL);
105         }       
106         
107         if(win->eventstate) MEM_freeN(win->eventstate);
108         BLI_freelistN(&win->timers);
109         
110         wm_event_free_all(win);
111         wm_subwindows_free(win);
112         
113         wm_ghostwindow_destroy(win);
114         
115         MEM_freeN(win);
116 }
117
118 static int find_free_winid(wmWindowManager *wm)
119 {
120         wmWindow *win;
121         int id= 0;
122         
123         for(win= wm->windows.first; win; win= win->next)
124                 if(id <= win->winid)
125                         id= win->winid+1;
126         
127         return id;
128 }
129
130 /* dont change context itself */
131 wmWindow *wm_window_new(bContext *C)
132 {
133         wmWindowManager *wm= CTX_wm_manager(C);
134         wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
135         
136         BLI_addtail(&wm->windows, win);
137         win->winid= find_free_winid(wm);
138
139         return win;
140 }
141
142
143 /* part of wm_window.c api */
144 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
145 {
146         wmWindow *win= wm_window_new(C);
147         
148         win->posx= winorig->posx+10;
149         win->posy= winorig->posy;
150         win->sizex= winorig->sizex;
151         win->sizey= winorig->sizey;
152         
153         win->screen= ED_screen_duplicate(win, winorig->screen);
154         win->screen->do_refresh= 1;
155         win->screen->do_draw= 1;
156
157         win->drawmethod= -1;
158         win->drawdata= NULL;
159         
160         return win;
161 }
162
163 /* this is event from ghost */
164 static void wm_window_close(bContext *C, wmWindow *win)
165 {
166         wmWindowManager *wm= CTX_wm_manager(C);
167         BLI_remlink(&wm->windows, win);
168         
169         wm_draw_window_clear(win);
170         WM_event_remove_handlers(C, &win->handlers);
171         ED_screen_exit(C, win, win->screen);
172         wm_window_free(C, win);
173         
174         if(wm->windows.first==NULL)
175                 WM_exit(C);
176 }
177
178 /* belongs to below */
179 static void wm_window_add_ghostwindow(wmWindowManager *wm, char *title, wmWindow *win)
180 {
181         GHOST_WindowHandle ghostwin;
182         GHOST_TWindowState inital_state;
183         int scr_w, scr_h, posy;
184         
185         wm_get_screensize(&scr_w, &scr_h);
186         posy= (scr_h - win->posy - win->sizey);
187         
188         //              inital_state = GHOST_kWindowStateFullScreen;
189         //              inital_state = GHOST_kWindowStateMaximized;
190         inital_state = GHOST_kWindowStateNormal;
191         
192 #ifdef __APPLE__
193         {
194                 extern int macPrefState; /* creator.c */
195                 inital_state += macPrefState;
196         }
197 #endif
198         
199         ghostwin= GHOST_CreateWindow(g_system, title, 
200                                                                  win->posx, posy, win->sizex, win->sizey, 
201                                                                  inital_state, 
202                                                                  GHOST_kDrawingContextTypeOpenGL,
203                                                                  0 /* no stereo */);
204         
205         if (ghostwin) {
206                 ListBase *keymap;
207                 
208                 win->ghostwin= ghostwin;
209                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
210                 
211                 if(win->eventstate==NULL)
212                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
213                 
214                 /* add keymap handlers (1 handler for all keys in map!) */
215                 keymap= WM_keymap_listbase(wm, "Window", 0, 0);
216                 WM_event_add_keymap_handler(&win->handlers, keymap);
217
218                 keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
219                 WM_event_add_keymap_handler(&win->handlers, keymap);
220                 
221                 /* until screens get drawn, make it nice grey */
222                 glClearColor(.55, .55, .55, 0.0);
223                 glClear(GL_COLOR_BUFFER_BIT);
224                 wm_window_swap_buffers(win);
225                 
226                 /* standard state vars for window */
227                 glEnable(GL_SCISSOR_TEST);
228                 
229                 GPU_state_init();
230         }
231 }
232
233 /* for wmWindows without ghostwin, open these and clear */
234 /* window size is read from window, if 0 it uses prefsize */
235 void wm_window_add_ghostwindows(wmWindowManager *wm)
236 {
237         wmWindow *win;
238         
239         /* no commandline prefsize? then we set this */
240         if (!prefsizx) {
241                 wm_get_screensize(&prefsizx, &prefsizy);
242                 
243 #ifdef __APPLE__
244                 {
245                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
246                         
247                         wm_set_apple_prefsize(prefsizx, prefsizy);
248                 }
249 #else
250                 prefstax= 0;
251                 prefstay= 0;
252                 
253 #endif
254         }
255         
256         for(win= wm->windows.first; win; win= win->next) {
257                 if(win->ghostwin==NULL) {
258                         if(win->sizex==0) {
259                                 win->posx= prefstax;
260                                 win->posy= prefstay;
261                                 win->sizex= prefsizx;
262                                 win->sizey= prefsizy;
263                                 win->windowstate= 0;
264                         }
265                         wm_window_add_ghostwindow(wm, "Blender", win);
266                 }
267         }
268 }
269
270 /* new window, no screen yet, but we open ghostwindow for it */
271 /* also gets the window level handlers */
272 /* area-rip calls this */
273 wmWindow *WM_window_open(bContext *C, rcti *rect)
274 {
275         wmWindowManager *wm= CTX_wm_manager(C);
276         wmWindow *win= wm_window_new(C);
277         
278         win->posx= rect->xmin;
279         win->posy= rect->ymin;
280         win->sizex= rect->xmax - rect->xmin;
281         win->sizey= rect->ymax - rect->ymin;
282
283         win->drawmethod= -1;
284         win->drawdata= NULL;
285         
286         wm_window_add_ghostwindow(wm, "Blender", win);
287         
288         return win;
289 }
290
291
292 /* ****************** Operators ****************** */
293
294 /* operator callback */
295 int wm_window_duplicate_op(bContext *C, wmOperator *op)
296 {
297         wm_window_copy(C, CTX_wm_window(C));
298         wm_check(C);
299         
300         return OPERATOR_FINISHED;
301 }
302
303
304 /* fullscreen operator callback */
305 int wm_window_fullscreen_toggle_op(bContext *C, wmOperator *op)
306 {
307         wmWindow *window= CTX_wm_window(C);
308         GHOST_TWindowState state = GHOST_GetWindowState(window->ghostwin);
309         if(state!=GHOST_kWindowStateFullScreen)
310                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
311         else
312                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
313
314         return OPERATOR_FINISHED;
315         
316 }
317
318         
319 /* exit blender */
320 int wm_exit_blender_op(bContext *C, wmOperator *op)
321 {
322         wmWindowManager *wm= CTX_wm_manager(C);
323         wmWindow *win= wm->windows.first;
324
325         while(win) {
326                 wm_window_close(C, win);
327                 win= win->next;
328         }
329
330         return OPERATOR_FINISHED;
331 }
332
333
334 /* ************ events *************** */
335
336 static int query_qual(char qual) 
337 {
338         GHOST_TModifierKeyMask left, right;
339         int val= 0;
340         
341         if (qual=='s') {
342                 left= GHOST_kModifierKeyLeftShift;
343                 right= GHOST_kModifierKeyRightShift;
344         } else if (qual=='c') {
345                 left= GHOST_kModifierKeyLeftControl;
346                 right= GHOST_kModifierKeyRightControl;
347         } else if (qual=='C') {
348                 left= right= GHOST_kModifierKeyCommand;
349         } else {
350                 left= GHOST_kModifierKeyLeftAlt;
351                 right= GHOST_kModifierKeyRightAlt;
352         }
353         
354         GHOST_GetModifierKeyState(g_system, left, &val);
355         if (!val)
356                 GHOST_GetModifierKeyState(g_system, right, &val);
357         
358         return val;
359 }
360
361 void wm_window_make_drawable(bContext *C, wmWindow *win) 
362 {
363         wmWindowManager *wm= CTX_wm_manager(C);
364
365         if (win != wm->windrawable && win->ghostwin) {
366 //              win->lmbut= 0;  /* keeps hanging when mousepressed while other window opened */
367                 
368                 wm->windrawable= win;
369                 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
370                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
371         }
372 }
373
374 /* called by ghost, here we handle events for windows themselves or send to event system */
375 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private) 
376 {
377         bContext *C= private;
378         GHOST_TEventType type= GHOST_GetEventType(evt);
379         
380         if (type == GHOST_kEventQuit) {
381                 WM_exit(C);
382         } else {
383                 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
384                 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
385                 wmWindow *win;
386                 
387                 if (!ghostwin) {
388                         // XXX - should be checked, why are we getting an event here, and
389                         //      what is it?
390                         
391                         return 1;
392                 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
393                         // XXX - should be checked, why are we getting an event here, and
394                         //      what is it?
395                         
396                         return 1;
397                 } else {
398                         win= GHOST_GetWindowUserData(ghostwin);
399                 }
400                 
401                 switch(type) {
402                         case GHOST_kEventWindowDeactivate:
403                                 win->active= 0; /* XXX */
404                                 break;
405                         case GHOST_kEventWindowActivate: 
406                         {
407                                 GHOST_TEventKeyData kdata;
408                                 int cx, cy, wx, wy;
409                                 
410                                 CTX_wm_manager(C)->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
411                                 
412                                 win->active= 1;
413 //                              window_handle(win, INPUTCHANGE, win->active);
414                                 
415                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
416                                 kdata.ascii= 0;
417                                 if (win->eventstate->shift && !query_qual('s')) {
418                                         kdata.key= GHOST_kKeyLeftShift;
419                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
420                                 }
421                                 if (win->eventstate->ctrl && !query_qual('c')) {
422                                         kdata.key= GHOST_kKeyLeftControl;
423                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
424                                 }
425                                 if (win->eventstate->alt && !query_qual('a')) {
426                                         kdata.key= GHOST_kKeyLeftAlt;
427                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
428                                 }
429                                 if (win->eventstate->oskey && !query_qual('C')) {
430                                         kdata.key= GHOST_kKeyCommand;
431                                         wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
432                                 }
433                                 
434                                 /* entering window, update mouse pos. but no event */
435                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
436                                 
437                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
438                                 win->eventstate->x= cx;
439                                 win->eventstate->y= (win->sizey-1) - cy;
440                                 
441                                 wm_window_make_drawable(C, win);
442                                 break;
443                         }
444                         case GHOST_kEventWindowClose: {
445                                 wm_window_close(C, win);
446                                 break;
447                         }
448                         case GHOST_kEventWindowUpdate: {
449                                 if(G.f & G_DEBUG) printf("ghost redraw\n");
450                                 
451                                 wm_window_make_drawable(C, win);
452                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
453
454                                 break;
455                         }
456                         case GHOST_kEventWindowSize:
457                         case GHOST_kEventWindowMove: {
458                                 GHOST_TWindowState state;
459                                 state = GHOST_GetWindowState(win->ghostwin);
460
461                                  /* win32: gives undefined window size when minimized */
462                                 if(state!=GHOST_kWindowStateMinimized) {
463                                         GHOST_RectangleHandle client_rect;
464                                         int l, t, r, b, scr_w, scr_h;
465                                         
466                                         client_rect= GHOST_GetClientBounds(win->ghostwin);
467                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
468                                         
469                                         GHOST_DisposeRectangle(client_rect);
470                                         
471                                         wm_get_screensize(&scr_w, &scr_h);
472                                         win->sizex= r-l;
473                                         win->sizey= b-t;
474                                         win->posx= l;
475                                         win->posy= scr_h - t - win->sizey;
476
477                                         /* debug prints */
478                                         if(0) {
479                                                 state = GHOST_GetWindowState(win->ghostwin);
480
481                                                 if(state==GHOST_kWindowStateNormal) {
482                                                         if(G.f & G_DEBUG) printf("window state: normal\n");
483                                                 }
484                                                 else if(state==GHOST_kWindowStateMinimized) {
485                                                         if(G.f & G_DEBUG) printf("window state: minimized\n");
486                                                 }
487                                                 else if(state==GHOST_kWindowStateMaximized) {
488                                                         if(G.f & G_DEBUG) printf("window state: maximized\n");
489                                                 }
490                                                 else if(state==GHOST_kWindowStateFullScreen) {
491                                                         if(G.f & G_DEBUG) printf("window state: fullscreen\n");
492                                                 }
493                                                 
494                                                 if(type!=GHOST_kEventWindowSize) {
495                                                         if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
496                                                 }
497                                                 
498                                         }
499                                 
500                                         wm_window_make_drawable(C, win);
501                                         wm_draw_window_clear(win);
502                                         WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
503                                 }
504                                 break;
505                         }
506                         default:
507                                 wm_event_add_ghostevent(win, type, data);
508                                 break;
509                 }
510
511         }
512         return 1;
513 }
514
515
516 /* This timer system only gives maximum 1 timer event per redraw cycle,
517    to prevent queues to get overloaded. 
518    Timer handlers should check for delta to decide if they just
519    update, or follow real time 
520 */
521 static int wm_window_timer(const bContext *C)
522 {
523         wmWindowManager *wm= CTX_wm_manager(C);
524         wmWindow *win;
525         double time= PIL_check_seconds_timer();
526         int retval= 0;
527         
528         for(win= wm->windows.first; win; win= win->next) {
529                 wmTimer *wt;
530                 for(wt= win->timers.first; wt; wt= wt->next) {
531                         if(wt->sleep==0) {
532                                 if(wt->timestep < time - wt->ltime) {
533                                         wmEvent event= *(win->eventstate);
534                                         
535                                         wt->delta= time - wt->ltime;
536                                         wt->duration += wt->delta;
537                                         wt->ltime= time;
538                                         
539                                         event.type= wt->event_type;
540                                         event.custom= EVT_DATA_TIMER;
541                                         event.customdata= wt;
542                                         wm_event_add(win, &event);
543
544                                         retval= 1;
545                                 }
546                         }
547                 }
548         }
549         return retval;
550 }
551
552 void wm_window_process_events(const bContext *C) 
553 {
554         int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
555         
556         if(hasevent)
557                 GHOST_DispatchEvents(g_system);
558         
559         hasevent |= wm_window_timer(C);
560
561         /* no event, we sleep 5 milliseconds */
562         if(hasevent==0)
563                 PIL_sleep_ms(5);
564 }
565
566 /* exported as handle callback to bke blender.c */
567 void wm_window_testbreak(void)
568 {
569         static double ltime= 0;
570         double curtime= PIL_check_seconds_timer();
571         
572         /* only check for breaks every 50 milliseconds
573                 * if we get called more often.
574                 */
575         if ((curtime-ltime)>.05) {
576                 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
577                 
578                 if(hasevent)
579                         GHOST_DispatchEvents(g_system);
580                 
581                 ltime= curtime;
582         }
583 }
584
585 /* **************** init ********************** */
586
587 void wm_ghost_init(bContext *C)
588 {
589         if (!g_system) {
590                 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
591                 
592                 g_system= GHOST_CreateSystem();
593                 GHOST_AddEventConsumer(g_system, consumer);
594         }       
595 }
596
597 /* **************** timer ********************** */
598
599 /* to (de)activate running timers temporary */
600 void WM_event_window_timer_sleep(wmWindow *win, wmTimer *timer, int dosleep)
601 {
602         wmTimer *wt;
603         
604         for(wt= win->timers.first; wt; wt= wt->next)
605                 if(wt==timer)
606                         break;
607         if(wt) {
608                 wt->sleep= dosleep;
609         }               
610 }
611
612 wmTimer *WM_event_add_window_timer(wmWindow *win, int event_type, double timestep)
613 {
614         wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
615         
616         wt->event_type= event_type;
617         wt->ltime= PIL_check_seconds_timer();
618         wt->timestep= timestep;
619         
620         BLI_addtail(&win->timers, wt);
621         
622         return wt;
623 }
624
625 void WM_event_remove_window_timer(wmWindow *win, wmTimer *timer)
626 {
627         wmTimer *wt;
628         
629         for(wt= win->timers.first; wt; wt= wt->next)
630                 if(wt==timer)
631                         break;
632         if(wt) {
633                 BLI_remlink(&win->timers, wt);
634                 MEM_freeN(wt);
635         }
636 }
637
638 /* ************************************ */
639
640 void wm_window_set_title(wmWindow *win, char *title) 
641 {
642         GHOST_SetTitle(win->ghostwin, title);
643 }
644
645 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
646 {
647         *posx_r= win->posx;
648         *posy_r= win->posy;
649 }
650
651 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r) 
652 {
653         *width_r= win->sizex;
654         *height_r= win->sizey;
655 }
656
657 void wm_window_set_size(wmWindow *win, int width, int height) 
658 {
659         GHOST_SetClientSize(win->ghostwin, width, height);
660 }
661
662 void wm_window_lower(wmWindow *win) 
663 {
664         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
665 }
666
667 void wm_window_raise(wmWindow *win) 
668 {
669         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
670 }
671
672 void wm_window_swap_buffers(wmWindow *win)
673 {
674         
675 #ifdef WIN32
676         glDisable(GL_SCISSOR_TEST);
677         GHOST_SwapWindowBuffers(win->ghostwin);
678         glEnable(GL_SCISSOR_TEST);
679 #else
680         GHOST_SwapWindowBuffers(win->ghostwin);
681 #endif
682 }
683
684 /* ******************* exported api ***************** */
685
686
687 /* called whem no ghost system was initialized */
688 void WM_setprefsize(int stax, int stay, int sizx, int sizy)
689 {
690         prefstax= stax;
691         prefstay= stay;
692         prefsizx= sizx;
693         prefsizy= sizy;
694 }
695