Ghost Context Refactor
[blender-staging.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  * Window management, wrap GHOST.
31  */
32
33 #include <math.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "DNA_listBase.h"       
39 #include "DNA_screen_types.h"
40 #include "DNA_windowmanager_types.h"
41
42 #include "MEM_guardedalloc.h"
43
44 #include "GHOST_C-api.h"
45
46 #include "BLI_math.h"
47 #include "BLI_blenlib.h"
48 #include "BLI_utildefines.h"
49
50 #include "BLF_translation.h"
51
52 #include "BKE_blender.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 "RNA_access.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 #include "GPU_init_exit.h"
77 #include "GPU_glew.h"
78
79 #include "UI_interface.h"
80
81 /* for assert */
82 #ifndef NDEBUG
83 #  include "BLI_threads.h"
84 #endif
85
86 /* the global to talk to ghost */
87 static GHOST_SystemHandle g_system = NULL;
88
89 typedef enum WinOverrideFlag {
90         WIN_OVERRIDE_GEOM     = (1 << 0),
91         WIN_OVERRIDE_WINSTATE = (1 << 1)
92 } WinOverrideFlag;
93
94 /* set by commandline */
95 static struct WMInitStruct {
96         /* window geometry */
97         int size_x, size_y;
98         int start_x, start_y;
99
100         int windowstate;
101         WinOverrideFlag override_flag;
102         
103         bool native_pixels;
104 } wm_init_state = {0, 0, 0, 0, GHOST_kWindowStateNormal, 0, true};
105
106 /* ******** win open & close ************ */
107
108 /* XXX this one should correctly check for apple top header...
109  * done for Cocoa : returns window contents (and not frame) max size*/
110 void wm_get_screensize(int *width_r, int *height_r)
111 {
112         unsigned int uiwidth;
113         unsigned int uiheight;
114         
115         GHOST_GetMainDisplayDimensions(g_system, &uiwidth, &uiheight);
116         *width_r = uiwidth;
117         *height_r = uiheight;
118 }
119
120 /* size of all screens (desktop), useful since the mouse is bound by this */
121 void wm_get_desktopsize(int *width_r, int *height_r)
122 {
123         unsigned int uiwidth;
124         unsigned int uiheight;
125
126         GHOST_GetAllDisplayDimensions(g_system, &uiwidth, &uiheight);
127         *width_r = uiwidth;
128         *height_r = uiheight;
129 }
130
131 /* keeps offset and size within monitor bounds */
132 /* XXX solve dual screen... */
133 static void wm_window_check_position(rcti *rect)
134 {
135         int width, height, d;
136         
137         wm_get_screensize(&width, &height);
138         
139         if (rect->xmin < 0) {
140                 rect->xmax -= rect->xmin;
141                 rect->xmin  = 0;
142         }
143         if (rect->ymin < 0) {
144                 rect->ymax -= rect->ymin;
145                 rect->ymin  = 0;
146         }
147         if (rect->xmax > width) {
148                 d = rect->xmax - width;
149                 rect->xmax -= d;
150                 rect->xmin -= d;
151         }
152         if (rect->ymax > height) {
153                 d = rect->ymax - height;
154                 rect->ymax -= d;
155                 rect->ymin -= d;
156         }
157         
158         if (rect->xmin < 0) rect->xmin = 0;
159         if (rect->ymin < 0) rect->ymin = 0;
160 }
161
162
163 static void wm_ghostwindow_destroy(wmWindow *win) 
164 {
165         if (win->ghostwin) {
166                 GHOST_DisposeWindow(g_system, win->ghostwin);
167                 win->ghostwin = NULL;
168         }
169 }
170
171 /* including window itself, C can be NULL. 
172  * ED_screen_exit should have been called */
173 void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
174 {
175         wmTimer *wt, *wtnext;
176         
177         /* update context */
178         if (C) {
179                 WM_event_remove_handlers(C, &win->handlers);
180                 WM_event_remove_handlers(C, &win->modalhandlers);
181
182                 if (CTX_wm_window(C) == win)
183                         CTX_wm_window_set(C, NULL);
184         }
185
186         /* always set drawable and active to NULL,
187          * prevents non-drawable state of main windows (bugs #22967 and #25071, possibly #22477 too) */
188         wm->windrawable = NULL;
189         wm->winactive = NULL;
190
191         /* end running jobs, a job end also removes its timer */
192         for (wt = wm->timers.first; wt; wt = wtnext) {
193                 wtnext = wt->next;
194                 if (wt->win == win && wt->event_type == TIMERJOBS)
195                         wm_jobs_timer_ended(wm, wt);
196         }
197         
198         /* timer removing, need to call this api function */
199         for (wt = wm->timers.first; wt; wt = wtnext) {
200                 wtnext = wt->next;
201                 if (wt->win == win)
202                         WM_event_remove_timer(wm, win, wt);
203         }
204
205         if (win->eventstate) MEM_freeN(win->eventstate);
206         
207         wm_event_free_all(win);
208         wm_subwindows_free(win);
209         
210         if (win->drawdata)
211                 MEM_freeN(win->drawdata);
212         
213         wm_ghostwindow_destroy(win);
214         
215         MEM_freeN(win);
216 }
217
218 static int find_free_winid(wmWindowManager *wm)
219 {
220         wmWindow *win;
221         int id = 1;
222         
223         for (win = wm->windows.first; win; win = win->next)
224                 if (id <= win->winid)
225                         id = win->winid + 1;
226         
227         return id;
228 }
229
230 /* don't change context itself */
231 wmWindow *wm_window_new(bContext *C)
232 {
233         wmWindowManager *wm = CTX_wm_manager(C);
234         wmWindow *win = MEM_callocN(sizeof(wmWindow), "window");
235         
236         BLI_addtail(&wm->windows, win);
237         win->winid = find_free_winid(wm);
238
239         return win;
240 }
241
242
243 /* part of wm_window.c api */
244 wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
245 {
246         wmWindow *win = wm_window_new(C);
247         
248         win->posx = winorig->posx + 10;
249         win->posy = winorig->posy;
250         win->sizex = winorig->sizex;
251         win->sizey = winorig->sizey;
252         
253         /* duplicate assigns to window */
254         win->screen = ED_screen_duplicate(win, winorig->screen);
255         BLI_strncpy(win->screenname, win->screen->id.name + 2, sizeof(win->screenname));
256         win->screen->winid = win->winid;
257
258         win->screen->do_refresh = true;
259         win->screen->do_draw = true;
260
261         win->drawmethod = U.wmdrawmethod;
262         win->drawdata = NULL;
263         
264         return win;
265 }
266
267 /* this is event from ghost, or exit-blender op */
268 void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
269 {
270         wmWindow *tmpwin;
271         bool do_exit = false;
272         
273         /* first check if we have to quit (there are non-temp remaining windows) */
274         for (tmpwin = wm->windows.first; tmpwin; tmpwin = tmpwin->next) {
275                 if (tmpwin == win)
276                         continue;
277                 if (tmpwin->screen->temp == 0)
278                         break;
279         }
280
281         if (tmpwin == NULL)
282                 do_exit = 1;
283         
284         if ((U.uiflag & USER_QUIT_PROMPT) && !wm->file_saved) {
285                 if (do_exit) {
286                         if (!GHOST_confirmQuit(win->ghostwin))
287                                 return;
288                 }
289         }
290
291         /* let WM_exit do all freeing, for correct quit.blend save */
292         if (do_exit) {
293                 WM_exit(C);
294         }
295         else {
296                 bScreen *screen = win->screen;
297                 
298                 BLI_remlink(&wm->windows, win);
299                 
300                 wm_draw_window_clear(win);
301                 
302                 CTX_wm_window_set(C, win);  /* needed by handlers */
303                 WM_event_remove_handlers(C, &win->handlers);
304                 WM_event_remove_handlers(C, &win->modalhandlers);
305                 ED_screen_exit(C, win, win->screen); 
306                 
307                 wm_window_free(C, wm, win);
308         
309                 /* if temp screen, delete it after window free (it stops jobs that can access it) */
310                 if (screen->temp) {
311                         Main *bmain = CTX_data_main(C);
312                         BKE_libblock_free(bmain, screen);
313                 }
314         }               
315 }
316
317 void wm_window_title(wmWindowManager *wm, wmWindow *win)
318 {
319         if (win->screen && win->screen->temp) {
320                 /* nothing to do for 'temp' windows,
321                  * because WM_window_open_temp always sets window title  */
322         }
323         else if (win->ghostwin) {
324                 /* this is set to 1 if you don't have startup.blend open */
325                 if (G.save_over && G.main->name[0]) {
326                         char str[sizeof(G.main->name) + 24];
327                         BLI_snprintf(str, sizeof(str), "Blender%s [%s%s]", wm->file_saved ? "" : "*", G.main->name,
328                                      G.main->recovered ? " (Recovered)" : "");
329                         GHOST_SetTitle(win->ghostwin, str);
330                 }
331                 else
332                         GHOST_SetTitle(win->ghostwin, "Blender");
333
334                 /* Informs GHOST of unsaved changes, to set window modified visual indicator (MAC OS X)
335                  * and to give hint of unsaved changes for a user warning mechanism
336                  * in case of OS application terminate request (e.g. OS Shortcut Alt+F4, Cmd+Q, (...), or session end) */
337                 GHOST_SetWindowModifiedState(win->ghostwin, (GHOST_TUns8) !wm->file_saved);
338                 
339         }
340 }
341
342 float wm_window_pixelsize(wmWindow *win)
343 {
344         float pixelsize = GHOST_GetNativePixelSize(win->ghostwin);
345         
346         switch (U.virtual_pixel) {
347                 default:
348                 case VIRTUAL_PIXEL_NATIVE:
349                         return pixelsize;
350                 case VIRTUAL_PIXEL_DOUBLE:
351                         return 2.0f * pixelsize;
352         }
353 }
354
355 /* belongs to below */
356 static void wm_window_add_ghostwindow(wmWindowManager *wm, const char *title, wmWindow *win)
357 {
358         GHOST_WindowHandle ghostwin;
359         static int multisamples = -1;
360         int scr_w, scr_h, posy;
361         
362         /* force setting multisamples only once, it requires restart - and you cannot 
363          * mix it, either all windows have it, or none (tested in OSX opengl) */
364         if (multisamples == -1)
365                 multisamples = U.ogl_multisamples;
366         
367         wm_get_screensize(&scr_w, &scr_h);
368         posy = (scr_h - win->posy - win->sizey);
369         
370         ghostwin = GHOST_CreateWindow(g_system, title,
371                                       win->posx, posy, win->sizex, win->sizey,
372                                       (GHOST_TWindowState)win->windowstate,
373                                       GHOST_kDrawingContextTypeOpenGL,
374                                       0 /* no stereo */,
375                                       multisamples /* AA */);
376         
377         if (ghostwin) {
378                 GHOST_RectangleHandle bounds;
379                 
380                 /* the new window has already been made drawable upon creation */
381                 wm->windrawable = win;
382
383                 /* needed so we can detect the graphics card below */
384                 GPU_init();
385                 
386                 win->ghostwin = ghostwin;
387                 GHOST_SetWindowUserData(ghostwin, win); /* pointer back */
388                 
389                 if (win->eventstate == NULL)
390                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
391                 
392 #ifdef __APPLE__
393                 /* set the state here, else OSX would not recognize changed screen resolution */
394                 /* we agreed to not set any fullscreen or iconized state on startup */
395                 GHOST_SetWindowState(ghostwin, GHOST_kWindowStateNormal);
396 #endif
397                 /* store actual window size in blender window */
398                 bounds = GHOST_GetClientBounds(win->ghostwin);
399                 win->sizex = GHOST_GetWidthRectangle(bounds);
400                 win->sizey = GHOST_GetHeightRectangle(bounds);
401                 GHOST_DisposeRectangle(bounds);
402                 
403 #ifndef __APPLE__
404                 /* set the state here, so minimized state comes up correct on windows */
405                 GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate);
406 #endif
407                 /* until screens get drawn, make it nice gray */
408                 glClearColor(0.55, 0.55, 0.55, 0.0);
409                 /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */
410                 if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) {
411                         glClear(GL_COLOR_BUFFER_BIT);
412                 }
413                 
414                 /* displays with larger native pixels, like Macbook. Used to scale dpi with */
415                 /* needed here, because it's used before it reads userdef */
416                 U.pixelsize = wm_window_pixelsize(win);
417                 BKE_userdef_state();
418                 
419                 wm_window_swap_buffers(win);
420                 
421                 //GHOST_SetWindowState(ghostwin, GHOST_kWindowStateModified);
422                 
423                 /* standard state vars for window */
424                 glEnable(GL_SCISSOR_TEST);
425                 GPU_state_init();
426         }
427 }
428
429 /* for wmWindows without ghostwin, open these and clear */
430 /* window size is read from window, if 0 it uses prefsize */
431 /* called in WM_check, also inits stuff after file read */
432 void wm_window_add_ghostwindows(wmWindowManager *wm)
433 {
434         wmKeyMap *keymap;
435         wmWindow *win;
436         
437         /* no commandline prefsize? then we set this.
438          * Note that these values will be used only
439          * when there is no startup.blend yet.
440          */
441         if (wm_init_state.size_x == 0) {
442                 wm_get_screensize(&wm_init_state.size_x, &wm_init_state.size_y);
443                 
444         /* note!, this isnt quite correct, active screen maybe offset 1000s if PX,
445          * we'd need a wm_get_screensize like function that gives offset,
446          * in practice the window manager will likely move to the correct monitor */
447         wm_init_state.start_x = 0;
448         wm_init_state.start_y = 0;
449
450 #ifdef WITH_X11 /* X11 */
451                 /* X11, start maximized but use default sane size */
452                 wm_init_state.size_x = min_ii(wm_init_state.size_x, WM_WIN_INIT_SIZE_X);
453                 wm_init_state.size_y = min_ii(wm_init_state.size_y, WM_WIN_INIT_SIZE_Y);
454                 /* pad */
455                 wm_init_state.start_x = WM_WIN_INIT_PAD;
456                 wm_init_state.start_y = WM_WIN_INIT_PAD;
457                 wm_init_state.size_x -= WM_WIN_INIT_PAD * 2;
458                 wm_init_state.size_y -= WM_WIN_INIT_PAD * 2;
459 #endif
460         }
461         
462         for (win = wm->windows.first; win; win = win->next) {
463                 if (win->ghostwin == NULL) {
464                         if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
465                                 win->posx = wm_init_state.start_x;
466                                 win->posy = wm_init_state.start_y;
467                                 win->sizex = wm_init_state.size_x;
468                                 win->sizey = wm_init_state.size_y;
469
470                                 win->windowstate = GHOST_kWindowStateNormal;
471                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
472                         }
473
474                         if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
475                                 win->windowstate = wm_init_state.windowstate;
476                                 wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
477                         }
478
479                         wm_window_add_ghostwindow(wm, "Blender", win);
480                 }
481                 /* happens after fileread */
482                 if (win->eventstate == NULL)
483                         win->eventstate = MEM_callocN(sizeof(wmEvent), "window event state");
484
485                 /* add keymap handlers (1 handler for all keys in map!) */
486                 keymap = WM_keymap_find(wm->defaultconf, "Window", 0, 0);
487                 WM_event_add_keymap_handler(&win->handlers, keymap);
488                 
489                 keymap = WM_keymap_find(wm->defaultconf, "Screen", 0, 0);
490                 WM_event_add_keymap_handler(&win->handlers, keymap);
491
492                 keymap = WM_keymap_find(wm->defaultconf, "Screen Editing", 0, 0);
493                 WM_event_add_keymap_handler(&win->modalhandlers, keymap);
494                 
495                 /* add drop boxes */
496                 {
497                         ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
498                         WM_event_add_dropbox_handler(&win->handlers, lb);
499                 }
500                 wm_window_title(wm, win);
501         }
502 }
503
504 /* new window, no screen yet, but we open ghostwindow for it */
505 /* also gets the window level handlers */
506 /* area-rip calls this */
507 wmWindow *WM_window_open(bContext *C, const rcti *rect)
508 {
509         wmWindow *win = wm_window_new(C);
510         
511         win->posx = rect->xmin;
512         win->posy = rect->ymin;
513         win->sizex = BLI_rcti_size_x(rect);
514         win->sizey = BLI_rcti_size_y(rect);
515
516         win->drawmethod = U.wmdrawmethod;
517         win->drawdata = NULL;
518         
519         WM_check(C);
520         
521         return win;
522 }
523
524 /* uses screen->temp tag to define what to do, currently it limits
525  * to only one "temp" window for render out, preferences, filewindow, etc */
526 /* type is defined in WM_api.h */
527
528 void WM_window_open_temp(bContext *C, rcti *position, int type)
529 {
530         wmWindow *win;
531         ScrArea *sa;
532         Scene *scene = CTX_data_scene(C);
533         
534         /* changes rect to fit within desktop */
535         wm_window_check_position(position);
536         
537         /* test if we have a temp screen already */
538         for (win = CTX_wm_manager(C)->windows.first; win; win = win->next)
539                 if (win->screen->temp)
540                         break;
541         
542         /* add new window? */
543         if (win == NULL) {
544                 win = wm_window_new(C);
545                 
546                 win->posx = position->xmin;
547                 win->posy = position->ymin;
548         }
549         
550         win->sizex = BLI_rcti_size_x(position);
551         win->sizey = BLI_rcti_size_y(position);
552         
553         if (win->ghostwin) {
554                 wm_window_set_size(win, win->sizex, win->sizey);
555                 wm_window_raise(win);
556         }
557         
558         if (win->screen == NULL) {
559                 /* add new screen */
560                 win->screen = ED_screen_add(win, scene, "temp");
561         }
562         else {
563                 /* switch scene for rendering */
564                 if (win->screen->scene != scene)
565                         ED_screen_set_scene(C, win->screen, scene);
566         }
567
568         win->screen->temp = 1; 
569         
570         /* make window active, and validate/resize */
571         CTX_wm_window_set(C, win);
572         WM_check(C);
573         
574         /* ensure it shows the right spacetype editor */
575         sa = win->screen->areabase.first;
576         CTX_wm_area_set(C, sa);
577         
578         if (type == WM_WINDOW_RENDER) {
579                 ED_area_newspace(C, sa, SPACE_IMAGE);
580         }
581         else {
582                 ED_area_newspace(C, sa, SPACE_USERPREF);
583         }
584         
585         ED_screen_set(C, win->screen);
586         ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
587         
588         if (sa->spacetype == SPACE_IMAGE)
589                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender Render"));
590         else if (ELEM(sa->spacetype, SPACE_OUTLINER, SPACE_USERPREF))
591                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender User Preferences"));
592         else if (sa->spacetype == SPACE_FILE)
593                 GHOST_SetTitle(win->ghostwin, IFACE_("Blender File View"));
594         else
595                 GHOST_SetTitle(win->ghostwin, "Blender");
596 }
597
598
599 /* ****************** Operators ****************** */
600
601 /* operator callback */
602 int wm_window_duplicate_exec(bContext *C, wmOperator *UNUSED(op))
603 {
604         wm_window_copy(C, CTX_wm_window(C));
605         WM_check(C);
606         
607         WM_event_add_notifier(C, NC_WINDOW | NA_ADDED, NULL);
608         
609         return OPERATOR_FINISHED;
610 }
611
612
613 /* fullscreen operator callback */
614 int wm_window_fullscreen_toggle_exec(bContext *C, wmOperator *UNUSED(op))
615 {
616         wmWindow *window = CTX_wm_window(C);
617         GHOST_TWindowState state;
618
619         if (G.background)
620                 return OPERATOR_CANCELLED;
621
622         state = GHOST_GetWindowState(window->ghostwin);
623         if (state != GHOST_kWindowStateFullScreen)
624                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateFullScreen);
625         else
626                 GHOST_SetWindowState(window->ghostwin, GHOST_kWindowStateNormal);
627
628         return OPERATOR_FINISHED;
629         
630 }
631
632
633 /* ************ events *************** */
634
635 static void wm_convert_cursor_position(wmWindow *win, int *x, int *y)
636 {
637         float fac = GHOST_GetNativePixelSize(win->ghostwin);
638         
639         GHOST_ScreenToClient(win->ghostwin, *x, *y, x, y);
640         *x *= fac;
641         
642         *y = (win->sizey - 1) - *y;
643         *y *= fac;
644 }
645
646
647 void wm_get_cursor_position(wmWindow *win, int *x, int *y)
648 {
649         GHOST_GetCursorPosition(g_system, x, y);
650         wm_convert_cursor_position(win, x, y);
651 }
652
653 typedef enum {
654         SHIFT    = 's',
655         CONTROL  = 'c',
656         ALT      = 'a',
657         OS       = 'C'
658 } modifierKeyType;
659
660 /* check if specified modifier key type is pressed */
661 static int query_qual(modifierKeyType qual) 
662 {
663         GHOST_TModifierKeyMask left, right;
664         int val = 0;
665         
666         switch (qual) {
667                 case SHIFT:
668                         left = GHOST_kModifierKeyLeftShift;
669                         right = GHOST_kModifierKeyRightShift;
670                         break;
671                 case CONTROL:
672                         left = GHOST_kModifierKeyLeftControl;
673                         right = GHOST_kModifierKeyRightControl;
674                         break;
675                 case OS:
676                         left = right = GHOST_kModifierKeyOS;
677                         break;
678                 case ALT:
679                 default:
680                         left = GHOST_kModifierKeyLeftAlt;
681                         right = GHOST_kModifierKeyRightAlt;
682                         break;
683         }
684         
685         GHOST_GetModifierKeyState(g_system, left, &val);
686         if (!val)
687                 GHOST_GetModifierKeyState(g_system, right, &val);
688         
689         return val;
690 }
691
692 void wm_window_make_drawable(wmWindowManager *wm, wmWindow *win) 
693 {
694         if (win != wm->windrawable && win->ghostwin) {
695 //              win->lmbut = 0; /* keeps hanging when mousepressed while other window opened */
696                 
697                 wm->windrawable = win;
698                 if (G.debug & G_DEBUG_EVENTS) {
699                         printf("%s: set drawable %d\n", __func__, win->winid);
700                 }
701                 GHOST_ActivateWindowDrawingContext(win->ghostwin);
702                 
703                 /* this can change per window */
704                 U.pixelsize = wm_window_pixelsize(win);
705                 BKE_userdef_state();
706         }
707 }
708
709 /* called by ghost, here we handle events for windows themselves or send to event system */
710 /* mouse coordinate converversion happens here */
711 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr)
712 {
713         bContext *C = C_void_ptr;
714         wmWindowManager *wm = CTX_wm_manager(C);
715         GHOST_TEventType type = GHOST_GetEventType(evt);
716         int time = GHOST_GetEventTime(evt);
717         
718         if (type == GHOST_kEventQuit) {
719                 WM_exit(C);
720         }
721         else {
722                 GHOST_WindowHandle ghostwin = GHOST_GetEventWindow(evt);
723                 GHOST_TEventDataPtr data = GHOST_GetEventData(evt);
724                 wmWindow *win;
725                 
726                 /* Ghost now can call this function for life resizes, but it should return if WM didn't initialize yet.
727                  * Can happen on file read (especially full size window)  */
728                 if ((wm->initialized & WM_INIT_WINDOW) == 0) {
729                         return 1;
730                 }
731                 if (!ghostwin) {
732                         /* XXX - should be checked, why are we getting an event here, and */
733                         /* what is it? */
734                         puts("<!> event has no window");
735                         return 1;
736                 }
737                 else if (!GHOST_ValidWindow(g_system, ghostwin)) {
738                         /* XXX - should be checked, why are we getting an event here, and */
739                         /* what is it? */
740                         puts("<!> event has invalid window");
741                         return 1;
742                 }
743                 else {
744                         win = GHOST_GetWindowUserData(ghostwin);
745                 }
746                 
747                 switch (type) {
748                         case GHOST_kEventWindowDeactivate:
749                                 wm_event_add_ghostevent(wm, win, type, time, data);
750                                 win->active = 0; /* XXX */
751                                 
752                                 /* clear modifiers for inactive windows */
753                                 win->eventstate->alt = 0;
754                                 win->eventstate->ctrl = 0;
755                                 win->eventstate->shift = 0;
756                                 win->eventstate->oskey = 0;
757                                 win->eventstate->keymodifier = 0;
758
759                                 break;
760                         case GHOST_kEventWindowActivate: 
761                         {
762                                 GHOST_TEventKeyData kdata;
763                                 wmEvent event;
764                                 int wx, wy;
765                                 const int keymodifier = ((query_qual(SHIFT)     ? KM_SHIFT : 0) |
766                                                          (query_qual(CONTROL)   ? KM_CTRL  : 0) |
767                                                          (query_qual(ALT)       ? KM_ALT   : 0) |
768                                                          (query_qual(OS)        ? KM_OSKEY : 0));
769
770                                 /* Win23/GHOST modifier bug, see T40317 */
771 #ifndef WIN32
772 //#  define USE_WIN_ACTIVATE
773 #endif
774
775                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
776                                 
777                                 win->active = 1;
778 //                              window_handle(win, INPUTCHANGE, win->active);
779                                 
780                                 /* bad ghost support for modifier keys... so on activate we set the modifiers again */
781
782                                 /* TODO: This is not correct since a modifier may be held when a window is activated...
783                                  * better solve this at ghost level. attempted fix r54450 but it caused bug [#34255]
784                                  *
785                                  * For now don't send GHOST_kEventKeyDown events, just set the 'eventstate'.
786                                  */
787                                 kdata.ascii = '\0';
788                                 kdata.utf8_buf[0] = '\0';
789
790                                 if (win->eventstate->shift) {
791                                         if ((keymodifier & KM_SHIFT) == 0) {
792                                                 kdata.key = GHOST_kKeyLeftShift;
793                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
794                                         }
795                                 }
796 #ifdef USE_WIN_ACTIVATE
797                                 else {
798                                         if (keymodifier & KM_SHIFT) {
799                                                 win->eventstate->shift = KM_MOD_FIRST;
800                                         }
801                                 }
802 #endif
803                                 if (win->eventstate->ctrl) {
804                                         if ((keymodifier & KM_CTRL) == 0) {
805                                                 kdata.key = GHOST_kKeyLeftControl;
806                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
807                                         }
808                                 }
809 #ifdef USE_WIN_ACTIVATE
810                                 else {
811                                         if (keymodifier & KM_CTRL) {
812                                                 win->eventstate->ctrl = KM_MOD_FIRST;
813                                         }
814                                 }
815 #endif
816                                 if (win->eventstate->alt) {
817                                         if ((keymodifier & KM_ALT) == 0) {
818                                                 kdata.key = GHOST_kKeyLeftAlt;
819                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
820                                         }
821                                 }
822 #ifdef USE_WIN_ACTIVATE
823                                 else {
824                                         if (keymodifier & KM_ALT) {
825                                                 win->eventstate->alt = KM_MOD_FIRST;
826                                         }
827                                 }
828 #endif
829                                 if (win->eventstate->oskey) {
830                                         if ((keymodifier & KM_OSKEY) == 0) {
831                                                 kdata.key = GHOST_kKeyOS;
832                                                 wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, time, &kdata);
833                                         }
834                                 }
835 #ifdef USE_WIN_ACTIVATE
836                                 else {
837                                         if (keymodifier & KM_OSKEY) {
838                                                 win->eventstate->oskey = KM_MOD_FIRST;
839                                         }
840                                 }
841 #endif
842
843 #undef USE_WIN_ACTIVATE
844
845
846                                 /* keymodifier zero, it hangs on hotkeys that open windows otherwise */
847                                 win->eventstate->keymodifier = 0;
848                                 
849                                 /* entering window, update mouse pos. but no event */
850                                 wm_get_cursor_position(win,  &wx, &wy);
851
852                                 win->eventstate->x = wx;
853                                 win->eventstate->y = wy;
854                                 
855                                 win->addmousemove = 1;   /* enables highlighted buttons */
856                                 
857                                 wm_window_make_drawable(wm, win);
858
859                                 /* window might be focused by mouse click in configuration of window manager
860                                  * when focus is not following mouse
861                                  * click could have been done on a button and depending on window manager settings
862                                  * click would be passed to blender or not, but in any case button under cursor
863                                  * should be activated, so at max next click on button without moving mouse
864                                  * would trigger it's handle function
865                                  * currently it seems to be common practice to generate new event for, but probably
866                                  * we'll need utility function for this? (sergey)
867                                  */
868                                 wm_event_init_from_window(win, &event);
869                                 event.type = MOUSEMOVE;
870                                 event.prevx = event.x;
871                                 event.prevy = event.y;
872
873                                 wm_event_add(win, &event);
874
875                                 break;
876                         }
877                         case GHOST_kEventWindowClose:
878                         {
879                                 wm_window_close(C, wm, win);
880                                 break;
881                         }
882                         case GHOST_kEventWindowUpdate:
883                         {
884                                 if (G.debug & G_DEBUG_EVENTS) {
885                                         printf("%s: ghost redraw %d\n", __func__, win->winid);
886                                 }
887                                 
888                                 wm_window_make_drawable(wm, win);
889                                 WM_event_add_notifier(C, NC_WINDOW, NULL);
890
891                                 break;
892                         }
893                         case GHOST_kEventWindowSize:
894                         case GHOST_kEventWindowMove:
895                         {
896                                 GHOST_TWindowState state;
897                                 state = GHOST_GetWindowState(win->ghostwin);
898                                 win->windowstate = state;
899
900                                 /* stop screencast if resize */
901                                 if (type == GHOST_kEventWindowSize) {
902                                         WM_jobs_stop(wm, win->screen, NULL);
903                                 }
904                                 
905                                 /* win32: gives undefined window size when minimized */
906                                 if (state != GHOST_kWindowStateMinimized) {
907                                         GHOST_RectangleHandle client_rect;
908                                         int l, t, r, b, scr_w, scr_h;
909                                         int sizex, sizey, posx, posy;
910                                         
911                                         client_rect = GHOST_GetClientBounds(win->ghostwin);
912                                         GHOST_GetRectangle(client_rect, &l, &t, &r, &b);
913                                         
914                                         GHOST_DisposeRectangle(client_rect);
915                                         
916                                         wm_get_desktopsize(&scr_w, &scr_h);
917                                         sizex = r - l;
918                                         sizey = b - t;
919                                         posx = l;
920                                         posy = scr_h - t - win->sizey;
921
922                                         /*
923                                          * Ghost sometimes send size or move events when the window hasn't changed.
924                                          * One case of this is using compiz on linux. To alleviate the problem
925                                          * we ignore all such event here.
926                                          * 
927                                          * It might be good to eventually do that at Ghost level, but that is for 
928                                          * another time.
929                                          */
930                                         if (win->sizex != sizex ||
931                                             win->sizey != sizey ||
932                                             win->posx != posx ||
933                                             win->posy != posy)
934                                         {
935                                                 win->sizex = sizex;
936                                                 win->sizey = sizey;
937                                                 win->posx = posx;
938                                                 win->posy = posy;
939
940                                                 /* debug prints */
941                                                 if (G.debug & G_DEBUG_EVENTS) {
942                                                         const char *state_str;
943                                                         state = GHOST_GetWindowState(win->ghostwin);
944
945                                                         if (state == GHOST_kWindowStateNormal) {
946                                                                 state_str = "normal";
947                                                         }
948                                                         else if (state == GHOST_kWindowStateMinimized) {
949                                                                 state_str = "minimized";
950                                                         }
951                                                         else if (state == GHOST_kWindowStateMaximized) {
952                                                                 state_str = "maximized";
953                                                         }
954                                                         else if (state == GHOST_kWindowStateFullScreen) {
955                                                                 state_str = "fullscreen";
956                                                         }
957                                                         else {
958                                                                 state_str = "<unknown>";
959                                                         }
960
961                                                         printf("%s: window %d state = %s\n", __func__, win->winid, state_str);
962
963                                                         if (type != GHOST_kEventWindowSize) {
964                                                                 printf("win move event pos %d %d size %d %d\n",
965                                                                        win->posx, win->posy, win->sizex, win->sizey);
966                                                         }
967                                                 }
968                                         
969                                                 wm_window_make_drawable(wm, win);
970                                                 wm_draw_window_clear(win);
971                                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
972                                                 WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
973                                                 
974 #if defined(__APPLE__) || defined(WIN32)
975                                                 /* OSX and Win32 don't return to the mainloop while resize */
976                                                 wm_event_do_handlers(C);
977                                                 wm_event_do_notifiers(C);
978                                                 wm_draw_update(C);
979 #endif
980                                         }
981                                 }
982                                 break;
983                         }
984                                 
985                         case GHOST_kEventOpenMainFile:
986                         {
987                                 PointerRNA props_ptr;
988                                 wmWindow *oldWindow;
989                                 const char *path = GHOST_GetEventData(evt);
990                                 
991                                 if (path) {
992                                         wmOperatorType *ot = WM_operatortype_find("WM_OT_open_mainfile", false);
993                                         /* operator needs a valid window in context, ensures
994                                          * it is correctly set */
995                                         oldWindow = CTX_wm_window(C);
996                                         CTX_wm_window_set(C, win);
997                                         
998                                         WM_operator_properties_create_ptr(&props_ptr, ot);
999                                         RNA_string_set(&props_ptr, "filepath", path);
1000                                         WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr);
1001                                         WM_operator_properties_free(&props_ptr);
1002                                         
1003                                         CTX_wm_window_set(C, oldWindow);
1004                                 }
1005                                 break;
1006                         }
1007                         case GHOST_kEventDraggingDropDone:
1008                         {
1009                                 wmEvent event;
1010                                 GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt);
1011                                 int wx, wy;
1012                                 
1013                                 /* entering window, update mouse pos */
1014                                 wm_get_cursor_position(win, &wx, &wy);
1015                                 win->eventstate->x = wx;
1016                                 win->eventstate->y = wy;
1017                                 
1018                                 wm_event_init_from_window(win, &event);  /* copy last state, like mouse coords */
1019                                 
1020                                 /* activate region */
1021                                 event.type = MOUSEMOVE;
1022                                 event.prevx = event.x;
1023                                 event.prevy = event.y;
1024                                 
1025                                 wm->winactive = win; /* no context change! c->wm->windrawable is drawable, or for area queues */
1026                                 win->active = 1;
1027                                 
1028                                 wm_event_add(win, &event);
1029                                 
1030                                 
1031                                 /* make blender drop event with custom data pointing to wm drags */
1032                                 event.type = EVT_DROP;
1033                                 event.val = KM_RELEASE;
1034                                 event.custom = EVT_DATA_DRAGDROP;
1035                                 event.customdata = &wm->drags;
1036                                 event.customdatafree = 1;
1037                                 
1038                                 wm_event_add(win, &event);
1039                                 
1040                                 /* printf("Drop detected\n"); */
1041                                 
1042                                 /* add drag data to wm for paths: */
1043                                 
1044                                 if (ddd->dataType == GHOST_kDragnDropTypeFilenames) {
1045                                         GHOST_TStringArray *stra = ddd->data;
1046                                         int a, icon;
1047                                         
1048                                         for (a = 0; a < stra->count; a++) {
1049                                                 printf("drop file %s\n", stra->strings[a]);
1050                                                 /* try to get icon type from extension */
1051                                                 icon = ED_file_extension_icon((char *)stra->strings[a]);
1052                                                 
1053                                                 WM_event_start_drag(C, icon, WM_DRAG_PATH, stra->strings[a], 0.0, WM_DRAG_NOP);
1054                                                 /* void poin should point to string, it makes a copy */
1055                                                 break; /* only one drop element supported now */
1056                                         }
1057                                 }
1058                                 
1059                                 break;
1060                         }
1061                         case GHOST_kEventNativeResolutionChange:
1062                                 // printf("change, pixel size %f\n", GHOST_GetNativePixelSize(win->ghostwin));
1063                                 
1064                                 U.pixelsize = wm_window_pixelsize(win);
1065                                 BKE_userdef_state();
1066                                 WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL);
1067                                 WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
1068
1069                                 break;
1070                         case GHOST_kEventTrackpad:
1071                         {
1072                                 GHOST_TEventTrackpadData *pd = data;
1073                                 
1074                                 wm_convert_cursor_position(win, &pd->x, &pd->y);
1075                                 wm_event_add_ghostevent(wm, win, type, time, data);
1076                                 break;
1077                         }
1078                         case GHOST_kEventCursorMove:
1079                         {
1080                                 GHOST_TEventCursorData *cd = data;
1081                                 
1082                                 wm_convert_cursor_position(win, &cd->x, &cd->y);
1083                                 wm_event_add_ghostevent(wm, win, type, time, data);
1084                                 break;
1085                         }
1086                         default:
1087                                 wm_event_add_ghostevent(wm, win, type, time, data);
1088                                 break;
1089                 }
1090
1091         }
1092         return 1;
1093 }
1094
1095
1096 /* This timer system only gives maximum 1 timer event per redraw cycle,
1097  * to prevent queues to get overloaded.
1098  * Timer handlers should check for delta to decide if they just
1099  * update, or follow real time.
1100  * Timer handlers can also set duration to match frames passed
1101  */
1102 static int wm_window_timer(const bContext *C)
1103 {
1104         wmWindowManager *wm = CTX_wm_manager(C);
1105         wmTimer *wt, *wtnext;
1106         wmWindow *win;
1107         double time = PIL_check_seconds_timer();
1108         int retval = 0;
1109         
1110         for (wt = wm->timers.first; wt; wt = wtnext) {
1111                 wtnext = wt->next; /* in case timer gets removed */
1112                 win = wt->win;
1113
1114                 if (wt->sleep == 0) {
1115                         if (time > wt->ntime) {
1116                                 wt->delta = time - wt->ltime;
1117                                 wt->duration += wt->delta;
1118                                 wt->ltime = time;
1119                                 wt->ntime = wt->stime + wt->timestep * ceil(wt->duration / wt->timestep);
1120
1121                                 if (wt->event_type == TIMERJOBS)
1122                                         wm_jobs_timer(C, wm, wt);
1123                                 else if (wt->event_type == TIMERAUTOSAVE)
1124                                         wm_autosave_timer(C, wm, wt);
1125                                 else if (win) {
1126                                         wmEvent event;
1127                                         wm_event_init_from_window(win, &event);
1128                                         
1129                                         event.type = wt->event_type;
1130                                         event.val = 0;
1131                                         event.keymodifier = 0;
1132                                         event.custom = EVT_DATA_TIMER;
1133                                         event.customdata = wt;
1134                                         wm_event_add(win, &event);
1135
1136                                         retval = 1;
1137                                 }
1138                         }
1139                 }
1140         }
1141         return retval;
1142 }
1143
1144 void wm_window_process_events(const bContext *C) 
1145 {
1146         int hasevent;
1147
1148         BLI_assert(BLI_thread_is_main());
1149
1150         hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
1151
1152         if (hasevent)
1153                 GHOST_DispatchEvents(g_system);
1154         
1155         hasevent |= wm_window_timer(C);
1156
1157         /* no event, we sleep 5 milliseconds */
1158         if (hasevent == 0)
1159                 PIL_sleep_ms(5);
1160 }
1161
1162 void wm_window_process_events_nosleep(void) 
1163 {
1164         if (GHOST_ProcessEvents(g_system, 0))
1165                 GHOST_DispatchEvents(g_system);
1166 }
1167
1168 /* exported as handle callback to bke blender.c */
1169 void wm_window_testbreak(void)
1170 {
1171         static double ltime = 0;
1172         double curtime = PIL_check_seconds_timer();
1173
1174         BLI_assert(BLI_thread_is_main());
1175
1176         /* only check for breaks every 50 milliseconds
1177          * if we get called more often.
1178          */
1179         if ((curtime - ltime) > 0.05) {
1180                 int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */
1181                 
1182                 if (hasevent)
1183                         GHOST_DispatchEvents(g_system);
1184                 
1185                 ltime = curtime;
1186         }
1187 }
1188
1189 /* **************** init ********************** */
1190
1191 void wm_ghost_init(bContext *C)
1192 {
1193         if (!g_system) {
1194                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
1195                 
1196                 g_system = GHOST_CreateSystem();
1197                 GHOST_AddEventConsumer(g_system, consumer);
1198                 
1199                 if (wm_init_state.native_pixels) {
1200                         GHOST_UseNativePixels();
1201                 }
1202         }
1203 }
1204
1205 void wm_ghost_exit(void)
1206 {
1207         if (g_system)
1208                 GHOST_DisposeSystem(g_system);
1209
1210         g_system = NULL;
1211 }
1212
1213 /* **************** timer ********************** */
1214
1215 /* to (de)activate running timers temporary */
1216 void WM_event_timer_sleep(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer, bool do_sleep)
1217 {
1218         wmTimer *wt;
1219         
1220         for (wt = wm->timers.first; wt; wt = wt->next)
1221                 if (wt == timer)
1222                         break;
1223
1224         if (wt)
1225                 wt->sleep = do_sleep;
1226 }
1227
1228 wmTimer *WM_event_add_timer(wmWindowManager *wm, wmWindow *win, int event_type, double timestep)
1229 {
1230         wmTimer *wt = MEM_callocN(sizeof(wmTimer), "window timer");
1231         
1232         wt->event_type = event_type;
1233         wt->ltime = PIL_check_seconds_timer();
1234         wt->ntime = wt->ltime + timestep;
1235         wt->stime = wt->ltime;
1236         wt->timestep = timestep;
1237         wt->win = win;
1238         
1239         BLI_addtail(&wm->timers, wt);
1240         
1241         return wt;
1242 }
1243
1244 void WM_event_remove_timer(wmWindowManager *wm, wmWindow *UNUSED(win), wmTimer *timer)
1245 {
1246         wmTimer *wt;
1247         
1248         /* extra security check */
1249         for (wt = wm->timers.first; wt; wt = wt->next)
1250                 if (wt == timer)
1251                         break;
1252         if (wt) {
1253                 wmWindow *win;
1254                 
1255                 if (wm->reports.reporttimer == wt)
1256                         wm->reports.reporttimer = NULL;
1257                 
1258                 BLI_remlink(&wm->timers, wt);
1259                 if (wt->customdata)
1260                         MEM_freeN(wt->customdata);
1261                 MEM_freeN(wt);
1262                 
1263                 /* there might be events in queue with this timer as customdata */
1264                 for (win = wm->windows.first; win; win = win->next) {
1265                         wmEvent *event;
1266                         for (event = win->queue.first; event; event = event->next) {
1267                                 if (event->customdata == wt) {
1268                                         event->customdata = NULL;
1269                                         event->type = EVENT_NONE;       /* timer users customdata, dont want NULL == NULL */
1270                                 }
1271                         }
1272                 }
1273         }
1274 }
1275
1276 /* ******************* clipboard **************** */
1277
1278 static char *wm_clipboard_text_get_ex(bool selection, int *r_len,
1279                                       bool firstline)
1280 {
1281         char *p, *p2, *buf, *newbuf;
1282
1283         if (G.background) {
1284                 *r_len = 0;
1285                 return NULL;
1286         }
1287
1288         buf = (char *)GHOST_getClipboard(selection);
1289         if (!buf) {
1290                 *r_len = 0;
1291                 return NULL;
1292         }
1293         
1294         /* always convert from \r\n to \n */
1295         p2 = newbuf = MEM_mallocN(strlen(buf) + 1, __func__);
1296
1297         if (firstline) {
1298                 /* will return an over-alloc'ed value in the case there are newlines */
1299                 for (p = buf; *p; p++) {
1300                         if ((*p != '\n') && (*p != '\r')) {
1301                                 *(p2++) = *p;
1302                         }
1303                         else {
1304                                 break;
1305                         }
1306                 }
1307         }
1308         else {
1309                 for (p = buf; *p; p++) {
1310                         if (*p != '\r') {
1311                                 *(p2++) = *p;
1312                         }
1313                 }
1314         }
1315
1316         *p2 = '\0';
1317
1318         free(buf); /* ghost uses regular malloc */
1319         
1320         *r_len = (p2 - newbuf);
1321
1322         return newbuf;
1323 }
1324
1325 /**
1326  * Return text from the clipboard.
1327  *
1328  * \note Caller needs to check for valid utf8 if this is a requirement.
1329  */
1330 char *WM_clipboard_text_get(bool selection, int *r_len)
1331 {
1332         return wm_clipboard_text_get_ex(selection, r_len, false);
1333 }
1334
1335 /**
1336  * Convenience function for pasting to areas of Blender which don't support newlines.
1337  */
1338 char *WM_clipboard_text_get_firstline(bool selection, int *r_len)
1339 {
1340         return wm_clipboard_text_get_ex(selection, r_len, true);
1341 }
1342
1343 void WM_clipboard_text_set(const char *buf, bool selection)
1344 {
1345         if (!G.background) {
1346 #ifdef _WIN32
1347                 /* do conversion from \n to \r\n on Windows */
1348                 const char *p;
1349                 char *p2, *newbuf;
1350                 int newlen = 0;
1351                 
1352                 for (p = buf; *p; p++) {
1353                         if (*p == '\n')
1354                                 newlen += 2;
1355                         else
1356                                 newlen++;
1357                 }
1358                 
1359                 newbuf = MEM_callocN(newlen + 1, "WM_clipboard_text_set");
1360         
1361                 for (p = buf, p2 = newbuf; *p; p++, p2++) {
1362                         if (*p == '\n') {
1363                                 *(p2++) = '\r'; *p2 = '\n';
1364                         }
1365                         else {
1366                                 *p2 = *p;
1367                         }
1368                 }
1369                 *p2 = '\0';
1370         
1371                 GHOST_putClipboard((GHOST_TInt8 *)newbuf, selection);
1372                 MEM_freeN(newbuf);
1373 #else
1374                 GHOST_putClipboard((GHOST_TInt8 *)buf, selection);
1375 #endif
1376         }
1377 }
1378
1379 /* ******************* progress bar **************** */
1380
1381 void WM_progress_set(wmWindow *win, float progress)
1382 {
1383         GHOST_SetProgressBar(win->ghostwin, progress);
1384 }
1385
1386 void WM_progress_clear(wmWindow *win)
1387 {
1388         GHOST_EndProgressBar(win->ghostwin);
1389 }
1390
1391 /* ************************************ */
1392
1393 void wm_window_get_position(wmWindow *win, int *posx_r, int *posy_r) 
1394 {
1395         *posx_r = win->posx;
1396         *posy_r = win->posy;
1397 }
1398
1399 void wm_window_set_size(wmWindow *win, int width, int height) 
1400 {
1401         GHOST_SetClientSize(win->ghostwin, width, height);
1402 }
1403
1404 void wm_window_lower(wmWindow *win) 
1405 {
1406         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderBottom);
1407 }
1408
1409 void wm_window_raise(wmWindow *win) 
1410 {
1411         GHOST_SetWindowOrder(win->ghostwin, GHOST_kWindowOrderTop);
1412 }
1413
1414 void wm_window_swap_buffers(wmWindow *win)
1415 {
1416         
1417 #ifdef WIN32
1418         glDisable(GL_SCISSOR_TEST);
1419         GHOST_SwapWindowBuffers(win->ghostwin);
1420         glEnable(GL_SCISSOR_TEST);
1421 #else
1422         GHOST_SwapWindowBuffers(win->ghostwin);
1423 #endif
1424 }
1425
1426 void wm_window_set_swap_interval (wmWindow *win, int interval)
1427 {
1428         GHOST_SetSwapInterval(win->ghostwin, interval);
1429 }
1430
1431 bool wm_window_get_swap_interval(wmWindow *win, int *intervalOut)
1432 {
1433         return GHOST_GetSwapInterval(win->ghostwin, intervalOut);
1434 }
1435
1436
1437 /* ******************* exported api ***************** */
1438
1439
1440 /* called whem no ghost system was initialized */
1441 void WM_init_state_size_set(int stax, int stay, int sizx, int sizy)
1442 {
1443         wm_init_state.start_x = stax; /* left hand pos */
1444         wm_init_state.start_y = stay; /* bottom pos */
1445         wm_init_state.size_x = sizx < 640 ? 640 : sizx;
1446         wm_init_state.size_y = sizy < 480 ? 480 : sizy;
1447         wm_init_state.override_flag |= WIN_OVERRIDE_GEOM;
1448 }
1449
1450 /* for borderless and border windows set from command-line */
1451 void WM_init_state_fullscreen_set(void)
1452 {
1453         wm_init_state.windowstate = GHOST_kWindowStateFullScreen;
1454         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1455 }
1456
1457 void WM_init_state_normal_set(void)
1458 {
1459         wm_init_state.windowstate = GHOST_kWindowStateNormal;
1460         wm_init_state.override_flag |= WIN_OVERRIDE_WINSTATE;
1461 }
1462
1463 void WM_init_native_pixels(bool do_it)
1464 {
1465         wm_init_state.native_pixels = do_it;
1466 }
1467
1468 /* This function requires access to the GHOST_SystemHandle (g_system) */
1469 void WM_cursor_warp(wmWindow *win, int x, int y)
1470 {
1471         if (win && win->ghostwin) {
1472                 float f = GHOST_GetNativePixelSize(win->ghostwin);
1473                 int oldx = x, oldy = y;
1474
1475                 x = x / f;
1476                 y = y / f;
1477                 y = win->sizey - y - 1;
1478
1479                 GHOST_ClientToScreen(win->ghostwin, x, y, &x, &y);
1480                 GHOST_SetCursorPosition(g_system, x, y);
1481
1482                 win->eventstate->prevx = oldx;
1483                 win->eventstate->prevy = oldy;
1484
1485                 win->eventstate->x = oldx;
1486                 win->eventstate->y = oldy;
1487         }
1488 }
1489
1490 /**
1491  * Get the cursor pressure, in most cases you'll want to use wmTabletData from the event
1492  */
1493 float WM_cursor_pressure(const struct wmWindow *win)
1494 {
1495         const GHOST_TabletData *td = GHOST_GetTabletData(win->ghostwin);
1496         /* if there's tablet data from an active tablet device then add it */
1497         if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
1498                 return td->Pressure;
1499         }
1500         else {
1501                 return -1.0f;
1502         }
1503 }
1504
1505 /* support for native pixel size */
1506 /* mac retina opens window in size X, but it has up to 2 x more pixels */
1507 int WM_window_pixels_x(wmWindow *win)
1508 {
1509         float f = GHOST_GetNativePixelSize(win->ghostwin);
1510         
1511         return (int)(f * (float)win->sizex);
1512 }
1513
1514 int WM_window_pixels_y(wmWindow *win)
1515 {
1516         float f = GHOST_GetNativePixelSize(win->ghostwin);
1517         
1518         return (int)(f * (float)win->sizey);
1519         
1520 }
1521
1522 bool WM_window_is_fullscreen(wmWindow *win)
1523 {
1524         return win->windowstate == GHOST_kWindowStateFullScreen;
1525 }
1526