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 *****
33 #include "DNA_listBase.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_windowmanager_types.h"
37 #include "MEM_guardedalloc.h"
39 #include "GHOST_C-api.h"
41 #include "BLI_blenlib.h"
43 #include "BKE_blender.h"
44 #include "BKE_context.h"
45 #include "BKE_global.h"
46 #include "BKE_utildefines.h"
54 #include "wm_window.h"
55 #include "wm_subwindow.h"
56 #include "wm_event_system.h"
58 #include "ED_screen.h"
64 /* the global to talk to ghost */
65 GHOST_SystemHandle g_system= NULL;
67 /* set by commandline */
68 static int prefsizx= 0, prefsizy= 0, prefstax= 0, prefstay= 0;
71 /* ******** win open & close ************ */
73 /* XXX this one should correctly check for apple top header... */
74 static void wm_get_screensize(int *width_r, int *height_r)
77 unsigned int uiheight;
79 GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
84 /* keeps offset and size within monitor bounds */
85 /* XXX solve dual screen... */
86 static void wm_window_check_position(rcti *rect)
90 wm_get_screensize(&width, &height);
106 if(rect->xmax > width) {
107 d= rect->xmax - width;
111 if(rect->ymax > height) {
112 d= rect->ymax - height;
117 if(rect->xmin < 0) rect->xmin= 0;
118 if(rect->ymin < 0) rect->ymin= 0;
122 static void wm_ghostwindow_destroy(wmWindow *win)
125 GHOST_DisposeWindow(g_system, win->ghostwin);
130 /* including window itself, C can be NULL.
131 ED_screen_exit should have been called */
132 void wm_window_free(bContext *C, wmWindow *win)
134 wmTimer *wt, *wtnext;
138 wmWindowManager *wm= CTX_wm_manager(C);
140 if(wm->windrawable==win)
141 wm->windrawable= NULL;
142 if(wm->winactive==win)
144 if(CTX_wm_window(C)==win)
145 CTX_wm_window_set(C, NULL);
147 WM_event_remove_handlers(C, &win->handlers);
149 /* end running jobs, a job end also removes its timer */
150 for(wt= win->timers.first; wt; wt= wtnext) {
152 if(wt->event_type==TIMERJOBS)
153 wm_jobs_timer_ended(wm, wt);
157 if(win->eventstate) MEM_freeN(win->eventstate);
159 /* timer removing, need to call this api function */
160 while((wt= win->timers.first))
161 WM_event_remove_window_timer(win, wt);
163 wm_event_free_all(win);
164 wm_subwindows_free(win);
167 MEM_freeN(win->drawdata);
169 wm_ghostwindow_destroy(win);
174 static int find_free_winid(wmWindowManager *wm)
179 for(win= wm->windows.first; win; win= win->next)
186 /* dont change context itself */
187 wmWindow *wm_window_new(bContext *C)
189 wmWindowManager *wm= CTX_wm_manager(C);
190 wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
192 BLI_addtail(&wm->windows, win);
193 win->winid= find_free_winid(wm);
199 /* part of wm_window.c api */
200 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
202 wmWindow *win= wm_window_new(C);
204 win->posx= winorig->posx+10;
205 win->posy= winorig->posy;
206 win->sizex= winorig->sizex;
207 win->sizey= winorig->sizey;
209 /* duplicate assigns to window */
210 win->screen= ED_screen_duplicate(win, winorig->screen);
211 BLI_strncpy(win->screenname, win->screen->id.name+2, 21);
212 win->screen->winid= win->winid;
214 win->screen->do_refresh= 1;
215 win->screen->do_draw= 1;
223 /* this is event from ghost, or exit-blender op */
224 void wm_window_close(bContext *C, wmWindow *win)
226 wmWindowManager *wm= CTX_wm_manager(C);
227 BLI_remlink(&wm->windows, win);
229 wm_draw_window_clear(win);
230 ED_screen_exit(C, win, win->screen);
231 wm_window_free(C, win);
233 /* check remaining windows */
234 if(wm->windows.first) {
235 for(win= wm->windows.first; win; win= win->next)
236 if(win->screen->full!=SCREENTEMP)
238 /* in this case we close all */
246 void wm_window_title(wmWindowManager *wm, wmWindow *win)
248 /* handle the 'temp' window */
249 if(win->screen && win->screen->full==SCREENTEMP) {
250 GHOST_SetTitle(win->ghostwin, "Blender");
254 /* this is set to 1 if you don't have .B.blend open */
256 char *str= MEM_mallocN(strlen(G.sce) + 16, "title");
259 sprintf(str, "Blender [%s]", G.sce);
261 sprintf(str, "Blender* [%s]", G.sce);
263 GHOST_SetTitle(win->ghostwin, str);
268 GHOST_SetTitle(win->ghostwin, "Blender");
272 GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
274 GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
279 /* belongs to below */
280 static void wm_window_add_ghostwindow(wmWindowManager *wm, char *title, wmWindow *win)
282 GHOST_WindowHandle ghostwin;
283 GHOST_TWindowState inital_state;
284 int scr_w, scr_h, posy;
286 wm_get_screensize(&scr_w, &scr_h);
287 posy= (scr_h - win->posy - win->sizey);
289 // inital_state = GHOST_kWindowStateFullScreen;
290 // inital_state = GHOST_kWindowStateMaximized;
291 inital_state = GHOST_kWindowStateNormal;
295 extern int macPrefState; /* creator.c */
296 inital_state += macPrefState;
300 ghostwin= GHOST_CreateWindow(g_system, title,
301 win->posx, posy, win->sizex, win->sizey,
303 GHOST_kDrawingContextTypeOpenGL,
308 win->ghostwin= ghostwin;
309 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
311 if(win->eventstate==NULL)
312 win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
314 /* until screens get drawn, make it nice grey */
315 glClearColor(.55, .55, .55, 0.0);
316 glClear(GL_COLOR_BUFFER_BIT);
317 wm_window_swap_buffers(win);
319 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
321 /* standard state vars for window */
322 glEnable(GL_SCISSOR_TEST);
328 /* for wmWindows without ghostwin, open these and clear */
329 /* window size is read from window, if 0 it uses prefsize */
330 /* called in wm_check, also inits stuff after file read */
331 void wm_window_add_ghostwindows(wmWindowManager *wm)
336 /* no commandline prefsize? then we set this */
338 wm_get_screensize(&prefsizx, &prefsizy);
342 extern void wm_set_apple_prefsize(int, int); /* wm_apple.c */
344 wm_set_apple_prefsize(prefsizx, prefsizy);
353 for(win= wm->windows.first; win; win= win->next) {
354 if(win->ghostwin==NULL) {
358 win->sizex= prefsizx;
359 win->sizey= prefsizy;
362 wm_window_add_ghostwindow(wm, "Blender", win);
364 /* happens after fileread */
365 if(win->eventstate==NULL)
366 win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
368 /* add keymap handlers (1 handler for all keys in map!) */
369 keymap= WM_keymap_listbase(wm, "Window", 0, 0);
370 WM_event_add_keymap_handler(&win->handlers, keymap);
372 keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
373 WM_event_add_keymap_handler(&win->handlers, keymap);
375 wm_window_title(wm, win);
379 /* new window, no screen yet, but we open ghostwindow for it */
380 /* also gets the window level handlers */
381 /* area-rip calls this */
382 wmWindow *WM_window_open(bContext *C, rcti *rect)
384 wmWindow *win= wm_window_new(C);
386 win->posx= rect->xmin;
387 win->posy= rect->ymin;
388 win->sizex= rect->xmax - rect->xmin;
389 win->sizey= rect->ymax - rect->ymin;
399 /* uses screen->full tag to define what to do, currently it limits
400 to only one "temp" window for render out, preferences, filewindow, etc */
401 /* type is #define in WM_api.h */
403 void WM_window_open_temp(bContext *C, rcti *position, int type)
408 /* changes rect to fit within desktop */
409 wm_window_check_position(position);
411 /* test if we have a temp screen already */
412 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next)
413 if(win->screen->full == SCREENTEMP)
416 /* add new window? */
418 win= wm_window_new(C);
420 win->posx= position->xmin;
421 win->posy= position->ymin;
424 win->sizex= position->xmax - position->xmin;
425 win->sizey= position->ymax - position->ymin;
428 wm_window_set_size(win, win->sizex, win->sizey) ;
429 wm_window_raise(win);
432 /* add new screen? */
433 if(win->screen==NULL)
434 win->screen= ED_screen_add(win, CTX_data_scene(C), "temp");
435 win->screen->full = SCREENTEMP;
437 /* make window active, and validate/resize */
438 CTX_wm_window_set(C, win);
441 /* ensure it shows the right spacetype editor */
442 sa= win->screen->areabase.first;
443 CTX_wm_area_set(C, sa);
445 if(type==WM_WINDOW_RENDER) {
446 ED_area_newspace(C, sa, SPACE_IMAGE);
449 ED_area_newspace(C, sa, SPACE_USERPREF);
452 ED_screen_set(C, win->screen);
454 if(sa->spacetype==SPACE_IMAGE)
455 GHOST_SetTitle(win->ghostwin, "Blender Render");
456 else if(ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
457 GHOST_SetTitle(win->ghostwin, "Blender User Preferences");
458 else if(sa->spacetype==SPACE_FILE)
459 GHOST_SetTitle(win->ghostwin, "Blender File View");
461 GHOST_SetTitle(win->ghostwin, "Blender");
465 /* ****************** Operators ****************** */
467 /* operator callback */
468 int wm_window_duplicate_op(bContext *C, wmOperator *op)
470 wm_window_copy(C, CTX_wm_window(C));
473 return OPERATOR_FINISHED;
477 /* fullscreen operator callback */
478 int wm_window_fullscreen_toggle_op(bContext *C, wmOperator *op)
480 wmWindow *window= CTX_wm_window(C);
481 GHOST_TWindowState state = GHOST_GetWindowState(window->ghostwin);
482 if(state!=GHOST_kWindowStateFullScreen)
483 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
485 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
487 return OPERATOR_FINISHED;
492 /* ************ events *************** */
494 static int query_qual(char qual)
496 GHOST_TModifierKeyMask left, right;
500 left= GHOST_kModifierKeyLeftShift;
501 right= GHOST_kModifierKeyRightShift;
502 } else if (qual=='c') {
503 left= GHOST_kModifierKeyLeftControl;
504 right= GHOST_kModifierKeyRightControl;
505 } else if (qual=='C') {
506 left= right= GHOST_kModifierKeyCommand;
508 left= GHOST_kModifierKeyLeftAlt;
509 right= GHOST_kModifierKeyRightAlt;
512 GHOST_GetModifierKeyState(g_system, left, &val);
514 GHOST_GetModifierKeyState(g_system, right, &val);
519 void wm_window_make_drawable(bContext *C, wmWindow *win)
521 wmWindowManager *wm= CTX_wm_manager(C);
523 if (win != wm->windrawable && win->ghostwin) {
524 // win->lmbut= 0; /* keeps hanging when mousepressed while other window opened */
526 wm->windrawable= win;
527 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
528 GHOST_ActivateWindowDrawingContext(win->ghostwin);
532 /* called by ghost, here we handle events for windows themselves or send to event system */
533 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private)
535 bContext *C= private;
536 GHOST_TEventType type= GHOST_GetEventType(evt);
538 if (type == GHOST_kEventQuit) {
541 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
542 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
546 // XXX - should be checked, why are we getting an event here, and
550 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
551 // XXX - should be checked, why are we getting an event here, and
556 win= GHOST_GetWindowUserData(ghostwin);
560 case GHOST_kEventWindowDeactivate:
561 win->active= 0; /* XXX */
563 case GHOST_kEventWindowActivate:
565 GHOST_TEventKeyData kdata;
568 CTX_wm_manager(C)->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
571 // window_handle(win, INPUTCHANGE, win->active);
573 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
575 if (win->eventstate->shift && !query_qual('s')) {
576 kdata.key= GHOST_kKeyLeftShift;
577 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
579 if (win->eventstate->ctrl && !query_qual('c')) {
580 kdata.key= GHOST_kKeyLeftControl;
581 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
583 if (win->eventstate->alt && !query_qual('a')) {
584 kdata.key= GHOST_kKeyLeftAlt;
585 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
587 if (win->eventstate->oskey && !query_qual('C')) {
588 kdata.key= GHOST_kKeyCommand;
589 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
591 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
592 win->eventstate->keymodifier= 0;
594 /* entering window, update mouse pos. but no event */
595 GHOST_GetCursorPosition(g_system, &wx, &wy);
597 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
598 win->eventstate->x= cx;
599 win->eventstate->y= (win->sizey-1) - cy;
601 wm_window_make_drawable(C, win);
604 case GHOST_kEventWindowClose: {
605 wm_window_close(C, win);
608 case GHOST_kEventWindowUpdate: {
609 if(G.f & G_DEBUG) printf("ghost redraw\n");
611 wm_window_make_drawable(C, win);
612 WM_event_add_notifier(C, NC_WINDOW, NULL);
616 case GHOST_kEventWindowSize:
617 case GHOST_kEventWindowMove: {
618 GHOST_TWindowState state;
619 state = GHOST_GetWindowState(win->ghostwin);
621 /* win32: gives undefined window size when minimized */
622 if(state!=GHOST_kWindowStateMinimized) {
623 GHOST_RectangleHandle client_rect;
624 int l, t, r, b, scr_w, scr_h;
626 client_rect= GHOST_GetClientBounds(win->ghostwin);
627 GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
629 GHOST_DisposeRectangle(client_rect);
631 wm_get_screensize(&scr_w, &scr_h);
635 win->posy= scr_h - t - win->sizey;
639 state = GHOST_GetWindowState(win->ghostwin);
641 if(state==GHOST_kWindowStateNormal) {
642 if(G.f & G_DEBUG) printf("window state: normal\n");
644 else if(state==GHOST_kWindowStateMinimized) {
645 if(G.f & G_DEBUG) printf("window state: minimized\n");
647 else if(state==GHOST_kWindowStateMaximized) {
648 if(G.f & G_DEBUG) printf("window state: maximized\n");
650 else if(state==GHOST_kWindowStateFullScreen) {
651 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
654 if(type!=GHOST_kEventWindowSize) {
655 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
660 wm_window_make_drawable(C, win);
661 wm_draw_window_clear(win);
662 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
667 wm_event_add_ghostevent(win, type, data);
676 /* This timer system only gives maximum 1 timer event per redraw cycle,
677 to prevent queues to get overloaded.
678 Timer handlers should check for delta to decide if they just
679 update, or follow real time.
680 Timer handlers can also set duration to match frames passed
682 static int wm_window_timer(const bContext *C)
684 wmWindowManager *wm= CTX_wm_manager(C);
686 double time= PIL_check_seconds_timer();
689 for(win= wm->windows.first; win; win= win->next) {
691 for(wt= win->timers.first; wt; wt= wt->next) {
693 if(wt->timestep < time - wt->ltime) {
694 wmEvent event= *(win->eventstate);
696 wt->delta= time - wt->ltime;
697 wt->duration += wt->delta;
700 event.type= wt->event_type;
701 event.custom= EVT_DATA_TIMER;
702 event.customdata= wt;
703 wm_event_add(win, &event);
713 void wm_window_process_events(const bContext *C)
715 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
718 GHOST_DispatchEvents(g_system);
720 hasevent |= wm_window_timer(C);
722 /* no event, we sleep 5 milliseconds */
727 void wm_window_process_events_nosleep(const bContext *C)
729 if(GHOST_ProcessEvents(g_system, 0))
730 GHOST_DispatchEvents(g_system);
733 /* exported as handle callback to bke blender.c */
734 void wm_window_testbreak(void)
736 static double ltime= 0;
737 double curtime= PIL_check_seconds_timer();
739 /* only check for breaks every 50 milliseconds
740 * if we get called more often.
742 if ((curtime-ltime)>.05) {
743 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
746 GHOST_DispatchEvents(g_system);
752 /* **************** init ********************** */
754 void wm_ghost_init(bContext *C)
757 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
759 g_system= GHOST_CreateSystem();
760 GHOST_AddEventConsumer(g_system, consumer);
764 void wm_ghost_exit(void)
767 GHOST_DisposeSystem(g_system);
772 /* **************** timer ********************** */
774 /* to (de)activate running timers temporary */
775 void WM_event_window_timer_sleep(wmWindow *win, wmTimer *timer, int dosleep)
779 for(wt= win->timers.first; wt; wt= wt->next)
787 wmTimer *WM_event_add_window_timer(wmWindow *win, int event_type, double timestep)
789 wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
791 wt->event_type= event_type;
792 wt->ltime= PIL_check_seconds_timer();
793 wt->timestep= timestep;
795 BLI_addtail(&win->timers, wt);
800 void WM_event_remove_window_timer(wmWindow *win, wmTimer *timer)
804 /* extra security check */
805 for(wt= win->timers.first; wt; wt= wt->next)
810 BLI_remlink(&win->timers, wt);
812 MEM_freeN(wt->customdata);
817 /* ******************* clipboard **************** */
819 char *WM_clipboard_text_get(int selection)
821 char *p, *p2, *buf, *newbuf;
823 buf= (char*)GHOST_getClipboard(selection);
827 /* always convert from \r\n to \n */
828 newbuf= MEM_callocN(strlen(buf)+1, "WM_clipboard_text_get");
830 for(p= buf, p2= newbuf; *p; p++) {
836 free(buf); /* ghost uses regular malloc */
841 void WM_clipboard_text_set(char *buf, int selection)
844 /* do conversion from \n to \r\n on Windows */
845 char *p, *p2, *newbuf;
848 for(p= buf; *p; p++) {
855 newbuf= MEM_callocN(newlen+1, "WM_clipboard_text_set");
857 for(p= buf, p2= newbuf; *p; p++, p2++) {
859 *(p2++)= '\r'; *p2= '\n';
865 GHOST_putClipboard((GHOST_TInt8*)newbuf, selection);
868 GHOST_putClipboard((GHOST_TInt8*)buf, selection);
872 /* ************************************ */
874 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r)
880 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r)
882 *width_r= win->sizex;
883 *height_r= win->sizey;
886 void wm_window_set_size(wmWindow *win, int width, int height)
888 GHOST_SetClientSize(win->ghostwin, width, height);
891 void wm_window_lower(wmWindow *win)
893 GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
896 void wm_window_raise(wmWindow *win)
898 GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
901 void wm_window_swap_buffers(wmWindow *win)
905 glDisable(GL_SCISSOR_TEST);
906 GHOST_SwapWindowBuffers(win->ghostwin);
907 glEnable(GL_SCISSOR_TEST);
909 GHOST_SwapWindowBuffers(win->ghostwin);
913 /* ******************* exported api ***************** */
916 /* called whem no ghost system was initialized */
917 void WM_setprefsize(int stax, int stay, int sizx, int sizy)