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