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