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