svn merge ^/trunk/blender -r50173:50179
[blender.git] / source / blender / windowmanager / intern / wm_window.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2007 Blender Foundation but based 
19  * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV
20  * All rights reserved.
21  *
22  * Contributor(s): Blender Foundation, 2008
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_window.c
28  *  \ingroup wm
29  */
30
31
32 #include <math.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36
37 #include "DNA_listBase.h"       
38 #include "DNA_screen_types.h"
39 #include "DNA_windowmanager_types.h"
40 #include "RNA_access.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "GHOST_C-api.h"
45
46 #include "BLI_blenlib.h"
47 #include "BLI_utildefines.h"
48
49 #include "BLF_translation.h"
50
51 #include "BKE_blender.h"
52 #include "BKE_colortools.h"
53 #include "BKE_context.h"
54 #include "BKE_library.h"
55 #include "BKE_global.h"
56 #include "BKE_main.h"
57
58
59 #include "BIF_gl.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63 #include "wm.h"
64 #include "wm_draw.h"
65 #include "wm_window.h"
66 #include "wm_subwindow.h"
67 #include "wm_event_system.h"
68
69 #include "ED_screen.h"
70 #include "ED_fileselect.h"
71
72 #include "PIL_time.h"
73
74 #include "GPU_draw.h"
75 #include "GPU_extensions.h"
76
77 #include "UI_interface.h"
78
79 /* the global to talk to ghost */
80 static GHOST_SystemHandle g_system = NULL;
81
82 typedef enum WinOverrideFlag {
83         WIN_OVERRIDE_GEOM     = (1 << 0),
84         WIN_OVERRIDE_WINSTATE = (1 << 1)
85 } WinOverrideFlag;
86
87 /* set by commandline */
88 static struct WMInitStruct {
89         /* window geometry */
90         int size_x, size_y;
91         int start_x, start_y;
92
93         int windowstate;
94         WinOverrideFlag override_flag;
95 } wm_init_state = {0, 0, 0, 0, GHOST_kWindowStateNormal, 0};
96
97 /* ******** win open & close ************ */
98
99 /* XXX this one should correctly check for apple top header...
100  * done for Cocoa : returns window contents (and not frame) max size*/
101 void wm_get_screensize(int *width_r, int *height_r)
102 {
103         unsigned int uiwidth;
104         unsigned int uiheight;
105         
106         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
107         *width_r = uiwidth;
108         *height_r = uiheight;
109 }
110
111 /* keeps offset and size within monitor bounds */
112 /* XXX solve dual screen... */
113 static void wm_window_check_position(rcti *rect)
114 {
115         int width, height, d;
116         
117         wm_get_screensize(&width, &height);
118         
119 #if defined(__APPLE__) && !defined(GHOST_COCOA)
120         height -= 70;
121 #endif
122         
123         if (rect->xmin < 0) {
124                 rect->xmax -= rect->xmin;
125                 rect->xmin  = 0;
126         }
127         if (rect->ymin < 0) {
128                 rect->ymax -= rect->ymin;
129                 rect->ymin  = 0;
130         }
131         if (rect->xmax > width) {
132                 d = rect->xmax - width;
133                 rect->xmax -= d;
134                 rect->xmin -= d;
135         }
136         if (rect->ymax > height) {
137                 d = rect->ymax - height;
138                 rect->ymax -= d;
139                 rect->ymin -= d;
140         }
141         
142         if (rect->xmin < 0) rect->xmin = 0;
143         if (rect->ymin < 0) rect->ymin = 0;
144 }
145
146
147 static void wm_ghostwindow_destroy(wmWindow *win) 
148 {
149         if (win->ghostwin) {
150                 GHOST_DisposeWindow(g_system, win->ghostwin);
151                 win->ghostwin = NULL;
152         }
153 }
154
155 /* including window itself, C can be NULL. 
156  * ED_screen_exit should have been called */
157 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
158 {
159         wmTimer *wt, *wtnext;
160         
161         /* update context */
162         if (C) {
163                 WM_event_remove_handlers(C, &win->handlers);
164                 WM_event_remove_handlers(C, &win->modalhandlers);
165
166                 if (CTX_wm_window(C) == win)
167                         CTX_wm_window_set(C, NULL);
168         }       
169
170         /* always set drawable and active to NULL,
171          * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
172         wm->windrawable = NULL;
173         wm->winactive = NULL;
174
175         /* end running jobs, a job end also removes its timer */
176         for (wt = wm->timers.first; wt; wt = wtnext) {
177                 wtnext = wt->next;
178                 if (wt->win == win && wt->event_type == TIMERJOBS)
179                         wm_jobs_timer_ended(wm, wt);
180         }
181         
182         /* timer removing, need to call this api function */
183         for (wt = wm->timers.first; wt; wt = wtnext) {
184                 wtnext = wt->next;
185                 if (wt->win == win)
186                         WM_event_remove_timer(wm, win, wt);
187         }
188
189         if (win->eventstate) MEM_freeN(win->eventstate);
190         
191         wm_event_free_all(win);
192         wm_subwindows_free(win);
193         
194         if (win->drawdata)
195                 MEM_freeN(win->drawdata);
196         
197         wm_ghostwindow_destroy(win);
198         
199         MEM_freeN(win);
200 }
201
202 static int find_free_winid(wmWindowManager *wm)
203 {
204         wmWindow *win;
205         int id = 1;
206         
207         for (win = wm->windows.first; win; win = win->next)
208                 if (id <= win->winid)
209                         id = win->winid + 1;
210         
211         return id;
212 }
213
214 /* don't change context itself */
215 wmWindow *wm_window_new(bContext *C)
216 {
217         wmWindowManager *wm = CTX_wm_manager(C);
218         wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
219         
220         BLI_addtail(&wm->windows, win);
221         win->winid = find_free_winid(wm);
222
223         return win;
224 }
225
226
227 /* part of wm_window.c api */
228 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
229 {
230         wmWindow *win = wm_window_new(C);
231         
232         win->posx = winorig->posx + 10;
233         win->posy = winorig->posy;
234         win->sizex = winorig->sizex;
235         win->sizey = winorig->sizey;
236         
237         /* duplicate assigns to window */
238         win->screen = ED_screen_duplicate(win, winorig->screen);
239         BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
240         win->screen->winid = win->winid;
241
242         win->screen->do_refresh = TRUE;
243         win->screen->do_draw = TRUE;
244
245         win->drawmethod = -1;
246         win->drawdata = NULL;
247
248         return win;
249 }
250
251 /* this is event from ghost, or exit-blender op */
252 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
253 {
254         wmWindow *tmpwin;
255         bScreen *screen = win->screen;
256         
257         /* first check if we have any non-temp remaining windows */
258         if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved) {
259                 if (wm->windows.first) {
260                         for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
261                                 if (tmpwin == win)
262                                         continue;
263                                 if (tmpwin->screen->temp == 0)
264                                         break;
265                         }
266                         if (tmpwin == NULL) {
267                                 if (!GHOST_confirmQuit(win->ghostwin))
268                                         return;
269                         }
270                 }
271         }
272
273         BLI_remlink(&wm->windows, win);
274         
275         wm_draw_window_clear(win);
276         CTX_wm_window_set(C, win);  /* needed by handlers */
277         WM_event_remove_handlers(C, &win->handlers);
278         WM_event_remove_handlers(C, &win->modalhandlers);
279         ED_screen_exit(C, win, win->screen); 
280         
281         wm_window_free(C, wm, win);
282         
283         /* if temp screen, delete it after window free (it stops jobs that can access it) */
284         if (screen->temp) {
285                 Main *bmain = CTX_data_main(C);
286                 BKE_libblock_free(&bmain->screen, screen);
287         }
288         
289         /* check remaining windows */
290         if (wm->windows.first) {
291                 for (win = wm->windows.first; win; win = win->next)
292                         if (win->screen->temp == 0)
293                                 break;
294                 /* in this case we close all */
295                 if (win == NULL)
296                         WM_exit(C);
297         }
298         else
299                 WM_exit(C);
300 }
301
302 void wm_window_title(wmWindowManager *wm, wmWindow *win)
303 {
304         /* handle the 'temp' window, only set title when not set before */
305         if (win->screen && win->screen->temp) {
306                 char *title = GHOST_GetTitle(win->ghostwin);
307                 if (title == NULL || title[0] == 0)
308                         GHOST_SetTitle(win->ghostwin, "Blender");
309         }
310         else {
311                 
312                 /* this is set to 1 if you don't have startup.blend open */
313                 if (G.save_over && G.main->name[0]) {
314                         char str[sizeof(G.main->name) + 12];
315                         BLI_snprintf(str, sizeof(str), "Blender%s [%s]", wm->file_saved ? "" : "*", G.main->name);
316                         GHOST_SetTitle(win->ghostwin, str);
317                 }
318                 else
319                         GHOST_SetTitle(win->ghostwin, "Blender");
320
321                 /* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
322                  * and to give hint of unsaved changes for a user warning mechanism
323                  * in case of OS application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end) */
324                 GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8) !wm->file_saved);
325                 
326 #if defined(__APPLE__) && !defined(GHOST_COCOA)
327                 if (wm->file_saved)
328                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateUnModified);
329                 else
330                         GHOST_SetWindowState(win->ghostwin, GHOST_kWindowStateModified);
331 #endif
332         }
333 }
334
335 /* belongs to below */
336 static void wm_window_add_ghostwindow(const char *title, wmWindow *win)
337 {
338         GHOST_WindowHandle ghostwin;
339         int scr_w, scr_h, posy;
340         
341         wm_get_screensize(&scr_w, &scr_h);
342         posy = (scr_h - win->posy - win->sizey);
343         
344         /* Disable AA for now, as GL_SELECT (used for border, lasso, ... select)
345          * doesn't work well when AA is initialized, even if not used. */
346         ghostwin = GHOST_CreateWindow(g_system, title,
347                                       win->posx, posy, win->sizex, win->sizey,
348                                       (GHOST_TWindowState)win->windowstate,
349                                       GHOST_kDrawingContextTypeOpenGL,
350                                       0 /* no stereo */,
351                                       0 /* no AA */);
352         
353         if (ghostwin) {
354                 /* needed so we can detect the graphics card below */
355                 GPU_extensions_init();
356                 
357                 /* set the state*/
358                 GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate);
359
360                 win->ghostwin = ghostwin;
361                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
362                 
363                 if (win->eventstate == NULL)
364                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
365                 
366                 /* until screens get drawn, make it nice gray */
367                 glClearColor(0.55, 0.55, 0.55, 0.0);
368                 /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
369                 if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
370                         glClear(GL_COLOR_BUFFER_BIT);
371                 }
372
373                 wm_window_swap_buffers(win);
374                 
375                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
376                 
377                 /* standard state vars for window */
378                 glEnable(GL_SCISSOR_TEST);
379                 
380                 GPU_state_init();
381         }
382 }
383
384 /* for wmWindows without ghostwin, open these and clear */
385 /* window size is read from window, if 0 it uses prefsize */
386 /* called in WM_check, also inits stuff after file read */
387 void wm_window_add_ghostwindows(wmWindowManager *wm)
388 {
389         wmKeyMap *keymap;
390         wmWindow *win;
391         
392         /* no commandline prefsize? then we set this.
393          * Note that these values will be used only
394          * when there is no startup.blend yet.
395          */
396         if (wm_init_state.size_x == 0) {
397                 wm_get_screensize(&wm_init_state.size_x, &wm_init_state.size_y);
398                 
399 #if defined(__APPLE__) && !defined(GHOST_COCOA)
400                 /* Cocoa provides functions to get correct max window size */
401                 {
402                         extern void wm_set_apple_prefsize(int, int);    /* wm_apple.c */
403                         
404                         wm_set_apple_prefsize(wm_init_state.size_x, wm_init_state.size_y);
405                 }
406 #else
407                 wm_init_state.start_x = 0;
408                 wm_init_state.start_y = 0;
409                 
410 #endif
411         }
412         
413         for (win = wm->windows.first; win; win = win->next) {
414                 if (win->ghostwin == NULL) {
415                         if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
416                                 win->posx = wm_init_state.start_x;
417                                 win->posy = wm_init_state.start_y;
418                                 win->sizex = wm_init_state.size_x;
419                                 win->sizey = wm_init_state.size_y;
420                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
421                         }
422
423                         if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
424                                 win->windowstate = wm_init_state.windowstate;
425                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
426                         }
427
428                         wm_window_add_ghostwindow("Blender", win);
429                 }
430                 /* happens after fileread */
431                 if (win->eventstate == NULL)
432                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
433
434                 /* add keymap handlers (1 handler for all keys in map!) */
435                 keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0);
436                 WM_event_add_keymap_handler(&win->handlers, keymap);
437                 
438                 keymap = WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
439                 WM_event_add_keymap_handler(&win->handlers, keymap);
440
441                 keymap = WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
442                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
443                 
444                 /* add drop boxes */
445                 {
446                         ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
447                         WM_event_add_dropbox_handler(&win->handlers, lb);
448                 }
449                 wm_window_title(wm, win);
450         }
451 }
452
453 /* new window, no screen yet, but we open ghostwindow for it */
454 /* also gets the window level handlers */
455 /* area-rip calls this */
456 wmWindow *WM_window_open(bContext *C, rcti *rect)
457 {
458         wmWindow *win = wm_window_new(C);
459         
460         win->posx = rect->xmin;
461         win->posy = rect->ymin;
462         win->sizex = BLI_RCT_SIZE_X(rect);
463         win->sizey = BLI_RCT_SIZE_Y(rect);
464
465         win->drawmethod = -1;
466         win->drawdata = NULL;
467         
468         WM_check(C);
469         
470         return win;
471 }
472
473 /* uses screen->temp tag to define what to do, currently it limits
474  * to only one "temp" window for render out, preferences, filewindow, etc */
475 /* type is defined in WM_api.h */
476
477 void WM_window_open_temp(bContext *C, rcti *position, int type)
478 {
479         wmWindow *win;
480         ScrArea *sa;
481         
482         /* changes rect to fit within desktop */
483         wm_window_check_position(position);
484         
485         /* test if we have a temp screen already */
486         for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
487                 if (win->screen->temp)
488                         break;
489         
490         /* add new window? */
491         if (win == NULL) {
492                 win = wm_window_new(C);
493                 
494                 win->posx = position->xmin;
495                 win->posy = position->ymin;
496         }
497         
498         win->sizex = BLI_RCT_SIZE_X(position);
499         win->sizey = BLI_RCT_SIZE_Y(position);
500         
501         if (win->ghostwin) {
502                 wm_window_set_size(win, win->sizex, win->sizey);
503                 wm_window_raise(win);
504         }
505         
506         /* add new screen? */
507         if (win->screen == NULL)
508                 win->screen = ED_screen_add(win, CTX_data_scene(C), "temp");
509         win->screen->temp = 1; 
510         
511         /* make window active, and validate/resize */
512         CTX_wm_window_set(C, win);
513         WM_check(C);
514         
515         /* ensure it shows the right spacetype editor */
516         sa = win->screen->areabase.first;
517         CTX_wm_area_set(C, sa);
518         
519         if (type == WM_WINDOW_RENDER) {
520                 ED_area_newspace(C, sa, SPACE_IMAGE);
521         }
522         else {
523                 ED_area_newspace(C, sa, SPACE_USERPREF);
524         }
525         
526         ED_screen_set(C, win->screen);
527         
528         if (sa->spacetype == SPACE_IMAGE)
529                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender Render"));
530         else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
531                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender User Preferences"));
532         else if (sa->spacetype == SPACE_FILE)
533                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender File View"));
534         else
535                 GHOST_SetTitle(win->ghostwin, "Blender");
536 }
537
538
539 /* ****************** Operators ****************** */
540
541 /* operator callback */
542 int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
543 {
544         wm_window_copy(C, CTX_wm_window(C));
545         WM_check(C);
546         
547         WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL);
548         
549         return OPERATOR_FINISHED;
550 }
551
552
553 /* fullscreen operator callback */
554 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
555 {
556         wmWindow *window = CTX_wm_window(C);
557         GHOST_TWindowState state;
558
559         if (G.background)
560                 return OPERATOR_CANCELLED;
561
562         state = GHOST_GetWindowState(window->ghostwin);
563         if (state != GHOST_kWindowStateFullScreen)
564                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
565         else
566                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
567
568         return OPERATOR_FINISHED;
569         
570 }
571
572
573 /* ************ events *************** */
574
575 typedef enum {
576         SHIFT    = 's',
577         CONTROL  = 'c',
578         ALT      = 'a',
579         OS       = 'C'
580 } modifierKeyType;
581
582 /* check if specified modifier key type is pressed */
583 static int query_qual(modifierKeyType qual) 
584 {
585         GHOST_TModifierKeyMask left, right;
586         int val = 0;
587         
588         switch (qual) {
589                 case SHIFT:
590                         left = GHOST_kModifierKeyLeftShift;
591                         right = GHOST_kModifierKeyRightShift;
592                         break;
593                 case CONTROL:
594                         left = GHOST_kModifierKeyLeftControl;
595                         right = GHOST_kModifierKeyRightControl;
596                         break;
597                 case OS:
598                         left = right = GHOST_kModifierKeyOS;
599                         break;
600                 case ALT:
601                 default:
602                         left = GHOST_kModifierKeyLeftAlt;
603                         right = GHOST_kModifierKeyRightAlt;
604                         break;
605         }
606         
607         GHOST_GetModifierKeyState(g_system, left, &val);
608         if (!val)
609                 GHOST_GetModifierKeyState(g_system, right, &val);
610         
611         return val;
612 }
613
614 void wm_window_make_drawable(bContext *C, wmWindow *win) 
615 {
616         wmWindowManager *wm = CTX_wm_manager(C);
617
618         if (win != wm->windrawable && win->ghostwin) {
619 //              win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
620                 
621                 wm->windrawable = win;
622                 if (G.debug & G_DEBUG_EVENTS) {
623                         printf("%s: set drawable %d\n", __func__, win->winid);
624                 }
625                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
626         }
627 }
628
629 /* called by ghost, here we handle events for windows themselves or send to event system */
630 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
631 {
632         bContext *C = C_void_ptr;
633         wmWindowManager *wm = CTX_wm_manager(C);
634         GHOST_TEventType type = GHOST_GetEventType(evt);
635         int time = GHOST_GetEventTime(evt);
636         
637         if (type == GHOST_kEventQuit) {
638                 WM_exit(C);
639         }
640         else {
641                 GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt);
642                 GHOST_TEventDataPtr data = GHOST_GetEventData(evt);
643                 wmWindow *win;
644                 
645                 if (!ghostwin) {
646                         /* XXX - should be checked, why are we getting an event here, and */
647                         /* what is it? */
648                         puts("<!> event has no window");
649                         return 1;
650                 }
651                 else if (!GHOST_ValidWindow(g_system, ghostwin)) {
652                         /* XXX - should be checked, why are we getting an event here, and */
653                         /* what is it? */
654                         puts("<!> event has invalid window");                   
655                         return 1;
656                 }
657                 else {
658                         win = GHOST_GetWindowUserData(ghostwin);
659                 }
660                 
661                 switch (type) {
662                         case GHOST_kEventWindowDeactivate:
663                                 wm_event_add_ghostevent(wm, win, type, time, data);
664                                 win->active = 0; /* XXX */
665                                 break;
666                         case GHOST_kEventWindowActivate: 
667                         {
668                                 GHOST_TEventKeyData kdata;
669                                 wmEvent event;
670                                 int cx, cy, wx, wy;
671                                 
672                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
673                                 
674                                 win->active = 1;
675 //                              window_handle(win, INPUTCHANGE, win->active);
676                                 
677                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
678                                 kdata.ascii = '\0';
679                                 kdata.utf8_buf[0] = '\0';
680                                 if (win->eventstate->shift && !query_qual(SHIFT)) {
681                                         kdata.key = GHOST_kKeyLeftShift;
682                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
683                                 }
684                                 if (win->eventstate->ctrl && !query_qual(CONTROL)) {
685                                         kdata.key = GHOST_kKeyLeftControl;
686                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
687                                 }
688                                 if (win->eventstate->alt && !query_qual(ALT)) {
689                                         kdata.key = GHOST_kKeyLeftAlt;
690                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
691                                 }
692                                 if (win->eventstate->oskey && !query_qual(OS)) {
693                                         kdata.key = GHOST_kKeyOS;
694                                         wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
695                                 }
696                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
697                                 win->eventstate->keymodifier = 0;
698                                 
699                                 /* entering window, update mouse pos. but no event */
700                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
701                                 
702                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
703                                 win->eventstate->x = cx;
704                                 win->eventstate->y = (win->sizey - 1) - cy;
705                                 
706                                 win->addmousemove = 1;   /* enables highlighted buttons */
707                                 
708                                 wm_window_make_drawable(C, win);
709
710                                 /* window might be focused by mouse click in configuration of window manager
711                                  * when focus is not following mouse
712                                  * click could have been done on a button and depending on window manager settings
713                                  * click would be passed to blender or not, but in any case button under cursor
714                                  * should be activated, so at max next click on button without moving mouse
715                                  * would trigger it's handle function
716                                  * currently it seems to be common practice to generate new event for, but probably
717                                  * we'll need utility function for this? (sergey)
718                                  */
719                                 event = *(win->eventstate);
720                                 event.type = MOUSEMOVE;
721                                 event.prevx = event.x;
722                                 event.prevy = event.y;
723
724                                 wm_event_add(win, &event);
725
726                                 break;
727                         }
728                         case GHOST_kEventWindowClose: {
729                                 wm_window_close(C, wm, win);
730                                 break;
731                         }
732                         case GHOST_kEventWindowUpdate: {
733                                 if (G.debug & G_DEBUG_EVENTS) {
734                                         printf("%s: ghost redraw %d\n", __func__, win->winid);
735                                 }
736                                 
737                                 wm_window_make_drawable(C, win);
738                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
739
740                                 break;
741                         }
742                         case GHOST_kEventWindowSize:
743                         case GHOST_kEventWindowMove: {
744                                 GHOST_TWindowState state;
745                                 state = GHOST_GetWindowState(win->ghostwin);
746                                 win->windowstate = state;
747
748                                 /* win32: gives undefined window size when minimized */
749                                 if (state != GHOST_kWindowStateMinimized) {
750                                         GHOST_RectangleHandle client_rect;
751                                         int l, t, r, b, scr_w, scr_h;
752                                         int sizex, sizey, posx, posy;
753                                         
754                                         client_rect = GHOST_GetClientBounds(win->ghostwin);
755                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
756                                         
757                                         GHOST_DisposeRectangle(client_rect);
758                                         
759                                         wm_get_screensize(&scr_w, &scr_h);
760                                         sizex = r - l;
761                                         sizey = b - t;
762                                         posx = l;
763                                         posy = scr_h - t - win->sizey;
764
765                                         /*
766                                          * Ghost sometimes send size or move events when the window hasn't changed.
767                                          * One case of this is using compiz on linux. To alleviate the problem
768                                          * we ignore all such event here.
769                                          * 
770                                          * It might be good to eventually do that at Ghost level, but that is for 
771                                          * another time.
772                                          */
773                                         if (win->sizex != sizex ||
774                                             win->sizey != sizey ||
775                                             win->posx != posx ||
776                                             win->posy != posy)
777                                         {
778                                                 win->sizex = sizex;
779                                                 win->sizey = sizey;
780                                                 win->posx = posx;
781                                                 win->posy = posy;
782
783                                                 /* debug prints */
784                                                 if (G.debug & G_DEBUG_EVENTS) {
785                                                         const char *state_str;
786                                                         state = GHOST_GetWindowState(win->ghostwin);
787
788                                                         if (state == GHOST_kWindowStateNormal) {
789                                                                 state_str = "normal";
790                                                         }
791                                                         else if (state == GHOST_kWindowStateMinimized) {
792                                                                 state_str = "minimized";
793                                                         }
794                                                         else if (state == GHOST_kWindowStateMaximized) {
795                                                                 state_str = "maximized";
796                                                         }
797                                                         else if (state == GHOST_kWindowStateFullScreen) {
798                                                                 state_str = "fullscreen";
799                                                         }
800                                                         else {
801                                                                 state_str = "<unknown>";
802                                                         }
803
804                                                         printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
805
806                                                         if (type != GHOST_kEventWindowSize) {
807                                                                 printf("win move event pos %d %d size %d %d\n",
808                                                                        win->posx, win->posy, win->sizex, win->sizey);
809                                                         }
810                                                 }
811                                         
812                                                 wm_window_make_drawable(C, win);
813                                                 wm_draw_window_clear(win);
814                                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
815                                                 WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
816                                         }
817                                 }
818                                 break;
819                         }
820                                 
821                         case GHOST_kEventOpenMainFile:
822                         {
823                                 PointerRNA props_ptr;
824                                 wmWindow *oldWindow;
825                                 char *path = GHOST_GetEventData(evt);
826                                 
827                                 if (path) {
828                                         /* operator needs a valid window in context, ensures
829                                          * it is correctly set */
830                                         oldWindow = CTX_wm_window(C);
831                                         CTX_wm_window_set(C, win);
832                                         
833                                         WM_operator_properties_create(&props_ptr, "WM_OT_open_mainfile");
834                                         RNA_string_set(&props_ptr, "filepath", path);
835                                         WM_operator_name_call(C, "WM_OT_open_mainfile", WM_OP_EXEC_DEFAULT, &props_ptr);
836                                         WM_operator_properties_free(&props_ptr);
837                                         
838                                         CTX_wm_window_set(C, oldWindow);
839                                 }
840                                 break;
841                         }
842                         case GHOST_kEventDraggingDropDone:
843                         {
844                                 wmEvent event;
845                                 GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
846                                 int cx, cy, wx, wy;
847                                 
848                                 /* entering window, update mouse pos */
849                                 GHOST_GetCursorPosition(g_system, &wx, &wy);
850                                 
851                                 GHOST_ScreenToClient(win->ghostwin, wx, wy, &cx, &cy);
852                                 win->eventstate->x = cx;
853                                 win->eventstate->y = (win->sizey - 1) - cy;
854                                 
855                                 event = *(win->eventstate);  /* copy last state, like mouse coords */
856                                 
857                                 /* activate region */
858                                 event.type = MOUSEMOVE;
859                                 event.prevx = event.x;
860                                 event.prevy = event.y;
861                                 
862                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
863                                 win->active = 1;
864                                 
865                                 wm_event_add(win, &event);
866                                 
867                                 
868                                 /* make blender drop event with custom data pointing to wm drags */
869                                 event.type = EVT_DROP;
870                                 event.val = KM_RELEASE;
871                                 event.custom = EVT_DATA_LISTBASE;
872                                 event.customdata = &wm->drags;
873                                 event.customdatafree = 1;
874                                 
875                                 wm_event_add(win, &event);
876                                 
877                                 /* printf("Drop detected\n"); */
878                                 
879                                 /* add drag data to wm for paths: */
880                                 
881                                 if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
882                                         GHOST_TStringArray *stra = ddd->data;
883                                         int a, icon;
884                                         
885                                         for (a = 0; a < stra->count; a++) {
886                                                 printf("drop file %s\n", stra->strings[a]);
887                                                 /* try to get icon type from extension */
888                                                 icon = ED_file_extension_icon((char *)stra->strings[a]);
889                                                 
890                                                 WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0);
891                                                 /* void poin should point to string, it makes a copy */
892                                                 break; /* only one drop element supported now */
893                                         }
894                                 }
895                                 
896                                 
897                                 
898                                 break;
899                         }
900                         
901                         default:
902                                 wm_event_add_ghostevent(wm, win, type, time, data);
903                                 break;
904                 }
905
906         }
907         return 1;
908 }
909
910
911 /* This timer system only gives maximum 1 timer event per redraw cycle,
912  * to prevent queues to get overloaded.
913  * Timer handlers should check for delta to decide if they just
914  * update, or follow real time.
915  * Timer handlers can also set duration to match frames passed
916  */
917 static int wm_window_timer(const bContext *C)
918 {
919         wmWindowManager *wm = CTX_wm_manager(C);
920         wmTimer *wt, *wtnext;
921         wmWindow *win;
922         double time = PIL_check_seconds_timer();
923         int retval = 0;
924         
925         for (wt = wm->timers.first; wt; wt = wtnext) {
926                 wtnext = wt->next; /* in case timer gets removed */
927                 win = wt->win;
928
929                 if (wt->sleep == 0) {
930                         if (time > wt->ntime) {
931                                 wt->delta = time - wt->ltime;
932                                 wt->duration += wt->delta;
933                                 wt->ltime = time;
934                                 wt->ntime = wt->stime + wt->timestep *ceil(wt->duration / wt->timestep);
935
936                                 if (wt->event_type == TIMERJOBS)
937                                         wm_jobs_timer(C, wm, wt);
938                                 else if (wt->event_type == TIMERAUTOSAVE)
939                                         wm_autosave_timer(C, wm, wt);
940                                 else if (win) {
941                                         wmEvent event = *(win->eventstate);
942                                         
943                                         event.type = wt->event_type;
944                                         event.custom = EVT_DATA_TIMER;
945                                         event.customdata = wt;
946                                         wm_event_add(win, &event);
947
948                                         retval = 1;
949                                 }
950                         }
951                 }
952         }
953         return retval;
954 }
955
956 void wm_window_process_events(const bContext *C) 
957 {
958         int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
959         
960         if (hasevent)
961                 GHOST_DispatchEvents(g_system);
962         
963         hasevent |= wm_window_timer(C);
964
965         /* no event, we sleep 5 milliseconds */
966         if (hasevent == 0)
967                 PIL_sleep_ms(5);
968 }
969
970 void wm_window_process_events_nosleep(void) 
971 {
972         if (GHOST_ProcessEvents(g_system, 0))
973                 GHOST_DispatchEvents(g_system);
974 }
975
976 /* exported as handle callback to bke blender.c */
977 void wm_window_testbreak(void)
978 {
979         static double ltime = 0;
980         double curtime = PIL_check_seconds_timer();
981         
982         /* only check for breaks every 50 milliseconds
983          * if we get called more often.
984          */
985         if ((curtime - ltime) > 0.05) {
986                 int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
987                 
988                 if (hasevent)
989                         GHOST_DispatchEvents(g_system);
990                 
991                 ltime = curtime;
992         }
993 }
994
995 /* **************** init ********************** */
996
997 void wm_ghost_init(bContext *C)
998 {
999         if (!g_system) {
1000                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
1001                 
1002                 g_system = GHOST_CreateSystem();
1003                 GHOST_AddEventConsumer(g_system, consumer);
1004         }       
1005 }
1006
1007 void wm_ghost_exit(void)
1008 {
1009         if (g_system)
1010                 GHOST_DisposeSystem(g_system);
1011
1012         g_system = NULL;
1013 }
1014
1015 /* **************** timer ********************** */
1016
1017 /* to (de)activate running timers temporary */
1018 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer, int dosleep)
1019 {
1020         wmTimer *wt;
1021         
1022         for (wt = wm->timers.first; wt; wt = wt->next)
1023                 if (wt == timer)
1024                         break;
1025
1026         if (wt)
1027                 wt->sleep = dosleep;
1028 }
1029
1030 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
1031 {
1032         wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1033         
1034         wt->event_type = event_type;
1035         wt->ltime = PIL_check_seconds_timer();
1036         wt->ntime = wt->ltime + timestep;
1037         wt->stime = wt->ltime;
1038         wt->timestep = timestep;
1039         wt->win = win;
1040         
1041         BLI_addtail(&wm->timers, wt);
1042         
1043         return wt;
1044 }
1045
1046 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
1047 {
1048         wmTimer *wt;
1049         
1050         /* extra security check */
1051         for (wt = wm->timers.first; wt; wt = wt->next)
1052                 if (wt == timer)
1053                         break;
1054         if (wt) {
1055                 if (wm->reports.reporttimer == wt)
1056                         wm->reports.reporttimer = NULL;
1057                 
1058                 BLI_remlink(&wm->timers, wt);
1059                 if (wt->customdata)
1060                         MEM_freeN(wt->customdata);
1061                 MEM_freeN(wt);
1062         }
1063 }
1064
1065 /* ******************* clipboard **************** */
1066
1067 char *WM_clipboard_text_get(int selection)
1068 {
1069         char *p, *p2, *buf, *newbuf;
1070
1071         if (G.background)
1072                 return NULL;
1073
1074         buf = (char *)GHOST_getClipboard(selection);
1075         if (!buf)
1076                 return NULL;
1077         
1078         /* always convert from \r\n to \n */
1079         newbuf = MEM_callocN(strlen(buf) + 1, "WM_clipboard_text_get");
1080
1081         for (p = buf, p2 = newbuf; *p; p++) {
1082                 if (*p != '\r')
1083                         *(p2++) = *p;
1084         }
1085         *p2 = '\0';
1086
1087         free(buf); /* ghost uses regular malloc */
1088         
1089         return newbuf;
1090 }
1091
1092 void WM_clipboard_text_set(char *buf, int selection)
1093 {
1094         if (!G.background) {
1095 #ifdef _WIN32
1096                 /* do conversion from \n to \r\n on Windows */
1097                 char *p, *p2, *newbuf;
1098                 int newlen = 0;
1099                 
1100                 for (p = buf; *p; p++) {
1101                         if (*p == '\n')
1102                                 newlen += 2;
1103                         else
1104                                 newlen++;
1105                 }
1106                 
1107                 newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
1108         
1109                 for (p = buf, p2 = newbuf; *p; p++, p2++) {
1110                         if (*p == '\n') {
1111                                 *(p2++) = '\r'; *p2 = '\n';
1112                         }
1113                         else *p2 = *p;
1114                 }
1115                 *p2 = '\0';
1116         
1117                 GHOST_putClipboard((GHOST_TInt8 *)newbuf, selection);
1118                 MEM_freeN(newbuf);
1119 #else
1120                 GHOST_putClipboard((GHOST_TInt8 *)buf, selection);
1121 #endif
1122         }
1123 }
1124
1125 /* ******************* progress bar **************** */
1126
1127 void WM_progress_set(wmWindow *win, float progress)
1128 {
1129         GHOST_SetProgressBar(win->ghostwin, progress);
1130 }
1131
1132 void WM_progress_clear(wmWindow *win)
1133 {
1134         GHOST_EndProgressBar(win->ghostwin);
1135 }
1136
1137 /* ************************************ */
1138
1139 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
1140 {
1141         *posx_r = win->posx;
1142         *posy_r = win->posy;
1143 }
1144
1145 void wm_window_get_size(wmWindow *win, int *width_r, int *height_r) 
1146 {
1147         *width_r = win->sizex;
1148         *height_r = win->sizey;
1149 }
1150
1151 /* exceptional case: - splash is called before events are processed
1152  * this means we don't actually know the window size so get this from GHOST */
1153 void wm_window_get_size_ghost(wmWindow *win, int *width_r, int *height_r)
1154 {
1155         GHOST_RectangleHandle bounds = GHOST_GetClientBounds(win->ghostwin);
1156         *width_r = GHOST_GetWidthRectangle(bounds);
1157         *height_r = GHOST_GetHeightRectangle(bounds);
1158         
1159         GHOST_DisposeRectangle(bounds);
1160 }
1161
1162 void wm_window_set_size(wmWindow *win, int width, int height) 
1163 {
1164         GHOST_SetClientSize(win->ghostwin, width, height);
1165 }
1166
1167 void wm_window_lower(wmWindow *win) 
1168 {
1169         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
1170 }
1171
1172 void wm_window_raise(wmWindow *win) 
1173 {
1174         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
1175 }
1176
1177 void wm_window_swap_buffers(wmWindow *win)
1178 {
1179         
1180 #ifdef WIN32
1181         glDisable(GL_SCISSOR_TEST);
1182         GHOST_SwapWindowBuffers(win->ghostwin);
1183         glEnable(GL_SCISSOR_TEST);
1184 #else
1185         GHOST_SwapWindowBuffers(win->ghostwin);
1186 #endif
1187 }
1188
1189 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
1190 {
1191         GHOST_GetCursorPosition(g_system, x, y);
1192         GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
1193         *y = (win->sizey - 1) - *y;
1194 }
1195
1196 /* ******************* exported api ***************** */
1197
1198
1199 /* called whem no ghost system was initialized */
1200 void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
1201 {
1202         wm_init_state.start_x = stax; /* left hand pos */
1203         wm_init_state.start_y = stay; /* bottom pos */
1204         wm_init_state.size_x = sizx;
1205         wm_init_state.size_y = sizy;
1206         wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
1207 }
1208
1209 /* for borderless and border windows set from command-line */
1210 void WM_init_state_fullscreen_set(void)
1211 {
1212         wm_init_state.windowstate = GHOST_kWindowStateFullScreen;
1213         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1214 }
1215
1216 void WM_init_state_normal_set(void)
1217 {
1218         wm_init_state.windowstate = GHOST_kWindowStateNormal;
1219         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1220 }
1221
1222 /* This function requires access to the GHOST_SystemHandle (g_system) */
1223 void WM_cursor_warp(wmWindow *win, int x, int y)
1224 {
1225         if (win && win->ghostwin) {
1226                 int oldx = x, oldy = y;
1227
1228                 y = win->sizey - y - 1;
1229
1230                 GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
1231                 GHOST_SetCursorPosition(g_system, x, y);
1232
1233                 win->eventstate->prevx = oldx;
1234                 win->eventstate->prevy = oldy;
1235         }
1236 }
1237