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);
150 /* end running jobs, a job end also removes its timer */
151 for(wt= win->timers.first; wt; wt= wtnext) {
153 if(wt->event_type==TIMERJOBS)
154 wm_jobs_timer_ended(wm, wt);
158 if(win->eventstate) MEM_freeN(win->eventstate);
160 /* timer removing, need to call this api function */
161 while((wt= win->timers.first))
162 WM_event_remove_window_timer(win, wt);
164 wm_event_free_all(win);
165 wm_subwindows_free(win);
168 MEM_freeN(win->drawdata);
170 wm_ghostwindow_destroy(win);
175 static int find_free_winid(wmWindowManager *wm)
180 for(win= wm->windows.first; win; win= win->next)
187 /* dont change context itself */
188 wmWindow *wm_window_new(bContext *C)
190 wmWindowManager *wm= CTX_wm_manager(C);
191 wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
193 BLI_addtail(&wm->windows, win);
194 win->winid= find_free_winid(wm);
200 /* part of wm_window.c api */
201 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
203 wmWindow *win= wm_window_new(C);
205 win->posx= winorig->posx+10;
206 win->posy= winorig->posy;
207 win->sizex= winorig->sizex;
208 win->sizey= winorig->sizey;
210 /* duplicate assigns to window */
211 win->screen= ED_screen_duplicate(win, winorig->screen);
212 BLI_strncpy(win->screenname, win->screen->id.name+2, 21);
213 win->screen->winid= win->winid;
215 win->screen->do_refresh= 1;
216 win->screen->do_draw= 1;
224 /* this is event from ghost, or exit-blender op */
225 void wm_window_close(bContext *C, wmWindow *win)
227 wmWindowManager *wm= CTX_wm_manager(C);
228 BLI_remlink(&wm->windows, win);
230 wm_draw_window_clear(win);
231 ED_screen_exit(C, win, win->screen);
232 wm_window_free(C, win);
234 /* check remaining windows */
235 if(wm->windows.first) {
236 for(win= wm->windows.first; win; win= win->next)
237 if(win->screen->full!=SCREENTEMP)
239 /* in this case we close all */
247 void wm_window_title(wmWindowManager *wm, wmWindow *win)
249 /* handle the 'temp' window */
250 if(win->screen && win->screen->full==SCREENTEMP) {
251 GHOST_SetTitle(win->ghostwin, "Blender");
255 /* this is set to 1 if you don't have .B.blend open */
257 char *str= MEM_mallocN(strlen(G.sce) + 16, "title");
260 sprintf(str, "Blender [%s]", G.sce);
262 sprintf(str, "Blender* [%s]", G.sce);
264 GHOST_SetTitle(win->ghostwin, str);
269 GHOST_SetTitle(win->ghostwin, "Blender");
273 GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
275 GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
280 /* belongs to below */
281 static void wm_window_add_ghostwindow(wmWindowManager *wm, char *title, wmWindow *win)
283 GHOST_WindowHandle ghostwin;
284 GHOST_TWindowState inital_state;
285 int scr_w, scr_h, posy;
287 wm_get_screensize(&scr_w, &scr_h);
288 posy= (scr_h - win->posy - win->sizey);
290 // inital_state = GHOST_kWindowStateFullScreen;
291 // inital_state = GHOST_kWindowStateMaximized;
292 inital_state = GHOST_kWindowStateNormal;
296 extern int macPrefState; /* creator.c */
297 inital_state += macPrefState;
301 ghostwin= GHOST_CreateWindow(g_system, title,
302 win->posx, posy, win->sizex, win->sizey,
304 GHOST_kDrawingContextTypeOpenGL,
309 win->ghostwin= ghostwin;
310 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
312 if(win->eventstate==NULL)
313 win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
315 /* until screens get drawn, make it nice grey */
316 glClearColor(.55, .55, .55, 0.0);
317 glClear(GL_COLOR_BUFFER_BIT);
318 wm_window_swap_buffers(win);
320 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
322 /* standard state vars for window */
323 glEnable(GL_SCISSOR_TEST);
329 /* for wmWindows without ghostwin, open these and clear */
330 /* window size is read from window, if 0 it uses prefsize */
331 /* called in wm_check, also inits stuff after file read */
332 void wm_window_add_ghostwindows(wmWindowManager *wm)
337 /* no commandline prefsize? then we set this */
339 wm_get_screensize(&prefsizx, &prefsizy);
343 extern void wm_set_apple_prefsize(int, int); /* wm_apple.c */
345 wm_set_apple_prefsize(prefsizx, prefsizy);
354 for(win= wm->windows.first; win; win= win->next) {
355 if(win->ghostwin==NULL) {
359 win->sizex= prefsizx;
360 win->sizey= prefsizy;
363 wm_window_add_ghostwindow(wm, "Blender", win);
365 /* happens after fileread */
366 if(win->eventstate==NULL)
367 win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
369 /* add keymap handlers (1 handler for all keys in map!) */
370 keymap= WM_keymap_listbase(wm, "Window", 0, 0);
371 WM_event_add_keymap_handler(&win->handlers, keymap);
373 keymap= WM_keymap_listbase(wm, "Screen", 0, 0);
374 WM_event_add_keymap_handler(&win->handlers, keymap);
376 wm_window_title(wm, win);
380 /* new window, no screen yet, but we open ghostwindow for it */
381 /* also gets the window level handlers */
382 /* area-rip calls this */
383 wmWindow *WM_window_open(bContext *C, rcti *rect)
385 wmWindow *win= wm_window_new(C);
387 win->posx= rect->xmin;
388 win->posy= rect->ymin;
389 win->sizex= rect->xmax - rect->xmin;
390 win->sizey= rect->ymax - rect->ymin;
400 /* uses screen->full tag to define what to do, currently it limits
401 to only one "temp" window for render out, preferences, filewindow, etc */
402 /* type is #define in WM_api.h */
404 void WM_window_open_temp(bContext *C, rcti *position, int type)
409 /* changes rect to fit within desktop */
410 wm_window_check_position(position);
412 /* test if we have a temp screen already */
413 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next)
414 if(win->screen->full == SCREENTEMP)
417 /* add new window? */
419 win= wm_window_new(C);
421 win->posx= position->xmin;
422 win->posy= position->ymin;
425 win->sizex= position->xmax - position->xmin;
426 win->sizey= position->ymax - position->ymin;
429 wm_window_set_size(win, win->sizex, win->sizey) ;
430 wm_window_raise(win);
433 /* add new screen? */
434 if(win->screen==NULL)
435 win->screen= ED_screen_add(win, CTX_data_scene(C), "temp");
436 win->screen->full = SCREENTEMP;
438 /* make window active, and validate/resize */
439 CTX_wm_window_set(C, win);
442 /* ensure it shows the right spacetype editor */
443 sa= win->screen->areabase.first;
444 CTX_wm_area_set(C, sa);
446 if(type==WM_WINDOW_RENDER) {
447 ED_area_newspace(C, sa, SPACE_IMAGE);
450 ED_area_newspace(C, sa, SPACE_USERPREF);
453 ED_screen_set(C, win->screen);
455 if(sa->spacetype==SPACE_IMAGE)
456 GHOST_SetTitle(win->ghostwin, "Blender Render");
457 else if(ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
458 GHOST_SetTitle(win->ghostwin, "Blender User Preferences");
459 else if(sa->spacetype==SPACE_FILE)
460 GHOST_SetTitle(win->ghostwin, "Blender File View");
462 GHOST_SetTitle(win->ghostwin, "Blender");
466 /* ****************** Operators ****************** */
468 /* operator callback */
469 int wm_window_duplicate_op(bContext *C, wmOperator *op)
471 wm_window_copy(C, CTX_wm_window(C));
474 return OPERATOR_FINISHED;
478 /* fullscreen operator callback */
479 int wm_window_fullscreen_toggle_op(bContext *C, wmOperator *op)
481 wmWindow *window= CTX_wm_window(C);
482 GHOST_TWindowState state = GHOST_GetWindowState(window->ghostwin);
483 if(state!=GHOST_kWindowStateFullScreen)
484 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
486 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
488 return OPERATOR_FINISHED;
493 /* ************ events *************** */
495 static int query_qual(char qual)
497 GHOST_TModifierKeyMask left, right;
501 left= GHOST_kModifierKeyLeftShift;
502 right= GHOST_kModifierKeyRightShift;
503 } else if (qual=='c') {
504 left= GHOST_kModifierKeyLeftControl;
505 right= GHOST_kModifierKeyRightControl;
506 } else if (qual=='C') {
507 left= right= GHOST_kModifierKeyCommand;
509 left= GHOST_kModifierKeyLeftAlt;
510 right= GHOST_kModifierKeyRightAlt;
513 GHOST_GetModifierKeyState(g_system, left, &val);
515 GHOST_GetModifierKeyState(g_system, right, &val);
520 void wm_window_make_drawable(bContext *C, wmWindow *win)
522 wmWindowManager *wm= CTX_wm_manager(C);
524 if (win != wm->windrawable && win->ghostwin) {
525 // win->lmbut= 0; /* keeps hanging when mousepressed while other window opened */
527 wm->windrawable= win;
528 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
529 GHOST_ActivateWindowDrawingContext(win->ghostwin);
533 /* called by ghost, here we handle events for windows themselves or send to event system */
534 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private)
536 bContext *C= private;
537 GHOST_TEventType type= GHOST_GetEventType(evt);
539 if (type == GHOST_kEventQuit) {
542 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
543 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
547 // XXX - should be checked, why are we getting an event here, and
551 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
552 // XXX - should be checked, why are we getting an event here, and
557 win= GHOST_GetWindowUserData(ghostwin);
561 case GHOST_kEventWindowDeactivate:
562 win->active= 0; /* XXX */
564 case GHOST_kEventWindowActivate:
566 GHOST_TEventKeyData kdata;
569 CTX_wm_manager(C)->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
572 // window_handle(win, INPUTCHANGE, win->active);
574 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
576 if (win->eventstate->shift && !query_qual('s')) {
577 kdata.key= GHOST_kKeyLeftShift;
578 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
580 if (win->eventstate->ctrl && !query_qual('c')) {
581 kdata.key= GHOST_kKeyLeftControl;
582 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
584 if (win->eventstate->alt && !query_qual('a')) {
585 kdata.key= GHOST_kKeyLeftAlt;
586 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
588 if (win->eventstate->oskey && !query_qual('C')) {
589 kdata.key= GHOST_kKeyCommand;
590 wm_event_add_ghostevent(win, GHOST_kEventKeyUp, &kdata);
592 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
593 win->eventstate->keymodifier= 0;
595 /* entering window, update mouse pos. but no event */
596 GHOST_GetCursorPosition(g_system, &wx, &wy);
598 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
599 win->eventstate->x= cx;
600 win->eventstate->y= (win->sizey-1) - cy;
602 wm_window_make_drawable(C, win);
605 case GHOST_kEventWindowClose: {
606 wm_window_close(C, win);
609 case GHOST_kEventWindowUpdate: {
610 if(G.f & G_DEBUG) printf("ghost redraw\n");
612 wm_window_make_drawable(C, win);
613 WM_event_add_notifier(C, NC_WINDOW, NULL);
617 case GHOST_kEventWindowSize:
618 case GHOST_kEventWindowMove: {
619 GHOST_TWindowState state;
620 state = GHOST_GetWindowState(win->ghostwin);
622 /* win32: gives undefined window size when minimized */
623 if(state!=GHOST_kWindowStateMinimized) {
624 GHOST_RectangleHandle client_rect;
625 int l, t, r, b, scr_w, scr_h;
627 client_rect= GHOST_GetClientBounds(win->ghostwin);
628 GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
630 GHOST_DisposeRectangle(client_rect);
632 wm_get_screensize(&scr_w, &scr_h);
636 win->posy= scr_h - t - win->sizey;
640 state = GHOST_GetWindowState(win->ghostwin);
642 if(state==GHOST_kWindowStateNormal) {
643 if(G.f & G_DEBUG) printf("window state: normal\n");
645 else if(state==GHOST_kWindowStateMinimized) {
646 if(G.f & G_DEBUG) printf("window state: minimized\n");
648 else if(state==GHOST_kWindowStateMaximized) {
649 if(G.f & G_DEBUG) printf("window state: maximized\n");
651 else if(state==GHOST_kWindowStateFullScreen) {
652 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
655 if(type!=GHOST_kEventWindowSize) {
656 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
661 wm_window_make_drawable(C, win);
662 wm_draw_window_clear(win);
663 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
668 wm_event_add_ghostevent(win, type, data);
677 /* This timer system only gives maximum 1 timer event per redraw cycle,
678 to prevent queues to get overloaded.
679 Timer handlers should check for delta to decide if they just
680 update, or follow real time.
681 Timer handlers can also set duration to match frames passed
683 static int wm_window_timer(const bContext *C)
685 wmWindowManager *wm= CTX_wm_manager(C);
687 double time= PIL_check_seconds_timer();
690 for(win= wm->windows.first; win; win= win->next) {
692 for(wt= win->timers.first; wt; wt= wt->next) {
694 if(time > wt->ntime) {
695 wmEvent event= *(win->eventstate);
697 wt->delta= time - wt->ltime;
698 wt->duration += wt->delta;
700 wt->ntime= wt->stime + wt->timestep*ceil(wt->duration/wt->timestep);
702 event.type= wt->event_type;
703 event.custom= EVT_DATA_TIMER;
704 event.customdata= wt;
705 wm_event_add(win, &event);
715 void wm_window_process_events(const bContext *C)
717 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
720 GHOST_DispatchEvents(g_system);
722 hasevent |= wm_window_timer(C);
724 /* no event, we sleep 5 milliseconds */
729 void wm_window_process_events_nosleep(const bContext *C)
731 if(GHOST_ProcessEvents(g_system, 0))
732 GHOST_DispatchEvents(g_system);
735 /* exported as handle callback to bke blender.c */
736 void wm_window_testbreak(void)
738 static double ltime= 0;
739 double curtime= PIL_check_seconds_timer();
741 /* only check for breaks every 50 milliseconds
742 * if we get called more often.
744 if ((curtime-ltime)>.05) {
745 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
748 GHOST_DispatchEvents(g_system);
754 /* **************** init ********************** */
756 void wm_ghost_init(bContext *C)
759 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
761 g_system= GHOST_CreateSystem();
762 GHOST_AddEventConsumer(g_system, consumer);
766 void wm_ghost_exit(void)
769 GHOST_DisposeSystem(g_system);
774 /* **************** timer ********************** */
776 /* to (de)activate running timers temporary */
777 void WM_event_window_timer_sleep(wmWindow *win, wmTimer *timer, int dosleep)
781 for(wt= win->timers.first; wt; wt= wt->next)
789 wmTimer *WM_event_add_window_timer(wmWindow *win, int event_type, double timestep)
791 wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
793 wt->event_type= event_type;
794 wt->ltime= PIL_check_seconds_timer();
795 wt->ntime= wt->ltime + timestep;
796 wt->stime= wt->ltime;
797 wt->timestep= timestep;
799 BLI_addtail(&win->timers, wt);
804 void WM_event_remove_window_timer(wmWindow *win, wmTimer *timer)
808 /* extra security check */
809 for(wt= win->timers.first; wt; wt= wt->next)
814 BLI_remlink(&win->timers, wt);
816 MEM_freeN(wt->customdata);
821 /* ******************* clipboard **************** */
823 char *WM_clipboard_text_get(int selection)
825 char *p, *p2, *buf, *newbuf;
827 buf= (char*)GHOST_getClipboard(selection);
831 /* always convert from \r\n to \n */
832 newbuf= MEM_callocN(strlen(buf)+1, "WM_clipboard_text_get");
834 for(p= buf, p2= newbuf; *p; p++) {
840 free(buf); /* ghost uses regular malloc */
845 void WM_clipboard_text_set(char *buf, int selection)
848 /* do conversion from \n to \r\n on Windows */
849 char *p, *p2, *newbuf;
852 for(p= buf; *p; p++) {
859 newbuf= MEM_callocN(newlen+1, "WM_clipboard_text_set");
861 for(p= buf, p2= newbuf; *p; p++, p2++) {
863 *(p2++)= '\r'; *p2= '\n';
869 GHOST_putClipboard((GHOST_TInt8*)newbuf, selection);
872 GHOST_putClipboard((GHOST_TInt8*)buf, selection);
876 /* ************************************ */
878 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r)
884 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r)
886 *width_r= win->sizex;
887 *height_r= win->sizey;
890 void wm_window_set_size(wmWindow *win, int width, int height)
892 GHOST_SetClientSize(win->ghostwin, width, height);
895 void wm_window_lower(wmWindow *win)
897 GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
900 void wm_window_raise(wmWindow *win)
902 GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
905 void wm_window_swap_buffers(wmWindow *win)
909 glDisable(GL_SCISSOR_TEST);
910 GHOST_SwapWindowBuffers(win->ghostwin);
911 glEnable(GL_SCISSOR_TEST);
913 GHOST_SwapWindowBuffers(win->ghostwin);
917 /* ******************* exported api ***************** */
920 /* called whem no ghost system was initialized */
921 void WM_setprefsize(int stax, int stay, int sizx, int sizy)