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