9425dd6cf58d4c17cfc22be07f8d2518220b35a9
[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
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_global.h"
44 #include "BKE_utildefines.h"
45
46 #include "BIF_gl.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50 #include "wm.h"
51 #include "wm_window.h"
52 #include "wm_subwindow.h"
53 #include "wm_event_system.h"
54
55 #include "ED_screen.h"
56
57 /* the global to talk to ghost */
58 GHOST_SystemHandle g_system= NULL;
59
60 /* set by commandline */
61 static int prefsizx= 0, prefsizy= 0, prefstax= 0, prefstay= 0;
62
63
64 /* ******** win open & close ************ */
65
66
67 static void wm_get_screensize(int *width_r, int *height_r) 
68 {
69         unsigned int uiwidth;
70         unsigned int uiheight;
71         
72         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
73         *width_r= uiwidth;
74         *height_r= uiheight;
75 }
76
77 static void wm_ghostwindow_destroy(wmWindow *win) 
78 {
79         
80         if (win->timer) {
81                 GHOST_RemoveTimer(g_system, (GHOST_TimerTaskHandle)win->timer);
82                 win->timer= NULL;
83         }
84         
85         if(win->ghostwin) {
86                 GHOST_DisposeWindow(g_system, win->ghostwin);
87                 win->ghostwin= NULL;
88         }
89 }
90
91 /* including window itself */
92 void wm_window_free(bContext *C, wmWindow *win)
93 {
94         
95         /* update context */
96         if(C) {
97                 if(C->wm->windrawable==win)
98                         C->wm->windrawable= NULL;
99                 if(C->wm->winactive==win)
100                         C->wm->winactive= NULL;
101                 if(C->window==win)
102                         C->window= NULL;
103                 if(C->screen==win->screen)
104                         C->screen= NULL;
105         }       
106         /* XXX free screens */
107         
108         if(win->eventstate) MEM_freeN(win->eventstate);
109
110         WM_gesture_free(win);
111         wm_event_free_handlers(&win->handlers);
112         wm_event_free_all(win);
113         wm_subwindows_free(win);
114         
115         wm_ghostwindow_destroy(win);
116         
117         MEM_freeN(win);
118 }
119
120 static int find_free_winid(wmWindowManager *wm)
121 {
122         wmWindow *win;
123         int id= 0;
124         
125         for(win= wm->windows.first; win; win= win->next)
126                 if(id <= win->winid)
127                         id= win->winid+1;
128         
129         return id;
130 }
131
132 /* dont change context itself */
133 wmWindow *wm_window_new(bContext *C, bScreen *screen)
134 {
135         wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
136         
137         BLI_addtail(&C->wm->windows, win);
138         win->winid= find_free_winid(C->wm);
139
140         win->screen= screen;
141         return win;
142 }
143
144 /* part of wm_window.c api */
145 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
146 {
147         wmWindow *win= wm_window_new(C, winorig->screen);
148         
149         win->posx= winorig->posx+10;
150         win->posy= winorig->posy;
151         win->sizex= winorig->sizex;
152         win->sizey= winorig->sizey;
153         
154         win->screen= ED_screen_duplicate(win, win->screen);
155         win->screen->do_refresh= 1;
156         win->screen->do_draw= 1;
157         
158         return win;
159 }
160
161 /* operator callback */
162 int wm_window_duplicate_op(bContext *C, wmOperator *op)
163 {
164         wm_window_copy(C, C->window);
165         wm_check(C);
166         
167         return OPERATOR_FINISHED;
168 }
169
170 wmWindow *wm_window_rip(bContext *C, wmWindow *winorig)
171 {
172         wmWindow *win= wm_window_new(C, winorig->screen);
173         
174         win->posx= winorig->posx+10;
175         win->posy= winorig->posy;
176         win->sizex= C->area->winx;
177         win->sizey= C->area->winy;
178         
179         win->screen= ED_screen_riparea(win, win->screen, C->area);
180         C->area = NULL; /* is removed */
181         win->screen->do_refresh= 1;
182         win->screen->do_draw= 1;
183         
184         return win;
185 }
186 /* operator callback */
187 int wm_window_rip_op(bContext *C, wmOperator *op, wmEvent *event)
188 {
189         /* need to make sure area is set in the current context */
190         if (!C->area) {
191                 ScrArea *sa= C->window->screen->areabase.first;
192                 for(; sa; sa= sa->next) {
193                         if(BLI_in_rcti(&sa->totrct, event->x, event->y)) {
194                                 C->area = sa;
195                                 break;
196                         }
197                 }
198                 if(C->area==NULL)
199                         return OPERATOR_CANCELLED;
200         }
201
202         wm_window_rip(C, C->window);
203         wm_check(C);
204         WM_event_add_notifier(C->wm, C->window, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
205         return OPERATOR_FINISHED;
206 }
207
208
209 /* fullscreen operator callback */
210 int wm_window_fullscreen_toggle_op(bContext *C, wmOperator *op)
211 {
212         GHOST_TWindowState state = GHOST_GetWindowState(C->window->ghostwin);
213         if(state!=GHOST_kWindowStateFullScreen)
214                 GHOST_SetWindowState(C->window->ghostwin, GHOST_kWindowStateFullScreen);
215         else
216                 GHOST_SetWindowState(C->window->ghostwin, GHOST_kWindowStateNormal);
217
218         return OPERATOR_FINISHED;
219         
220 }
221
222 /* this is event from ghost */
223 static void wm_window_close(bContext *C, wmWindow *win)
224 {
225         BLI_remlink(&C->wm->windows, win);
226         wm_window_free(C, win);
227
228         if(C->wm->windows.first==NULL)
229                 WM_exit(C);
230 }
231         
232 /* exit blender */
233 int wm_exit_blender_op(bContext *C, wmOperator *op)
234 {
235         wmWindow *win= C->wm->windows.first;
236         while(win) {
237                 wm_window_close(C, win);
238                 win= win->next;
239         }
240
241         return OPERATOR_FINISHED;
242 }
243
244 static void wm_window_open(wmWindowManager *wm, char *title, wmWindow *win)
245 {
246         GHOST_WindowHandle ghostwin;
247         GHOST_TWindowState inital_state;
248         int scr_w, scr_h, posy;
249         
250         wm_get_screensize(&scr_w, &scr_h);
251         posy= (scr_h - win->posy - win->sizey);
252         
253 //              inital_state = GHOST_kWindowStateFullScreen;
254 //              inital_state = GHOST_kWindowStateMaximized;
255                 inital_state = GHOST_kWindowStateNormal;
256
257 #ifdef __APPLE__
258         {
259                 extern int macPrefState; /* creator.c */
260                 inital_state += macPrefState;
261         }
262 #endif
263         
264         ghostwin= GHOST_CreateWindow(g_system, title, 
265                                                                  win->posx, posy, win->sizex, win->sizey, 
266                                                                  inital_state, 
267                                                                  GHOST_kDrawingContextTypeOpenGL,
268                                                                  0 /* no stereo */);
269         
270         if (ghostwin) {
271
272                 win->ghostwin= ghostwin;
273                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
274                 
275                 if(win->eventstate==NULL)
276                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
277                 
278                 /* add keymap handlers (1 for all keys in map!) */
279                 WM_event_add_keymap_handler(&wm->windowkeymap, &win->handlers);
280                 WM_event_add_keymap_handler(&wm->screenkeymap, &win->handlers);
281                 
282                 /* until screens get drawn, make it nice grey */
283                 glClearColor(.55, .55, .55, 0.0);
284                 glClear(GL_COLOR_BUFFER_BIT);
285                 wm_window_swap_buffers(win);
286
287                 /* standard state vars for window */
288                 glEnable(GL_SCISSOR_TEST);
289         }
290 }
291
292 /* for wmWindows without ghostwin, open these and clear */
293 void wm_window_add_ghostwindows(wmWindowManager *wm)
294 {
295         wmWindow *win;
296         
297         /* no commandline prefsize? then we set this */
298         if (!prefsizx) {
299                 wm_get_screensize(&prefsizx, &prefsizy);
300                 
301 #ifdef __APPLE__
302                 {
303                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
304                 
305                         wm_set_apple_prefsize(prefsizx, prefsizy);
306                 }
307 #else
308                 prefstax= 0;
309                 prefstay= 0;
310                 
311 #endif
312         }
313         
314         for(win= wm->windows.first; win; win= win->next) {
315                 if(win->ghostwin==NULL) {
316                         if(win->sizex==0) {
317                                 win->posx= prefstax;
318                                 win->posy= prefstay;
319                                 win->sizex= prefsizx;
320                                 win->sizey= prefsizy;
321                                 win->windowstate= 0;
322                         }
323                         wm_window_open(wm, "Blender", win);
324                 }
325         }
326 }
327
328 /* ************ events *************** */
329
330 static int query_qual(char qual) 
331 {
332         GHOST_TModifierKeyMask left, right;
333         int val= 0;
334         
335         if (qual=='s') {
336                 left= GHOST_kModifierKeyLeftShift;
337                 right= GHOST_kModifierKeyRightShift;
338         } else if (qual=='c') {
339                 left= GHOST_kModifierKeyLeftControl;
340                 right= GHOST_kModifierKeyRightControl;
341         } else if (qual=='C') {
342                 left= right= GHOST_kModifierKeyCommand;
343         } else {
344                 left= GHOST_kModifierKeyLeftAlt;
345                 right= GHOST_kModifierKeyRightAlt;
346         }
347         
348         GHOST_GetModifierKeyState(g_system, left, &val);
349         if (!val)
350                 GHOST_GetModifierKeyState(g_system, right, &val);
351         
352         return val;
353 }
354
355 void wm_window_make_drawable(bContext *C, wmWindow *win) 
356 {
357         if (win != C->wm->windrawable && win->ghostwin) {
358 //              win->lmbut= 0;  /* keeps hanging when mousepressed while other window opened */
359                 
360                 C->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                                 C->wm->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                                 ED_screen_set_subwinactive(win);        /* active subwindow in screen */
434                                 
435                                 wm_window_make_drawable(C, win);
436                                 break;
437                         }
438                         case GHOST_kEventWindowClose: {
439                                 wm_window_close(C, win);
440                                 break;
441                         }
442                         case GHOST_kEventWindowUpdate: {
443                                 if(G.f & G_DEBUG) printf("ghost redraw\n");
444                                 
445                                 wm_window_make_drawable(C, win);
446                                 WM_event_add_notifier(C->wm, win, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
447
448                                 break;
449                         }
450                         case GHOST_kEventWindowSize:
451                         case GHOST_kEventWindowMove: {
452                                 GHOST_RectangleHandle client_rect;
453                                 int l, t, r, b, scr_w, scr_h;
454                                 
455                                 client_rect= GHOST_GetClientBounds(win->ghostwin);
456                                 GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
457                                 
458                                 GHOST_DisposeRectangle(client_rect);
459                                 
460                                 wm_get_screensize(&scr_w, &scr_h);
461                                 win->sizex= r-l;
462                                 win->sizey= b-t;
463                                 win->posx= l;
464                                 win->posy= scr_h - t - win->sizey;
465
466                                 /* debug prints */
467                                 if(0) {
468                                         GHOST_TWindowState state;
469                                         state = GHOST_GetWindowState(win->ghostwin);
470
471                                         if(state==GHOST_kWindowStateNormal) {
472                                                 if(G.f & G_DEBUG) printf("window state: normal\n");
473                                         }
474                                         else if(state==GHOST_kWindowStateMinimized) {
475                                                 if(G.f & G_DEBUG) printf("window state: minimized\n");
476                                         }
477                                         else if(state==GHOST_kWindowStateMaximized) {
478                                                 if(G.f & G_DEBUG) printf("window state: maximized\n");
479                                         }
480                                         else if(state==GHOST_kWindowStateFullScreen) {
481                                                 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
482                                         }
483                                         
484                                         if(type!=GHOST_kEventWindowSize) {
485                                                 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
486                                         }
487                                         
488                                 }
489                                 
490                                 wm_window_make_drawable(C, win);
491                                 WM_event_add_notifier(C->wm, win, 0, WM_NOTE_SCREEN_CHANGED, 0, NULL);
492                                 
493                                 break;
494                         }
495                         default:
496                                 if(type==GHOST_kEventKeyDown) // XXX debug
497                                         WM_event_add_notifier(C->wm, win, 0, WM_NOTE_WINDOW_REDRAW, 0, NULL);
498                                 wm_event_add_ghostevent(win, type, data);
499                                 break;
500                 }
501
502         }
503         return 1;
504 }
505
506 void wm_window_process_events(int wait_for_event) 
507 {
508         GHOST_ProcessEvents(g_system, wait_for_event);
509         GHOST_DispatchEvents(g_system);
510 }
511
512 /* **************** init ********************** */
513
514 void wm_ghost_init(bContext *C)
515 {
516         if (!g_system) {
517                 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
518                 
519                 g_system= GHOST_CreateSystem();
520                 GHOST_AddEventConsumer(g_system, consumer);
521         }       
522 }
523
524 /* **************** timer ********************** */
525
526 static void window_timer_proc(GHOST_TimerTaskHandle timer, GHOST_TUns64 time)
527 {
528         wmWindow *win= GHOST_GetTimerTaskUserData(timer);
529         
530         wm_event_add_ghostevent(win, win->timer_event, NULL);
531 }
532
533 void wm_window_set_timer(wmWindow *win, int delay_ms, int event)
534 {
535         if (win->timer) GHOST_RemoveTimer(g_system, win->timer);
536         
537         win->timer_event= event;
538         win->timer= GHOST_InstallTimer(g_system, delay_ms, delay_ms, window_timer_proc, win);
539 }
540
541 /* ************************************ */
542
543 void wm_window_set_title(wmWindow *win, char *title) 
544 {
545         GHOST_SetTitle(win->ghostwin, title);
546 }
547
548 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
549 {
550         *posx_r= win->posx;
551         *posy_r= win->posy;
552 }
553
554 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r) 
555 {
556         *width_r= win->sizex;
557         *height_r= win->sizey;
558 }
559
560 void wm_window_set_size(wmWindow *win, int width, int height) 
561 {
562         GHOST_SetClientSize(win->ghostwin, width, height);
563 }
564
565 void wm_window_lower(wmWindow *win) 
566 {
567         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
568 }
569
570 void wm_window_raise(wmWindow *win) 
571 {
572         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
573 #ifdef _WIN32
574 //      markdirty_all(); /* to avoid redraw errors in fullscreen mode (aphex) */
575 #endif
576 }
577
578 void wm_window_swap_buffers(wmWindow *win)
579 {
580         GHOST_SwapWindowBuffers(win->ghostwin);
581 }
582
583 /* ******************* exported api ***************** */
584
585
586 /* called whem no ghost system was initialized */
587 void WM_setprefsize(int stax, int stay, int sizx, int sizy)
588 {
589         prefstax= stax;
590         prefstay= stay;
591         prefsizx= sizx;
592         prefsizy= sizy;
593 }
594