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