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