Blender  V2.59
wm_window.c
Go to the documentation of this file.
00001 /*
00002  * $Id: wm_window.c 38908 2011-08-02 04:28:05Z merwin $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version. 
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2007 Blender Foundation but based 
00021  * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV
00022  * All rights reserved.
00023  *
00024  * Contributor(s): Blender Foundation, 2008
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  */
00028 
00034 #include <math.h>
00035 #include <stdlib.h>
00036 #include <stdio.h>
00037 #include <string.h>
00038 
00039 #include "DNA_listBase.h"       
00040 #include "DNA_screen_types.h"
00041 #include "DNA_windowmanager_types.h"
00042 #include "RNA_access.h"
00043 
00044 #include "MEM_guardedalloc.h"
00045 
00046 #include "GHOST_C-api.h"
00047 
00048 #include "BLI_blenlib.h"
00049 #include "BLI_utildefines.h"
00050 
00051 #include "BKE_blender.h"
00052 #include "BKE_context.h"
00053 #include "BKE_library.h"
00054 #include "BKE_global.h"
00055 #include "BKE_main.h"
00056 
00057 
00058 #include "BIF_gl.h"
00059 
00060 #include "WM_api.h"
00061 #include "WM_types.h"
00062 #include "wm.h"
00063 #include "wm_draw.h"
00064 #include "wm_window.h"
00065 #include "wm_subwindow.h"
00066 #include "wm_event_system.h"
00067 
00068 #include "ED_screen.h"
00069 #include "ED_fileselect.h"
00070 
00071 #include "PIL_time.h"
00072 
00073 #include "GPU_draw.h"
00074 #include "GPU_extensions.h"
00075 
00076 /* the global to talk to ghost */
00077 static GHOST_SystemHandle g_system= NULL;
00078 
00079 /* set by commandline */
00080 static int prefsizx= 0, prefsizy= 0, prefstax= 0, prefstay= 0, initialstate= GHOST_kWindowStateNormal;
00081 static unsigned short useprefsize= 0;
00082 
00083 /* ******** win open & close ************ */
00084 
00085 /* XXX this one should correctly check for apple top header...
00086  done for Cocoa : returns window contents (and not frame) max size*/
00087 void wm_get_screensize(int *width_r, int *height_r)
00088 {
00089         unsigned int uiwidth;
00090         unsigned int uiheight;
00091         
00092         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
00093         *width_r= uiwidth;
00094         *height_r= uiheight;
00095 }
00096 
00097 /* keeps offset and size within monitor bounds */
00098 /* XXX solve dual screen... */
00099 static void wm_window_check_position(rcti *rect)
00100 {
00101         int width, height, d;
00102         
00103         wm_get_screensize(&width, &height);
00104         
00105 #if defined(__APPLE__) && !defined(GHOST_COCOA)
00106         height -= 70;
00107 #endif
00108         
00109         if(rect->xmin < 0) {
00110                 d= rect->xmin;
00111                 rect->xmax -= d;
00112                 rect->xmin -= d;
00113         }
00114         if(rect->ymin < 0) {
00115                 d= rect->ymin;
00116                 rect->ymax -= d;
00117                 rect->ymin -= d;
00118         }
00119         if(rect->xmax > width) {
00120                 d= rect->xmax - width;
00121                 rect->xmax -= d;
00122                 rect->xmin -= d;
00123         }
00124         if(rect->ymax > height) {
00125                 d= rect->ymax - height;
00126                 rect->ymax -= d;
00127                 rect->ymin -= d;
00128         }
00129         
00130         if(rect->xmin < 0) rect->xmin= 0;
00131         if(rect->ymin < 0) rect->ymin= 0;
00132 }
00133 
00134 
00135 static void wm_ghostwindow_destroy(wmWindow *win) 
00136 {
00137         if(win->ghostwin) {
00138                 GHOST_DisposeWindow(g_system, win->ghostwin);
00139                 win->ghostwin= NULL;
00140         }
00141 }
00142 
00143 /* including window itself, C can be NULL. 
00144    ED_screen_exit should have been called */
00145 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
00146 {
00147         wmTimer *wt, *wtnext;
00148         
00149         /* update context */
00150         if(C) {
00151                 WM_event_remove_handlers(C, &win->handlers);
00152                 WM_event_remove_handlers(C, &win->modalhandlers);
00153 
00154                 if(CTX_wm_window(C)==win)
00155                         CTX_wm_window_set(C, NULL);
00156         }       
00157 
00158         /* always set drawable and active to NULL, prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
00159         wm->windrawable= NULL;
00160         wm->winactive= NULL;
00161 
00162         /* end running jobs, a job end also removes its timer */
00163         for(wt= wm->timers.first; wt; wt= wtnext) {
00164                 wtnext= wt->next;
00165                 if(wt->win==win && wt->event_type==TIMERJOBS)
00166                         wm_jobs_timer_ended(wm, wt);
00167         }
00168         
00169         /* timer removing, need to call this api function */
00170         for(wt= wm->timers.first; wt; wt=wtnext) {
00171                 wtnext= wt->next;
00172                 if(wt->win==win)
00173                         WM_event_remove_timer(wm, win, wt);
00174         }
00175 
00176         if(win->eventstate) MEM_freeN(win->eventstate);
00177         
00178         wm_event_free_all(win);
00179         wm_subwindows_free(win);
00180         
00181         if(win->drawdata)
00182                 MEM_freeN(win->drawdata);
00183         
00184         wm_ghostwindow_destroy(win);
00185         
00186         MEM_freeN(win);
00187 }
00188 
00189 static int find_free_winid(wmWindowManager *wm)
00190 {
00191         wmWindow *win;
00192         int id= 1;
00193         
00194         for(win= wm->windows.first; win; win= win->next)
00195                 if(id <= win->winid)
00196                         id= win->winid+1;
00197         
00198         return id;
00199 }
00200 
00201 /* dont change context itself */
00202 wmWindow *wm_window_new(bContext *C)
00203 {
00204         wmWindowManager *wm= CTX_wm_manager(C);
00205         wmWindow *win= MEM_callocN(sizeof(wmWindow), "window");
00206         
00207         BLI_addtail(&wm->windows, win);
00208         win->winid= find_free_winid(wm);
00209 
00210         return win;
00211 }
00212 
00213 
00214 /* part of wm_window.c api */
00215 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
00216 {
00217         wmWindow *win= wm_window_new(C);
00218         
00219         win->posx= winorig->posx+10;
00220         win->posy= winorig->posy;
00221         win->sizex= winorig->sizex;
00222         win->sizey= winorig->sizey;
00223         
00224         /* duplicate assigns to window */
00225         win->screen= ED_screen_duplicate(win, winorig->screen);
00226         BLI_strncpy(win->screenname, win->screen->id.name+2, sizeof(win->screenname));
00227         win->screen->winid= win->winid;
00228 
00229         win->screen->do_refresh= 1;
00230         win->screen->do_draw= 1;
00231 
00232         win->drawmethod= -1;
00233         win->drawdata= NULL;
00234         
00235         return win;
00236 }
00237 
00238 /* this is event from ghost, or exit-blender op */
00239 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
00240 {
00241         bScreen *screen= win->screen;
00242         
00243         BLI_remlink(&wm->windows, win);
00244         
00245         wm_draw_window_clear(win);
00246         CTX_wm_window_set(C, win);      /* needed by handlers */
00247         WM_event_remove_handlers(C, &win->handlers);
00248         WM_event_remove_handlers(C, &win->modalhandlers);
00249         ED_screen_exit(C, win, win->screen); 
00250         
00251         wm_window_free(C, wm, win);
00252         
00253         /* if temp screen, delete it after window free (it stops jobs that can access it) */
00254         if(screen->temp) {
00255                 Main *bmain= CTX_data_main(C);
00256                 free_libblock(&bmain->screen, screen);
00257         }
00258         
00259         /* check remaining windows */
00260         if(wm->windows.first) {
00261                 for(win= wm->windows.first; win; win= win->next)
00262                         if(win->screen->temp == 0)
00263                                 break;
00264                 /* in this case we close all */
00265                 if(win==NULL)
00266                         WM_exit(C);
00267         }
00268         else
00269                 WM_exit(C);
00270 }
00271 
00272 void wm_window_title(wmWindowManager *wm, wmWindow *win)
00273 {
00274         /* handle the 'temp' window, only set title when not set before */
00275         if(win->screen && win->screen->temp) {
00276                 char *title= GHOST_GetTitle(win->ghostwin);
00277                 if(title==NULL || title[0]==0)
00278                         GHOST_SetTitle(win->ghostwin, "Blender");
00279         }
00280         else {
00281                 
00282                 /* this is set to 1 if you don't have startup.blend open */
00283                 if(G.save_over && G.main->name[0]) {
00284                         char str[sizeof(G.main->name) + 12];
00285                         BLI_snprintf(str, sizeof(str), "Blender%s [%s]", wm->file_saved ? "":"*", G.main->name);
00286                         GHOST_SetTitle(win->ghostwin, str);
00287                 }
00288                 else
00289                         GHOST_SetTitle(win->ghostwin, "Blender");
00290 
00291                 /* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
00292                  and to give hint of unsaved changes for a user warning mechanism
00293                  in case of OS application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end) */
00294                 GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8)!wm->file_saved);
00295                 
00296 #if defined(__APPLE__) && !defined(GHOST_COCOA)
00297                 if(wm->file_saved)
00298                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
00299                 else
00300                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
00301 #endif
00302         }
00303 }
00304 
00305 /* belongs to below */
00306 static void wm_window_add_ghostwindow(bContext *C, const char *title, wmWindow *win)
00307 {
00308         GHOST_WindowHandle ghostwin;
00309         int scr_w, scr_h, posy;
00310         GHOST_TWindowState initial_state;
00311 
00312         /* when there is no window open uses the initial state */
00313         if(!CTX_wm_window(C))
00314                 initial_state= initialstate;
00315         else
00316                 initial_state= GHOST_kWindowStateNormal;
00317         
00318         wm_get_screensize(&scr_w, &scr_h);
00319         posy= (scr_h - win->posy - win->sizey);
00320         
00321 #if defined(__APPLE__) && !defined(GHOST_COCOA)
00322         {
00323                 extern int macPrefState; /* creator.c */
00324                 initial_state += macPrefState;
00325         }
00326 #endif
00327         /* Disable AA for now, as GL_SELECT (used for border, lasso, ... select)
00328          doesn't work well when AA is initialized, even if not used. */
00329         ghostwin= GHOST_CreateWindow(g_system, title, 
00330                                                                  win->posx, posy, win->sizex, win->sizey, 
00331                                                                  initial_state, 
00332                                                                  GHOST_kDrawingContextTypeOpenGL,
00333                                                                  0 /* no stereo */,
00334                                                                  0 /* no AA */);
00335         
00336         if (ghostwin) {
00337                 /* needed so we can detect the graphics card below */
00338                 GPU_extensions_init();
00339                 
00340                 /* set the state*/
00341                 GHOST_SetWindowState(ghostwin, initial_state);
00342 
00343                 win->ghostwin= ghostwin;
00344                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
00345                 
00346                 if(win->eventstate==NULL)
00347                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
00348                 
00349                 /* until screens get drawn, make it nice grey */
00350                 glClearColor(.55, .55, .55, 0.0);
00351                 /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
00352                 if(!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
00353                         glClear(GL_COLOR_BUFFER_BIT);
00354                 }
00355 
00356                 wm_window_swap_buffers(win);
00357                 
00358                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
00359                 
00360                 /* standard state vars for window */
00361                 glEnable(GL_SCISSOR_TEST);
00362                 
00363                 GPU_state_init();
00364         }
00365 }
00366 
00367 /* for wmWindows without ghostwin, open these and clear */
00368 /* window size is read from window, if 0 it uses prefsize */
00369 /* called in WM_check, also inits stuff after file read */
00370 void wm_window_add_ghostwindows(bContext* C, wmWindowManager *wm)
00371 {
00372         wmKeyMap *keymap;
00373         wmWindow *win;
00374         
00375         /* no commandline prefsize? then we set this.
00376          * Note that these values will be used only
00377          * when there is no startup.blend yet.
00378          */
00379         if (!prefsizx) {
00380                 wm_get_screensize(&prefsizx, &prefsizy);
00381                 
00382 #if defined(__APPLE__) && !defined(GHOST_COCOA)
00383 //Cocoa provides functions to get correct max window size
00384                 {
00385                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
00386                         
00387                         wm_set_apple_prefsize(prefsizx, prefsizy);
00388                 }
00389 #else
00390                 prefstax= 0;
00391                 prefstay= 0;
00392                 
00393 #endif
00394         }
00395         
00396         for(win= wm->windows.first; win; win= win->next) {
00397                 if(win->ghostwin==NULL) {
00398                         if(win->sizex==0 || useprefsize) {
00399                                 win->posx= prefstax;
00400                                 win->posy= prefstay;
00401                                 win->sizex= prefsizx;
00402                                 win->sizey= prefsizy;
00403                                 win->windowstate= initialstate;
00404                                 useprefsize= 0;
00405                         }
00406                         wm_window_add_ghostwindow(C, "Blender", win);
00407                 }
00408                 /* happens after fileread */
00409                 if(win->eventstate==NULL)
00410                         win->eventstate= MEM_callocN(sizeof(wmEvent), "window event state");
00411 
00412                 /* add keymap handlers (1 handler for all keys in map!) */
00413                 keymap= WM_keymap_find(wm->defaultconf, "Window", 0, 0);
00414                 WM_event_add_keymap_handler(&win->handlers, keymap);
00415                 
00416                 keymap= WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
00417                 WM_event_add_keymap_handler(&win->handlers, keymap);
00418 
00419                 keymap= WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
00420                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
00421                 
00422                 /* add drop boxes */
00423                 {
00424                         ListBase *lb= WM_dropboxmap_find("Window", 0, 0);
00425                         WM_event_add_dropbox_handler(&win->handlers, lb);
00426                 }
00427                 wm_window_title(wm, win);
00428         }
00429 }
00430 
00431 /* new window, no screen yet, but we open ghostwindow for it */
00432 /* also gets the window level handlers */
00433 /* area-rip calls this */
00434 wmWindow *WM_window_open(bContext *C, rcti *rect)
00435 {
00436         wmWindow *win= wm_window_new(C);
00437         
00438         win->posx= rect->xmin;
00439         win->posy= rect->ymin;
00440         win->sizex= rect->xmax - rect->xmin;
00441         win->sizey= rect->ymax - rect->ymin;
00442 
00443         win->drawmethod= -1;
00444         win->drawdata= NULL;
00445         
00446         WM_check(C);
00447         
00448         return win;
00449 }
00450 
00451 /* uses screen->temp tag to define what to do, currently it limits
00452    to only one "temp" window for render out, preferences, filewindow, etc */
00453 /* type is #define in WM_api.h */
00454 
00455 void WM_window_open_temp(bContext *C, rcti *position, int type)
00456 {
00457         wmWindow *win;
00458         ScrArea *sa;
00459         
00460         /* changes rect to fit within desktop */
00461         wm_window_check_position(position);
00462         
00463         /* test if we have a temp screen already */
00464         for(win= CTX_wm_manager(C)->windows.first; win; win= win->next)
00465                 if(win->screen->temp)
00466                         break;
00467         
00468         /* add new window? */
00469         if(win==NULL) {
00470                 win= wm_window_new(C);
00471                 
00472                 win->posx= position->xmin;
00473                 win->posy= position->ymin;
00474         }
00475         
00476         win->sizex= position->xmax - position->xmin;
00477         win->sizey= position->ymax - position->ymin;
00478         
00479         if(win->ghostwin) {
00480                 wm_window_set_size(win, win->sizex, win->sizey) ;
00481                 wm_window_raise(win);
00482         }
00483         
00484         /* add new screen? */
00485         if(win->screen==NULL)
00486                 win->screen= ED_screen_add(win, CTX_data_scene(C), "temp");
00487         win->screen->temp = 1; 
00488         
00489         /* make window active, and validate/resize */
00490         CTX_wm_window_set(C, win);
00491         WM_check(C);
00492         
00493         /* ensure it shows the right spacetype editor */
00494         sa= win->screen->areabase.first;
00495         CTX_wm_area_set(C, sa);
00496         
00497         if(type==WM_WINDOW_RENDER) {
00498                 ED_area_newspace(C, sa, SPACE_IMAGE);
00499         }
00500         else {
00501                 ED_area_newspace(C, sa, SPACE_USERPREF);
00502         }
00503         
00504         ED_screen_set(C, win->screen);
00505         
00506         if(sa->spacetype==SPACE_IMAGE)
00507                 GHOST_SetTitle(win->ghostwin, "Blender Render");
00508         else if(ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
00509                 GHOST_SetTitle(win->ghostwin, "Blender User Preferences");
00510         else if(sa->spacetype==SPACE_FILE)
00511                 GHOST_SetTitle(win->ghostwin, "Blender File View");
00512         else
00513                 GHOST_SetTitle(win->ghostwin, "Blender");
00514 }
00515 
00516 
00517 /* ****************** Operators ****************** */
00518 
00519 /* operator callback */
00520 int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
00521 {
00522         wm_window_copy(C, CTX_wm_window(C));
00523         WM_check(C);
00524         
00525         WM_event_add_notifier(C, NC_WINDOW|NA_ADDED, NULL);
00526         
00527         return OPERATOR_FINISHED;
00528 }
00529 
00530 
00531 /* fullscreen operator callback */
00532 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
00533 {
00534         wmWindow *window= CTX_wm_window(C);
00535         GHOST_TWindowState state;
00536 
00537         if(G.background)
00538                 return OPERATOR_CANCELLED;
00539 
00540         state= GHOST_GetWindowState(window->ghostwin);
00541         if(state!=GHOST_kWindowStateFullScreen)
00542                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
00543         else
00544                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
00545 
00546         return OPERATOR_FINISHED;
00547         
00548 }
00549 
00550 
00551 /* ************ events *************** */
00552 
00553 typedef enum
00554 {
00555         SHIFT = 's',
00556         CONTROL = 'c',
00557         ALT = 'a',
00558         OS = 'C'
00559 } modifierKeyType;
00560 
00561 /* check if specified modifier key type is pressed */
00562 static int query_qual(modifierKeyType qual) 
00563 {
00564         GHOST_TModifierKeyMask left, right;
00565         int val= 0;
00566         
00567         switch(qual) {
00568                 case SHIFT:
00569                         left= GHOST_kModifierKeyLeftShift;
00570                         right= GHOST_kModifierKeyRightShift;
00571                         break;
00572                 case CONTROL:
00573                         left= GHOST_kModifierKeyLeftControl;
00574                         right= GHOST_kModifierKeyRightControl;
00575                         break;
00576                 case OS:
00577                         left= right= GHOST_kModifierKeyOS;
00578                         break;
00579                 case ALT:
00580                 default:
00581                         left= GHOST_kModifierKeyLeftAlt;
00582                         right= GHOST_kModifierKeyRightAlt;
00583                         break;
00584         }
00585         
00586         GHOST_GetModifierKeyState(g_system, left, &val);
00587         if (!val)
00588                 GHOST_GetModifierKeyState(g_system, right, &val);
00589         
00590         return val;
00591 }
00592 
00593 void wm_window_make_drawable(bContext *C, wmWindow *win) 
00594 {
00595         wmWindowManager *wm= CTX_wm_manager(C);
00596 
00597         if (win != wm->windrawable && win->ghostwin) {
00598 //              win->lmbut= 0;  /* keeps hanging when mousepressed while other window opened */
00599                 
00600                 wm->windrawable= win;
00601                 if(G.f & G_DEBUG) printf("set drawable %d\n", win->winid);
00602                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
00603         }
00604 }
00605 
00606 /* called by ghost, here we handle events for windows themselves or send to event system */
00607 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private) 
00608 {
00609         bContext *C= private;
00610         wmWindowManager *wm= CTX_wm_manager(C);
00611         GHOST_TEventType type= GHOST_GetEventType(evt);
00612         int time= GHOST_GetEventTime(evt);
00613         
00614         if (type == GHOST_kEventQuit) {
00615                 WM_exit(C);
00616         } else {
00617                 GHOST_WindowHandle ghostwin= GHOST_GetEventWindow(evt);
00618                 GHOST_TEventDataPtr data= GHOST_GetEventData(evt);
00619                 wmWindow *win;
00620                 
00621                 if (!ghostwin) {
00622                         // XXX - should be checked, why are we getting an event here, and
00623                         //      what is it?
00624                         puts("<!> event has no window");
00625                         return 1;
00626                 } else if (!GHOST_ValidWindow(g_system, ghostwin)) {
00627                         // XXX - should be checked, why are we getting an event here, and
00628                         //      what is it?
00629                         puts("<!> event has invalid window");                   
00630                         return 1;
00631                 } else {
00632                         win= GHOST_GetWindowUserData(ghostwin);
00633                 }
00634                 
00635                 switch(type) {
00636                         case GHOST_kEventWindowDeactivate:
00637                                 wm_event_add_ghostevent(wm, win, type, time, data);
00638                                 win->active= 0; /* XXX */
00639                                 break;
00640                         case GHOST_kEventWindowActivate: 
00641                         {
00642                                 GHOST_TEventKeyData kdata;
00643                                 int cx, cy, wx, wy;
00644                                 
00645                                 wm->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
00646                                 
00647                                 win->active= 1;
00648 //                              window_handle(win, INPUTCHANGE, win->active);
00649                                 
00650                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
00651                                 kdata.ascii= 0;
00652                                 if (win->eventstate->shift && !query_qual(SHIFT)) {
00653                                         kdata.key= GHOST_kKeyLeftShift;
00654                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
00655                                 }
00656                                 if (win->eventstate->ctrl && !query_qual(CONTROL)) {
00657                                         kdata.key= GHOST_kKeyLeftControl;
00658                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
00659                                 }
00660                                 if (win->eventstate->alt && !query_qual(ALT)) {
00661                                         kdata.key= GHOST_kKeyLeftAlt;
00662                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
00663                                 }
00664                                 if (win->eventstate->oskey && !query_qual(OS)) {
00665                                         kdata.key= GHOST_kKeyOS;
00666                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
00667                                 }
00668                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
00669                                 win->eventstate->keymodifier= 0;
00670                                 
00671                                 /* entering window, update mouse pos. but no event */
00672                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
00673                                 
00674                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
00675                                 win->eventstate->x= cx;
00676                                 win->eventstate->y= (win->sizey-1) - cy;
00677                                 
00678                                 win->addmousemove= 1;   /* enables highlighted buttons */
00679                                 
00680                                 wm_window_make_drawable(C, win);
00681                                 break;
00682                         }
00683                         case GHOST_kEventWindowClose: {
00684                                 wm_window_close(C, wm, win);
00685                                 break;
00686                         }
00687                         case GHOST_kEventWindowUpdate: {
00688                                 if(G.f & G_DEBUG) printf("ghost redraw\n");
00689                                 
00690                                 wm_window_make_drawable(C, win);
00691                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
00692 
00693                                 break;
00694                         }
00695                         case GHOST_kEventWindowSize:
00696                         case GHOST_kEventWindowMove: {
00697                                 GHOST_TWindowState state;
00698                                 state = GHOST_GetWindowState(win->ghostwin);
00699 
00700                                  /* win32: gives undefined window size when minimized */
00701                                 if(state!=GHOST_kWindowStateMinimized) {
00702                                         GHOST_RectangleHandle client_rect;
00703                                         int l, t, r, b, scr_w, scr_h;
00704                                         int sizex, sizey, posx, posy;
00705                                         
00706                                         client_rect= GHOST_GetClientBounds(win->ghostwin);
00707                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
00708                                         
00709                                         GHOST_DisposeRectangle(client_rect);
00710                                         
00711                                         wm_get_screensize(&scr_w, &scr_h);
00712                                         sizex= r-l;
00713                                         sizey= b-t;
00714                                         posx= l;
00715                                         posy= scr_h - t - win->sizey;
00716 
00717                                         /*
00718                                          * Ghost sometimes send size or move events when the window hasn't changed.
00719                                          * One case of this is using compiz on linux. To alleviate the problem
00720                                          * we ignore all such event here.
00721                                          * 
00722                                          * It might be good to eventually do that at Ghost level, but that is for 
00723                                          * another time.
00724                                          */
00725                                         if (win->sizex != sizex ||
00726                                                         win->sizey != sizey ||
00727                                                         win->posx != posx ||
00728                                                         win->posy != posy)
00729                                         {
00730                                                 win->sizex= sizex;
00731                                                 win->sizey= sizey;
00732                                                 win->posx= posx;
00733                                                 win->posy= posy;
00734 
00735                                                 /* debug prints */
00736                                                 if(0) {
00737                                                         state = GHOST_GetWindowState(win->ghostwin);
00738         
00739                                                         if(state==GHOST_kWindowStateNormal) {
00740                                                                 if(G.f & G_DEBUG) printf("window state: normal\n");
00741                                                         }
00742                                                         else if(state==GHOST_kWindowStateMinimized) {
00743                                                                 if(G.f & G_DEBUG) printf("window state: minimized\n");
00744                                                         }
00745                                                         else if(state==GHOST_kWindowStateMaximized) {
00746                                                                 if(G.f & G_DEBUG) printf("window state: maximized\n");
00747                                                         }
00748                                                         else if(state==GHOST_kWindowStateFullScreen) {
00749                                                                 if(G.f & G_DEBUG) printf("window state: fullscreen\n");
00750                                                         }
00751                                                         
00752                                                         if(type!=GHOST_kEventWindowSize) {
00753                                                                 if(G.f & G_DEBUG) printf("win move event pos %d %d size %d %d\n", win->posx, win->posy, win->sizex, win->sizey);
00754                                                         }
00755                                                         
00756                                                 }
00757                                         
00758                                                 wm_window_make_drawable(C, win);
00759                                                 wm_draw_window_clear(win);
00760                                                 WM_event_add_notifier(C, NC_SCREEN|NA_EDITED, NULL);
00761                                                 WM_event_add_notifier(C, NC_WINDOW|NA_EDITED, NULL);
00762                                         }
00763                                 }
00764                                 break;
00765                         }
00766                                 
00767                         case GHOST_kEventOpenMainFile:
00768                         {
00769                                 PointerRNA props_ptr;
00770                                 wmWindow *oldWindow;
00771                                 char *path = GHOST_GetEventData(evt);
00772                                 
00773                                 if (path) {
00774                                         /* operator needs a valid window in context, ensures
00775                                          it is correctly set */
00776                                         oldWindow = CTX_wm_window(C);
00777                                         CTX_wm_window_set(C, win);
00778                                         
00779                                         WM_operator_properties_create(&props_ptr, "WM_OT_open_mainfile");
00780                                         RNA_string_set(&props_ptr, "filepath", path);
00781                                         WM_operator_name_call(C, "WM_OT_open_mainfile", WM_OP_EXEC_DEFAULT, &props_ptr);
00782                                         WM_operator_properties_free(&props_ptr);
00783                                         
00784                                         CTX_wm_window_set(C, oldWindow);
00785                                 }
00786                                 break;
00787                         }
00788                         case GHOST_kEventDraggingDropDone:
00789                         {
00790                                 wmEvent event;
00791                                 GHOST_TEventDragnDropData *ddd= GHOST_GetEventData(evt);
00792                                 int cx, cy, wx, wy;
00793                                 
00794                                 /* entering window, update mouse pos */
00795                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
00796                                 
00797                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
00798                                 win->eventstate->x= cx;
00799                                 win->eventstate->y= (win->sizey-1) - cy;
00800                                 
00801                                 event= *(win->eventstate);      /* copy last state, like mouse coords */
00802                                 
00803                                 // activate region
00804                                 event.type= MOUSEMOVE;
00805                                 event.prevx= event.x;
00806                                 event.prevy= event.y;
00807                                 
00808                                 wm->winactive= win; /* no context change! c->wm->windrawable is drawable, or for area queues */
00809                                 win->active= 1;
00810                                 
00811                                 wm_event_add(win, &event);
00812                                 
00813                                 
00814                                 /* make blender drop event with custom data pointing to wm drags */
00815                                 event.type= EVT_DROP;
00816                                 event.val= KM_RELEASE;
00817                                 event.custom= EVT_DATA_LISTBASE;
00818                                 event.customdata= &wm->drags;
00819                                 event.customdatafree= 1;
00820                                 
00821                                 wm_event_add(win, &event);
00822                                 
00823                                 /* printf("Drop detected\n"); */
00824                                 
00825                                 /* add drag data to wm for paths: */
00826                                 
00827                                 if(ddd->dataType == GHOST_kDragnDropTypeFilenames) {
00828                                         GHOST_TStringArray *stra= ddd->data;
00829                                         int a, icon;
00830                                         
00831                                         for(a=0; a<stra->count; a++) {
00832                                                 printf("drop file %s\n", stra->strings[a]);
00833                                                 /* try to get icon type from extension */
00834                                                 icon= ED_file_extension_icon((char *)stra->strings[a]);
00835                                                 
00836                                                 WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0);
00837                                                 /* void poin should point to string, it makes a copy */
00838                                                 break; // only one drop element supported now 
00839                                         }
00840                                 }
00841                                 
00842                                 
00843                                 
00844                                 break;
00845                         }
00846                         
00847                         default:
00848                                 wm_event_add_ghostevent(wm, win, type, time, data);
00849                                 break;
00850                 }
00851 
00852         }
00853         return 1;
00854 }
00855 
00856 
00857 /* This timer system only gives maximum 1 timer event per redraw cycle,
00858    to prevent queues to get overloaded. 
00859    Timer handlers should check for delta to decide if they just
00860    update, or follow real time.
00861    Timer handlers can also set duration to match frames passed
00862 */
00863 static int wm_window_timer(const bContext *C)
00864 {
00865         wmWindowManager *wm= CTX_wm_manager(C);
00866         wmTimer *wt, *wtnext;
00867         wmWindow *win;
00868         double time= PIL_check_seconds_timer();
00869         int retval= 0;
00870         
00871         for(wt= wm->timers.first; wt; wt= wtnext) {
00872                 wtnext= wt->next; /* in case timer gets removed */
00873                 win= wt->win;
00874 
00875                 if(wt->sleep==0) {
00876                         if(time > wt->ntime) {
00877                                 wt->delta= time - wt->ltime;
00878                                 wt->duration += wt->delta;
00879                                 wt->ltime= time;
00880                                 wt->ntime= wt->stime + wt->timestep*ceil(wt->duration/wt->timestep);
00881 
00882                                 if(wt->event_type == TIMERJOBS)
00883                                         wm_jobs_timer(C, wm, wt);
00884                                 else if(wt->event_type == TIMERAUTOSAVE)
00885                                         wm_autosave_timer(C, wm, wt);
00886                                 else if(win) {
00887                                         wmEvent event= *(win->eventstate);
00888                                         
00889                                         event.type= wt->event_type;
00890                                         event.custom= EVT_DATA_TIMER;
00891                                         event.customdata= wt;
00892                                         wm_event_add(win, &event);
00893 
00894                                         retval= 1;
00895                                 }
00896                         }
00897                 }
00898         }
00899         return retval;
00900 }
00901 
00902 void wm_window_process_events(const bContext *C) 
00903 {
00904         int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
00905         
00906         if(hasevent)
00907                 GHOST_DispatchEvents(g_system);
00908         
00909         hasevent |= wm_window_timer(C);
00910 
00911         /* no event, we sleep 5 milliseconds */
00912         if(hasevent==0)
00913                 PIL_sleep_ms(5);
00914 }
00915 
00916 void wm_window_process_events_nosleep(void) 
00917 {
00918         if(GHOST_ProcessEvents(g_system, 0))
00919                 GHOST_DispatchEvents(g_system);
00920 }
00921 
00922 /* exported as handle callback to bke blender.c */
00923 void wm_window_testbreak(void)
00924 {
00925         static double ltime= 0;
00926         double curtime= PIL_check_seconds_timer();
00927         
00928         /* only check for breaks every 50 milliseconds
00929                 * if we get called more often.
00930                 */
00931         if ((curtime-ltime)>.05) {
00932                 int hasevent= GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
00933                 
00934                 if(hasevent)
00935                         GHOST_DispatchEvents(g_system);
00936                 
00937                 ltime= curtime;
00938         }
00939 }
00940 
00941 /* **************** init ********************** */
00942 
00943 void wm_ghost_init(bContext *C)
00944 {
00945         if (!g_system) {
00946                 GHOST_EventConsumerHandle consumer= GHOST_CreateEventConsumer(ghost_event_proc, C);
00947                 
00948                 g_system= GHOST_CreateSystem();
00949                 GHOST_AddEventConsumer(g_system, consumer);
00950         }       
00951 }
00952 
00953 void wm_ghost_exit(void)
00954 {
00955         if(g_system)
00956                 GHOST_DisposeSystem(g_system);
00957 
00958         g_system= NULL;
00959 }
00960 
00961 /* **************** timer ********************** */
00962 
00963 /* to (de)activate running timers temporary */
00964 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer, int dosleep)
00965 {
00966         wmTimer *wt;
00967         
00968         for(wt= wm->timers.first; wt; wt= wt->next)
00969                 if(wt==timer)
00970                         break;
00971 
00972         if(wt)
00973                 wt->sleep= dosleep;
00974 }
00975 
00976 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
00977 {
00978         wmTimer *wt= MEM_callocN(sizeof(wmTimer), "window timer");
00979         
00980         wt->event_type= event_type;
00981         wt->ltime= PIL_check_seconds_timer();
00982         wt->ntime= wt->ltime + timestep;
00983         wt->stime= wt->ltime;
00984         wt->timestep= timestep;
00985         wt->win= win;
00986         
00987         BLI_addtail(&wm->timers, wt);
00988         
00989         return wt;
00990 }
00991 
00992 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
00993 {
00994         wmTimer *wt;
00995         
00996         /* extra security check */
00997         for(wt= wm->timers.first; wt; wt= wt->next)
00998                 if(wt==timer)
00999                         break;
01000         if(wt) {
01001                 if(wm->reports.reporttimer == wt)
01002                         wm->reports.reporttimer= NULL;
01003                 
01004                 BLI_remlink(&wm->timers, wt);
01005                 if(wt->customdata)
01006                         MEM_freeN(wt->customdata);
01007                 MEM_freeN(wt);
01008         }
01009 }
01010 
01011 /* ******************* clipboard **************** */
01012 
01013 char *WM_clipboard_text_get(int selection)
01014 {
01015         char *p, *p2, *buf, *newbuf;
01016 
01017         if(G.background)
01018                 return NULL;
01019 
01020         buf= (char*)GHOST_getClipboard(selection);
01021         if(!buf)
01022                 return NULL;
01023         
01024         /* always convert from \r\n to \n */
01025         newbuf= MEM_callocN(strlen(buf)+1, "WM_clipboard_text_get");
01026 
01027         for(p= buf, p2= newbuf; *p; p++) {
01028                 if(*p != '\r')
01029                         *(p2++)= *p;
01030         }
01031         *p2= '\0';
01032 
01033         free(buf); /* ghost uses regular malloc */
01034         
01035         return newbuf;
01036 }
01037 
01038 void WM_clipboard_text_set(char *buf, int selection)
01039 {
01040         if(!G.background) {
01041 #ifdef _WIN32
01042                 /* do conversion from \n to \r\n on Windows */
01043                 char *p, *p2, *newbuf;
01044                 int newlen= 0;
01045                 
01046                 for(p= buf; *p; p++) {
01047                         if(*p == '\n')
01048                                 newlen += 2;
01049                         else
01050                                 newlen++;
01051                 }
01052                 
01053                 newbuf= MEM_callocN(newlen+1, "WM_clipboard_text_set");
01054         
01055                 for(p= buf, p2= newbuf; *p; p++, p2++) {
01056                         if(*p == '\n') { 
01057                                 *(p2++)= '\r'; *p2= '\n';
01058                         }
01059                         else *p2= *p;
01060                 }
01061                 *p2= '\0';
01062         
01063                 GHOST_putClipboard((GHOST_TInt8*)newbuf, selection);
01064                 MEM_freeN(newbuf);
01065 #else
01066                 GHOST_putClipboard((GHOST_TInt8*)buf, selection);
01067 #endif
01068         }
01069 }
01070 
01071 /* ******************* progress bar **************** */
01072 
01073 void WM_progress_set(wmWindow *win, float progress)
01074 {
01075         GHOST_SetProgressBar(win->ghostwin, progress);
01076 }
01077 
01078 void WM_progress_clear(wmWindow *win)
01079 {
01080         GHOST_EndProgressBar(win->ghostwin);
01081 }
01082 
01083 /* ************************************ */
01084 
01085 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
01086 {
01087         *posx_r= win->posx;
01088         *posy_r= win->posy;
01089 }
01090 
01091 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r) 
01092 {
01093         *width_r= win->sizex;
01094         *height_r= win->sizey;
01095 }
01096 
01097 /* exceptional case: - splash is called before events are processed
01098  * this means we dont actually know the window size so get this from GHOST */
01099 void wm_window_get_size_ghost(wmWindow *win, int *width_r, int *height_r)
01100 {
01101         GHOST_RectangleHandle bounds= GHOST_GetClientBounds(win->ghostwin);
01102         *width_r= GHOST_GetWidthRectangle(bounds);
01103         *height_r= GHOST_GetHeightRectangle(bounds);
01104         
01105         GHOST_DisposeRectangle(bounds);
01106 }
01107 
01108 void wm_window_set_size(wmWindow *win, int width, int height) 
01109 {
01110         GHOST_SetClientSize(win->ghostwin, width, height);
01111 }
01112 
01113 void wm_window_lower(wmWindow *win) 
01114 {
01115         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
01116 }
01117 
01118 void wm_window_raise(wmWindow *win) 
01119 {
01120         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
01121 }
01122 
01123 void wm_window_swap_buffers(wmWindow *win)
01124 {
01125         
01126 #ifdef WIN32
01127         glDisable(GL_SCISSOR_TEST);
01128         GHOST_SwapWindowBuffers(win->ghostwin);
01129         glEnable(GL_SCISSOR_TEST);
01130 #else
01131         GHOST_SwapWindowBuffers(win->ghostwin);
01132 #endif
01133 }
01134 
01135 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
01136 {
01137         GHOST_GetCursorPosition(g_system, x, y);
01138         GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
01139         *y = (win->sizey-1) - *y;
01140 }
01141 
01142 /* ******************* exported api ***************** */
01143 
01144 
01145 /* called whem no ghost system was initialized */
01146 void WM_setprefsize(int stax, int stay, int sizx, int sizy)
01147 {
01148         prefstax= stax;
01149         prefstay= stay;
01150         prefsizx= sizx;
01151         prefsizy= sizy;
01152         useprefsize= 1;
01153 }
01154 
01155 /* for borderless and border windows set from command-line */
01156 void WM_setinitialstate_fullscreen(void)
01157 {
01158         initialstate= GHOST_kWindowStateFullScreen;
01159 }
01160 
01161 void WM_setinitialstate_normal(void)
01162 {
01163         initialstate= GHOST_kWindowStateNormal;
01164 }
01165 
01166 /* This function requires access to the GHOST_SystemHandle (g_system) */
01167 void WM_cursor_warp(wmWindow *win, int x, int y)
01168 {
01169         if (win && win->ghostwin) {
01170                 int oldx=x, oldy=y;
01171 
01172                 y= win->sizey -y - 1;
01173 
01174                 GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
01175                 GHOST_SetCursorPosition(g_system, x, y);
01176 
01177                 win->eventstate->prevx= oldx;
01178                 win->eventstate->prevy= oldy;
01179         }
01180 }
01181