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