4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
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.
24 * Contributor(s): Blender Foundation, 2008
26 * ***** END GPL LICENSE BLOCK *****
34 #include "DNA_listBase.h"
35 #include "DNA_screen_types.h"
36 #include "DNA_windowmanager_types.h"
38 #include "MEM_guardedalloc.h"
40 #include "GHOST_C-api.h"
42 #include "BLI_blenlib.h"
44 #include "BKE_blender.h"
45 #include "BKE_context.h"
46 #include "BKE_global.h"
47 #include "BKE_utildefines.h"
55 #include "wm_window.h"
56 #include "wm_subwindow.h"
57 #include "wm_event_system.h"
59 #include "ED_screen.h"
65 /* the global to talk to ghost */
66 GHOST_SystemHandle g_system= NULL;
68 /* set by commandline */
69 static int prefsizx= 0, prefsizy= 0, prefstax= 0, prefstay= 0;
72 /* ******** win open & close ************ */
74 /* XXX this one should correctly check for apple top header... */
75 static void wm_get_screensize(int *width_r, int *height_r)
78 unsigned int uiheight;
80 GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
85 /* keeps offset and size within monitor bounds */
86 /* XXX solve dual screen... */
87 static void wm_window_check_position(rcti *rect)
91 wm_get_screensize(&width, &height);
107 if(rect->xmax > width) {
108 d= rect->xmax - width;
112 if(rect->ymax > height) {
113 d= rect->ymax - height;
118 if(rect->xmin < 0) rect->xmin= 0;
119 if(rect->ymin < 0) rect->ymin= 0;
123 static void wm_ghostwindow_destroy(wmWindow *win)
126 GHOST_DisposeWindow(g_system, win->ghostwin);
131 /* including window itself, C can be NULL.
132 ED_screen_exit should have been called */
133 void wm_window_free(bContext *C, wmWindow *win)
135 wmTimer *wt, *wtnext;
139 wmWindowManager *wm= CTX_wm_manager(C);
141 if(wm->windrawable==win)
142 wm->windrawable= NULL;
143 if(wm->winactive==win)
145 if(CTX_wm_window(C)==win)
146 CTX_wm_window_set(C, NULL);
148 WM_event_remove_handlers(C, &win->handlers);
149 WM_event_remove_handlers(C, &win->modalhandlers);
151 /* end running jobs, a job end also removes its timer */
152 for(wt= win->timers.first; wt; wt= wtnext) {
154 if(wt->event_type==TIMERJOBS)
155 wm_jobs_timer_ended(wm, wt);
159 if(win->eventstate) MEM_freeN(win->eventstate);
161 /* timer removing, need to call this api function */
162 while((wt= win->timers.first))
163 WM_event_remove_window_timer(win, wt);
165 wm_event_free_all(win);
166 wm_subwindows_free(win);
169 MEM_freeN(win->drawdata);
171 wm_ghostwindow_destroy(win);
176 static int find_free_winid(wmWindowManager *wm)
181 for(win= wm->windows.first; win; win= win->next)
188 /* dont change context itself */
189 wmWindow *wm_window_new(bContext *C)
191 wmWindowManager *wm= CTX_wm_manager(C);
192 wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
194 BLI_addtail(&wm->windows, win);
195 win->winid= find_free_winid(wm);
201 /* part of wm_window.c api */
202 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
204 wmWindow *win= wm_window_new(C);
206 win->posx= winorig->posx+10;
207 win->posy= winorig->posy;
208 win->sizex= winorig->sizex;
209 win->sizey= winorig->sizey;
211 /* duplicate assigns to window */
212 win->screen= ED_screen_duplicate(win, winorig->screen);
213 BLI_strncpy(win->screenname, win->screen->id.name+2, 21);
214 win->screen->winid= win->winid;
216 win->screen->do_refresh= 1;
217 win->screen->do_draw= 1;
225 /* this is event from ghost, or exit-blender op */
226 void wm_window_close(bContext *C, wmWindow *win)
228 wmWindowManager *wm= CTX_wm_manager(C);
229 BLI_remlink(&wm->windows, win);
231 wm_draw_window_clear(win);
232 ED_screen_exit(C, win, win->screen);
233 wm_window_free(C, win);
235 /* check remaining windows */
236 if(wm->windows.first) {
237 for(win= wm->windows.first; win; win= win->next)
238 if(win->screen->full!=SCREENTEMP)
240 /* in this case we close all */
248 void wm_window_title(wmWindowManager *wm, wmWindow *win)
250 /* handle the 'temp' window */
251 if(win->screen && win->screen->full==SCREENTEMP) {
252 GHOST_SetTitle(win->ghostwin, "Blender");
256 /* this is set to 1 if you don't have .B.blend open */
258 char *str= MEM_mallocN(strlen(G.sce) + 16, "title");
261 sprintf(str, "Blender [%s]", G.sce);
263 sprintf(str, "Blender* [%s]", G.sce);
265 GHOST_SetTitle(win->ghostwin, str);
270 GHOST_SetTitle(win->ghostwin, "Blender");
274 GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
276 GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
281 /* belongs to below */
282 static void wm_window_add_ghostwindow(wmWindowManager *wm, char *title, wmWindow *win)
284 GHOST_WindowHandle ghostwin;
285 GHOST_TWindowState inital_state;
286 int scr_w, scr_h, posy;
288 wm_get_screensize(&scr_w, &scr_h);
289 posy= (scr_h - win->posy - win->sizey);
291 // inital_state = GHOST_kWindowStateFullScreen;
292 // inital_state = GHOST_kWindowStateMaximized;
293 inital_state = GHOST_kWindowStateNormal;
297 extern int macPrefState; /* creator.c */
298 inital_state += macPrefState;
302 ghostwin= GHOST_CreateWindow(g_system, title,
303 win->posx, posy, win->sizex, win->sizey,
305 GHOST_kDrawingContextTypeOpenGL,
310 win->ghostwin= ghostwin;
311 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
313 if(win->eventstate==NULL)
314 win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
316 /* until screens get drawn, make it nice grey */
317 glClearColor(.55, .55, .55, 0.0);
318 glClear(GL_COLOR_BUFFER_BIT);
319 wm_window_swap_buffers(win);
321 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
323 /* standard state vars for window */
324 glEnable(GL_SCISSOR_TEST);
330 /* for wmWindows without ghostwin, open these and clear */
331 /* window size is read from window, if 0 it uses prefsize */
332 /* called in wm_check, also inits stuff after file read */
333 void wm_window_add_ghostwindows(wmWindowManager *wm)
338 /* no commandline prefsize? then we set this */
340 wm_get_screensize(&prefsizx, &prefsizy);
344 extern void wm_set_apple_prefsize(int, int); /* wm_apple.c */
346 wm_set_apple_prefsize(prefsizx, prefsizy);
355 for(win= wm->windows.first; win; win= win->next) {
356 if(win->ghostwin==NULL) {
360 win->sizex= prefsizx;
361 win->sizey= prefsizy;
364 wm_window_add_ghostwindow(wm, "Blender", win);
366 /* happens after fileread */
367 if(win->eventstate==NULL)
368 win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
370 /* add keymap handlers (1 handler for all keys in map!) */
371 keymap= WM_keymap_find(wm, "Window", 0, 0);
372 WM_event_add_keymap_handler(&win->handlers, keymap);
374 keymap= WM_keymap_find(wm, "Screen", 0, 0);
375 WM_event_add_keymap_handler(&win->handlers, keymap);
377 keymap= WM_keymap_find(wm, "Screen Editing", 0, 0);
378 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
380 wm_window_title(wm, win);
384 /* new window, no screen yet, but we open ghostwindow for it */
385 /* also gets the window level handlers */
386 /* area-rip calls this */
387 wmWindow *WM_window_open(bContext *C, rcti *rect)
389 wmWindow *win= wm_window_new(C);
391 win->posx= rect->xmin;
392 win->posy= rect->ymin;
393 win->sizex= rect->xmax - rect->xmin;
394 win->sizey= rect->ymax - rect->ymin;
404 /* uses screen->full tag to define what to do, currently it limits
405 to only one "temp" window for render out, preferences, filewindow, etc */
406 /* type is #define in WM_api.h */
408 void WM_window_open_temp(bContext *C, rcti *position, int type)
413 /* changes rect to fit within desktop */
414 wm_window_check_position(position);
416 /* test if we have a temp screen already */
417 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next)
418 if(win->screen->full == SCREENTEMP)
421 /* add new window? */
423 win= wm_window_new(C);
425 win->posx= position->xmin;
426 win->posy= position->ymin;
429 win->sizex= position->xmax - position->xmin;
430 win->sizey= position->ymax - position->ymin;
433 wm_window_set_size(win, win->sizex, win->sizey) ;
434 wm_window_raise(win);
437 /* add new screen? */
438 if(win->screen==NULL)
439 win->screen= ED_screen_add(win, CTX_data_scene(C), "temp");
440 win->screen->full = SCREENTEMP;
442 /* make window active, and validate/resize */
443 CTX_wm_window_set(C, win);
446 /* ensure it shows the right spacetype editor */
447 sa= win->screen->areabase.first;
448 CTX_wm_area_set(C, sa);
450 if(type==WM_WINDOW_RENDER) {
451 ED_area_newspace(C, sa, SPACE_IMAGE);
454 ED_area_newspace(C, sa, SPACE_USERPREF);
457 ED_screen_set(C, win->screen);
459 if(sa->spacetype==SPACE_IMAGE)
460 GHOST_SetTitle(win->ghostwin, "Blender Render");
461 else if(ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
462 GHOST_SetTitle(win->ghostwin, "Blender User Preferences");
463 else if(sa->spacetype==SPACE_FILE)
464 GHOST_SetTitle(win->ghostwin, "Blender File View");
466 GHOST_SetTitle(win->ghostwin, "Blender");
470 /* ****************** Operators ****************** */
472 /* operator callback */
473 int wm_window_duplicate_op(bContext *C, wmOperator *op)
475 wm_window_copy(C, CTX_wm_window(C));
478 return OPERATOR_FINISHED;
482 /* fullscreen operator callback */
483 int wm_window_fullscreen_toggle_op(bContext *C, wmOperator *op)
485 wmWindow *window= CTX_wm_window(C);
486 GHOST_TWindowState state = GHOST_GetWindowState(window->ghostwin);
487 if(state!=GHOST_kWindowStateFullScreen)
488 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
490 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
492 return OPERATOR_FINISHED;
497 /* ************ events *************** */
499 static int query_qual(char qual)
501 GHOST_TModifierKeyMask left, right;
505 left= GHOST_kModifierKeyLeftShift;
506 right= GHOST_kModifierKeyRightShift;
507 } else if (qual=='c') {
508 left= GHOST_kModifierKeyLeftControl;
509 right= GHOST_kModifierKeyRightControl;
510 } else if (qual=='C') {
511 left= right= GHOST_kModifierKeyCommand;
513 left= GHOST_kModifierKeyLeftAlt;
514 right= GHOST_kModifierKeyRightAlt;
517 GHOST_GetModifierKeyState(g_system, left, &val);
519 GHOST_GetModifierKeyState(g_system, right, &val);
524 void wm_window_make_drawable(bContext *C, wmWindow *win)
526 wmWindowManager *wm= CTX_wm_manager(C);
528 if (win != wm->windrawable && win->ghostwin) {
529 // win->lmbut= 0; /* keeps hanging when mousepressed while other window opened */
531 wm->windrawable= win;
532 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
533 GHOST_ActivateWindowDrawingContext(win->ghostwin);
537 /* called by ghost, here we handle events for windows themselves or send to event system */
538 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private)
540 bContext *C= private;
541 GHOST_TEventType type= GHOST_GetEventType(evt);
543 if (type == GHOST_kEventQuit) {
546 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
547 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
551 // XXX - should be checked, why are we getting an event here, and
555 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
556 // XXX - should be checked, why are we getting an event here, and
561 win= GHOST_GetWindowUserData(ghostwin);
565 case GHOST_kEventWindowDeactivate:
566 win->active= 0; /* XXX */
568 case GHOST_kEventWindowActivate:
570 GHOST_TEventKeyData kdata;
573 CTX_wm_manager(C)->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
576 // window_handle(win, INPUTCHANGE, win->active);
578 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
580 if (win->eventstate->shift && !query_qual('s')) {
581 kdata.key= GHOST_kKeyLeftShift;
582 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
584 if (win->eventstate->ctrl && !query_qual('c')) {
585 kdata.key= GHOST_kKeyLeftControl;
586 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
588 if (win->eventstate->alt && !query_qual('a')) {
589 kdata.key= GHOST_kKeyLeftAlt;
590 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
592 if (win->eventstate->oskey && !query_qual('C')) {
593 kdata.key= GHOST_kKeyCommand;
594 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
596 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
597 win->eventstate->keymodifier= 0;
599 /* entering window, update mouse pos. but no event */
600 GHOST_GetCursorPosition(g_system, &wx, &wy);
602 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
603 win->eventstate->x= cx;
604 win->eventstate->y= (win->sizey-1) - cy;
606 wm_window_make_drawable(C, win);
609 case GHOST_kEventWindowClose: {
610 wm_window_close(C, win);
613 case GHOST_kEventWindowUpdate: {
614 if(G.f & G_DEBUG) printf("ghost redraw\n");
616 wm_window_make_drawable(C, win);
617 WM_event_add_notifier(C, NC_WINDOW, NULL);
621 case GHOST_kEventWindowSize:
622 case GHOST_kEventWindowMove: {
623 GHOST_TWindowState state;
624 state = GHOST_GetWindowState(win->ghostwin);
626 /* win32: gives undefined window size when minimized */
627 if(state!=GHOST_kWindowStateMinimized) {
628 GHOST_RectangleHandle client_rect;
629 int l, t, r, b, scr_w, scr_h;
630 int sizex, sizey, posx, posy;
632 client_rect= GHOST_GetClientBounds(win->ghostwin);
633 GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
635 GHOST_DisposeRectangle(client_rect);
637 wm_get_screensize(&scr_w, &scr_h);
641 posy= scr_h - t - win->sizey;
644 * Ghost sometimes send size or move events when the window hasn't changed.
645 * One case of this is using compiz on linux. To alleviate the problem
646 * we ignore all such event here.
648 * It might be good to eventually do that at Ghost level, but that is for
651 if (win->sizex != sizex ||
652 win->sizey != sizey ||
663 state = GHOST_GetWindowState(win->ghostwin);
665 if(state==GHOST_kWindowStateNormal) {
666 if(G.f & G_DEBUG) printf("window state: normal\n");
668 else if(state==GHOST_kWindowStateMinimized) {
669 if(G.f & G_DEBUG) printf("window state: minimized\n");
671 else if(state==GHOST_kWindowStateMaximized) {
672 if(G.f & G_DEBUG) printf("window state: maximized\n");
674 else if(state==GHOST_kWindowStateFullScreen) {
675 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
678 if(type!=GHOST_kEventWindowSize) {
679 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
684 wm_window_make_drawable(C, win);
685 wm_draw_window_clear(win);
686 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
692 wm_event_add_ghostevent(win, type, data);
701 /* This timer system only gives maximum 1 timer event per redraw cycle,
702 to prevent queues to get overloaded.
703 Timer handlers should check for delta to decide if they just
704 update, or follow real time.
705 Timer handlers can also set duration to match frames passed
707 static int wm_window_timer(const bContext *C)
709 wmWindowManager *wm= CTX_wm_manager(C);
711 double time= PIL_check_seconds_timer();
714 for(win= wm->windows.first; win; win= win->next) {
716 for(wt= win->timers.first; wt; wt= wt->next) {
718 if(time > wt->ntime) {
719 wmEvent event= *(win->eventstate);
721 wt->delta= time - wt->ltime;
722 wt->duration += wt->delta;
724 wt->ntime= wt->stime + wt->timestep*ceil(wt->duration/wt->timestep);
726 event.type= wt->event_type;
727 event.custom= EVT_DATA_TIMER;
728 event.customdata= wt;
729 wm_event_add(win, &event);
739 void wm_window_process_events(const bContext *C)
741 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
744 GHOST_DispatchEvents(g_system);
746 hasevent |= wm_window_timer(C);
748 /* no event, we sleep 5 milliseconds */
753 void wm_window_process_events_nosleep(const bContext *C)
755 if(GHOST_ProcessEvents(g_system, 0))
756 GHOST_DispatchEvents(g_system);
759 /* exported as handle callback to bke blender.c */
760 void wm_window_testbreak(void)
762 static double ltime= 0;
763 double curtime= PIL_check_seconds_timer();
765 /* only check for breaks every 50 milliseconds
766 * if we get called more often.
768 if ((curtime-ltime)>.05) {
769 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
772 GHOST_DispatchEvents(g_system);
778 /* **************** init ********************** */
780 void wm_ghost_init(bContext *C)
783 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
785 g_system= GHOST_CreateSystem();
786 GHOST_AddEventConsumer(g_system, consumer);
790 void wm_ghost_exit(void)
793 GHOST_DisposeSystem(g_system);
798 /* **************** timer ********************** */
800 /* to (de)activate running timers temporary */
801 void WM_event_window_timer_sleep(wmWindow *win, wmTimer *timer, int dosleep)
805 for(wt= win->timers.first; wt; wt= wt->next)
813 wmTimer *WM_event_add_window_timer(wmWindow *win, int event_type, double timestep)
815 wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
817 wt->event_type= event_type;
818 wt->ltime= PIL_check_seconds_timer();
819 wt->ntime= wt->ltime + timestep;
820 wt->stime= wt->ltime;
821 wt->timestep= timestep;
823 BLI_addtail(&win->timers, wt);
828 void WM_event_remove_window_timer(wmWindow *win, wmTimer *timer)
832 /* extra security check */
833 for(wt= win->timers.first; wt; wt= wt->next)
838 BLI_remlink(&win->timers, wt);
840 MEM_freeN(wt->customdata);
845 /* ******************* clipboard **************** */
847 char *WM_clipboard_text_get(int selection)
849 char *p, *p2, *buf, *newbuf;
851 buf= (char*)GHOST_getClipboard(selection);
855 /* always convert from \r\n to \n */
856 newbuf= MEM_callocN(strlen(buf)+1, "WM_clipboard_text_get");
858 for(p= buf, p2= newbuf; *p; p++) {
864 free(buf); /* ghost uses regular malloc */
869 void WM_clipboard_text_set(char *buf, int selection)
872 /* do conversion from \n to \r\n on Windows */
873 char *p, *p2, *newbuf;
876 for(p= buf; *p; p++) {
883 newbuf= MEM_callocN(newlen+1, "WM_clipboard_text_set");
885 for(p= buf, p2= newbuf; *p; p++, p2++) {
887 *(p2++)= '\r'; *p2= '\n';
893 GHOST_putClipboard((GHOST_TInt8*)newbuf, selection);
896 GHOST_putClipboard((GHOST_TInt8*)buf, selection);
900 /* ************************************ */
902 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r)
908 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r)
910 *width_r= win->sizex;
911 *height_r= win->sizey;
914 void wm_window_set_size(wmWindow *win, int width, int height)
916 GHOST_SetClientSize(win->ghostwin, width, height);
919 void wm_window_lower(wmWindow *win)
921 GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
924 void wm_window_raise(wmWindow *win)
926 GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
929 void wm_window_swap_buffers(wmWindow *win)
933 glDisable(GL_SCISSOR_TEST);
934 GHOST_SwapWindowBuffers(win->ghostwin);
935 glEnable(GL_SCISSOR_TEST);
937 GHOST_SwapWindowBuffers(win->ghostwin);
941 /* ******************* exported api ***************** */
944 /* called whem no ghost system was initialized */
945 void WM_setprefsize(int stax, int stay, int sizx, int sizy)