Pass EvaluationContext argument everywhere
[blender.git] / source / blender / editors / space_view3d / view3d_fly.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  * Contributor(s): Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/editors/space_view3d/view3d_fly.c
24  *  \ingroup spview3d
25  */
26
27 /* defines VIEW3D_OT_fly modal operator */
28
29 #ifdef WITH_INPUT_NDOF
30 //#  define NDOF_FLY_DEBUG
31 //#  define NDOF_FLY_DRAW_TOOMUCH  /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */
32 #endif /* WITH_INPUT_NDOF */
33
34 #include "DNA_object_types.h"
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40
41 #include "BKE_context.h"
42 #include "BKE_report.h"
43
44 #include "BLT_translation.h"
45
46 #include "BIF_gl.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "ED_screen.h"
52 #include "ED_space_api.h"
53
54 #include "PIL_time.h" /* smoothview */
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 #include "GPU_immediate.h"
60
61 #include "view3d_intern.h"  /* own include */
62
63 /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
64 enum {
65         FLY_MODAL_CANCEL = 1,
66         FLY_MODAL_CONFIRM,
67         FLY_MODAL_ACCELERATE,
68         FLY_MODAL_DECELERATE,
69         FLY_MODAL_PAN_ENABLE,
70         FLY_MODAL_PAN_DISABLE,
71         FLY_MODAL_DIR_FORWARD,
72         FLY_MODAL_DIR_BACKWARD,
73         FLY_MODAL_DIR_LEFT,
74         FLY_MODAL_DIR_RIGHT,
75         FLY_MODAL_DIR_UP,
76         FLY_MODAL_DIR_DOWN,
77         FLY_MODAL_AXIS_LOCK_X,
78         FLY_MODAL_AXIS_LOCK_Z,
79         FLY_MODAL_PRECISION_ENABLE,
80         FLY_MODAL_PRECISION_DISABLE,
81         FLY_MODAL_FREELOOK_ENABLE,
82         FLY_MODAL_FREELOOK_DISABLE,
83         FLY_MODAL_SPEED,        /* mousepan typically */
84 };
85
86 /* relative view axis locking - xlock, zlock */
87 typedef enum eFlyPanState {
88         /* disabled */
89         FLY_AXISLOCK_STATE_OFF    = 0,
90
91         /* enabled but not checking because mouse hasn't moved outside the margin since locking was checked an not needed
92          * when the mouse moves, locking is set to 2 so checks are done. */
93         FLY_AXISLOCK_STATE_IDLE   = 1,
94
95         /* mouse moved and checking needed, if no view altering is done its changed back to #FLY_AXISLOCK_STATE_IDLE */
96         FLY_AXISLOCK_STATE_ACTIVE = 2
97 } eFlyPanState;
98
99 /* called in transform_ops.c, on each regeneration of keymaps  */
100 void fly_modal_keymap(wmKeyConfig *keyconf)
101 {
102         static EnumPropertyItem modal_items[] = {
103                 {FLY_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
104                 {FLY_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
105                 {FLY_MODAL_ACCELERATE, "ACCELERATE", 0, "Accelerate", ""},
106                 {FLY_MODAL_DECELERATE, "DECELERATE", 0, "Decelerate", ""},
107
108                 {FLY_MODAL_PAN_ENABLE, "PAN_ENABLE", 0, "Pan Enable", ""},
109                 {FLY_MODAL_PAN_DISABLE, "PAN_DISABLE", 0, "Pan Disable", ""},
110
111                 {FLY_MODAL_DIR_FORWARD, "FORWARD", 0, "Fly Forward", ""},
112                 {FLY_MODAL_DIR_BACKWARD, "BACKWARD", 0, "Fly Backward", ""},
113                 {FLY_MODAL_DIR_LEFT, "LEFT", 0, "Fly Left", ""},
114                 {FLY_MODAL_DIR_RIGHT, "RIGHT", 0, "Fly Right", ""},
115                 {FLY_MODAL_DIR_UP, "UP", 0, "Fly Up", ""},
116                 {FLY_MODAL_DIR_DOWN, "DOWN", 0, "Fly Down", ""},
117
118                 {FLY_MODAL_AXIS_LOCK_X, "AXIS_LOCK_X", 0, "X Axis Correction", "X axis correction (toggle)"},
119                 {FLY_MODAL_AXIS_LOCK_Z, "AXIS_LOCK_Z", 0, "X Axis Correction", "Z axis correction (toggle)"},
120
121                 {FLY_MODAL_PRECISION_ENABLE, "PRECISION_ENABLE", 0, "Precision Enable", ""},
122                 {FLY_MODAL_PRECISION_DISABLE, "PRECISION_DISABLE", 0, "Precision Disable", ""},
123
124                 {FLY_MODAL_FREELOOK_ENABLE, "FREELOOK_ENABLE", 0, "Rotation Enable", ""},
125                 {FLY_MODAL_FREELOOK_DISABLE, "FREELOOK_DISABLE", 0, "Rotation Disable", ""},
126
127                 {0, NULL, 0, NULL, NULL}};
128
129         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "View3D Fly Modal");
130
131         /* this function is called for each spacetype, only needs to add map once */
132         if (keymap && keymap->modal_items)
133                 return;
134
135         keymap = WM_modalkeymap_add(keyconf, "View3D Fly Modal", modal_items);
136
137         /* items for modal map */
138         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CANCEL);
139         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CANCEL);
140
141         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_ANY, KM_ANY, 0, FLY_MODAL_CONFIRM);
142         WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
143         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
144         WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, FLY_MODAL_CONFIRM);
145
146         WM_modalkeymap_add_item(keymap, PADPLUSKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_ACCELERATE);
147         WM_modalkeymap_add_item(keymap, PADMINUS, KM_PRESS, KM_ANY, 0, FLY_MODAL_DECELERATE);
148         WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_ACCELERATE);
149         WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_DECELERATE);
150
151         WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, FLY_MODAL_SPEED);
152         
153         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_PRESS, KM_ANY, 0, FLY_MODAL_PAN_ENABLE);
154         /* XXX - Bug in the event system, middle mouse release doesnt work */
155         WM_modalkeymap_add_item(keymap, MIDDLEMOUSE, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PAN_DISABLE);
156
157         /* WASD */
158         WM_modalkeymap_add_item(keymap, WKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
159         WM_modalkeymap_add_item(keymap, SKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
160         WM_modalkeymap_add_item(keymap, AKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
161         WM_modalkeymap_add_item(keymap, DKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
162         WM_modalkeymap_add_item(keymap, EKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
163         WM_modalkeymap_add_item(keymap, QKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
164
165         /* for legacy reasons, leave R/F working */
166         WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_UP);
167         WM_modalkeymap_add_item(keymap, FKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_DOWN);
168
169         WM_modalkeymap_add_item(keymap, UPARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_FORWARD);
170         WM_modalkeymap_add_item(keymap, DOWNARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_BACKWARD);
171         WM_modalkeymap_add_item(keymap, LEFTARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_LEFT);
172         WM_modalkeymap_add_item(keymap, RIGHTARROWKEY, KM_PRESS, 0, 0, FLY_MODAL_DIR_RIGHT);
173
174         WM_modalkeymap_add_item(keymap, XKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_X);
175         WM_modalkeymap_add_item(keymap, ZKEY, KM_PRESS, 0, 0, FLY_MODAL_AXIS_LOCK_Z);
176
177         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
178         WM_modalkeymap_add_item(keymap, LEFTALTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
179
180         /* for legacy reasons, leave shift working */
181         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_PRECISION_ENABLE);
182         WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_PRECISION_DISABLE);
183
184         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, FLY_MODAL_FREELOOK_ENABLE);
185         WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, FLY_MODAL_FREELOOK_DISABLE);
186
187         /* assign map to operators */
188         WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
189 }
190
191 typedef struct FlyInfo {
192         /* context stuff */
193         RegionView3D *rv3d;
194         View3D *v3d;
195         ARegion *ar;
196         Scene *scene;
197
198         wmTimer *timer; /* needed for redraws */
199
200         short state;
201         bool redraw;
202         bool use_precision;
203         /* if the user presses shift they can look about
204          * without moving the direction there looking */
205         bool use_freelook;
206
207         int mval[2]; /* latest 2D mouse values */
208         int center_mval[2]; /* center mouse values */
209         float width, height; /* camera viewport dimensions */
210
211 #ifdef WITH_INPUT_NDOF
212         wmNDOFMotionData *ndof;  /* latest 3D mouse values */
213 #endif
214
215         /* fly state state */
216         float speed; /* the speed the view is moving per redraw */
217         short axis; /* Axis index to move along by default Z to move along the view */
218         bool pan_view; /* when true, pan the view instead of rotating */
219
220         eFlyPanState xlock, zlock;
221         float xlock_momentum, zlock_momentum; /* nicer dynamics */
222         float grid; /* world scale 1.0 default */
223
224         /* compare between last state */
225         double time_lastwheel; /* used to accelerate when using the mousewheel a lot */
226         double time_lastdraw; /* time between draws */
227
228         void *draw_handle_pixel;
229
230         /* use for some lag */
231         float dvec_prev[3]; /* old for some lag */
232
233         struct View3DCameraControl *v3d_camera_control;
234
235 } FlyInfo;
236
237 static void drawFlyPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
238 {
239         FlyInfo *fly = arg;
240         rctf viewborder;
241         int xoff, yoff;
242         float x1, x2, y1, y2;
243
244         if (fly->scene->camera) {
245                 ED_view3d_calc_camera_border(fly->scene, fly->ar, fly->v3d, fly->rv3d, &viewborder, false);
246                 xoff = viewborder.xmin;
247                 yoff = viewborder.ymin;
248         }
249         else {
250                 xoff = 0;
251                 yoff = 0;
252         }
253
254         /* draws 4 edge brackets that frame the safe area where the
255          * mouse can move during fly mode without spinning the view */
256
257         x1 = xoff + 0.45f * fly->width;
258         y1 = yoff + 0.45f * fly->height;
259         x2 = xoff + 0.55f * fly->width;
260         y2 = yoff + 0.55f * fly->height;
261
262         Gwn_VertFormat *format = immVertexFormat();
263         unsigned int pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
264
265         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
266
267         immUniformThemeColor(TH_VIEW_OVERLAY);
268
269         immBegin(GWN_PRIM_LINES, 16);
270
271         /* bottom left */
272         immVertex2f(pos, x1, y1);
273         immVertex2f(pos, x1, y1 + 5);
274
275         immVertex2f(pos, x1, y1);
276         immVertex2f(pos, x1 + 5, y1);
277
278         /* top right */
279         immVertex2f(pos, x2, y2);
280         immVertex2f(pos, x2, y2 - 5);
281
282         immVertex2f(pos, x2, y2);
283         immVertex2f(pos, x2 - 5, y2);
284
285         /* top left */
286         immVertex2f(pos, x1, y2);
287         immVertex2f(pos, x1, y2 - 5);
288
289         immVertex2f(pos, x1, y2);
290         immVertex2f(pos, x1 + 5, y2);
291
292         /* bottom right */
293         immVertex2f(pos, x2, y1);
294         immVertex2f(pos, x2, y1 + 5);
295
296         immVertex2f(pos, x2, y1);
297         immVertex2f(pos, x2 - 5, y1);
298
299         immEnd();
300         immUnbindProgram();
301 }
302
303 static void fly_update_header(bContext *C, wmOperator *op, FlyInfo *fly)
304 {
305         char header[UI_MAX_DRAW_STR];
306         char buf[UI_MAX_DRAW_STR];
307
308         char *p = buf;
309         int available_len = sizeof(buf);
310
311 #define WM_MODALKEY(_id) \
312         WM_modalkeymap_operator_items_to_string_buf(op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
313
314         BLI_snprintf(header, sizeof(header), IFACE_("%s: confirm, %s: cancel, "
315                                                     "%s: pan enable, "
316                                                     "%s|%s|%s|%s|%s|%s: direction, "
317                                                     "%s: slow, %s: free look, "
318                                                     "%s: Upright x axis (%s), "
319                                                     "%s: Upright z axis (%s), "
320                                                     "%s: increase  speed, %s: decrease speed"),
321                      WM_MODALKEY(FLY_MODAL_CONFIRM), WM_MODALKEY(FLY_MODAL_CANCEL),
322                      WM_MODALKEY(FLY_MODAL_PAN_ENABLE),
323                      WM_MODALKEY(FLY_MODAL_DIR_FORWARD), WM_MODALKEY(FLY_MODAL_DIR_LEFT),
324                      WM_MODALKEY(FLY_MODAL_DIR_BACKWARD), WM_MODALKEY(FLY_MODAL_DIR_RIGHT),
325                      WM_MODALKEY(FLY_MODAL_DIR_UP), WM_MODALKEY(FLY_MODAL_DIR_DOWN),
326                      WM_MODALKEY(FLY_MODAL_PRECISION_ENABLE), WM_MODALKEY(FLY_MODAL_FREELOOK_ENABLE),
327                      WM_MODALKEY(FLY_MODAL_AXIS_LOCK_X), WM_bool_as_string(fly->xlock != FLY_AXISLOCK_STATE_OFF),
328                      WM_MODALKEY(FLY_MODAL_AXIS_LOCK_Z), WM_bool_as_string(fly->zlock != FLY_AXISLOCK_STATE_OFF),
329                      WM_MODALKEY(FLY_MODAL_ACCELERATE), WM_MODALKEY(FLY_MODAL_DECELERATE));
330
331 #undef WM_MODALKEY
332
333         ED_area_headerprint(CTX_wm_area(C), header);
334 }
335
336 /* FlyInfo->state */
337 enum {
338         FLY_RUNNING     = 0,
339         FLY_CANCEL      = 1,
340         FLY_CONFIRM     = 2,
341 };
342
343 static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent *event)
344 {
345         wmWindow *win = CTX_wm_window(C);
346         rctf viewborder;
347
348         float upvec[3]; /* tmp */
349         float mat[3][3];
350
351         fly->rv3d = CTX_wm_region_view3d(C);
352         fly->v3d = CTX_wm_view3d(C);
353         fly->ar = CTX_wm_region(C);
354         fly->scene = CTX_data_scene(C);
355
356 #ifdef NDOF_FLY_DEBUG
357         puts("\n-- fly begin --");
358 #endif
359
360         /* sanity check: for rare but possible case (if lib-linking the camera fails) */
361         if ((fly->rv3d->persp == RV3D_CAMOB) && (fly->v3d->camera == NULL)) {
362                 fly->rv3d->persp = RV3D_PERSP;
363         }
364
365         if (fly->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED_DATABLOCK(fly->v3d->camera)) {
366                 BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
367                 return false;
368         }
369
370         if (ED_view3d_offset_lock_check(fly->v3d, fly->rv3d)) {
371                 BKE_report(op->reports, RPT_ERROR, "Cannot fly when the view offset is locked");
372                 return false;
373         }
374
375         if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->constraints.first) {
376                 BKE_report(op->reports, RPT_ERROR, "Cannot fly an object with constraints");
377                 return false;
378         }
379
380         fly->state = FLY_RUNNING;
381         fly->speed = 0.0f;
382         fly->axis = 2;
383         fly->pan_view = false;
384         fly->xlock = FLY_AXISLOCK_STATE_OFF;
385         fly->zlock = FLY_AXISLOCK_STATE_OFF;
386         fly->xlock_momentum = 0.0f;
387         fly->zlock_momentum = 0.0f;
388         fly->grid = 1.0f;
389         fly->use_precision = false;
390         fly->use_freelook = false;
391
392 #ifdef NDOF_FLY_DRAW_TOOMUCH
393         fly->redraw = 1;
394 #endif
395         zero_v3(fly->dvec_prev);
396
397         fly->timer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, 0.01f);
398
399         copy_v2_v2_int(fly->mval, event->mval);
400
401 #ifdef WITH_INPUT_NDOF
402         fly->ndof = NULL;
403 #endif
404
405         fly->time_lastdraw = fly->time_lastwheel = PIL_check_seconds_timer();
406
407         fly->draw_handle_pixel = ED_region_draw_cb_activate(fly->ar->type, drawFlyPixel, fly, REGION_DRAW_POST_PIXEL);
408
409         fly->rv3d->rflag |= RV3D_NAVIGATING;
410
411         /* detect whether to start with Z locking */
412         copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
413         copy_m3_m4(mat, fly->rv3d->viewinv);
414         mul_m3_v3(mat, upvec);
415         if (fabsf(upvec[2]) < 0.1f) {
416                 fly->zlock = FLY_AXISLOCK_STATE_IDLE;
417         }
418
419         fly->v3d_camera_control = ED_view3d_cameracontrol_acquire(
420                 C, fly->scene, fly->v3d, fly->rv3d,
421                 (U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0);
422
423         /* calculate center */
424         if (fly->scene->camera) {
425                 ED_view3d_calc_camera_border(fly->scene, fly->ar, fly->v3d, fly->rv3d, &viewborder, false);
426
427                 fly->width = BLI_rctf_size_x(&viewborder);
428                 fly->height = BLI_rctf_size_y(&viewborder);
429
430                 fly->center_mval[0] = viewborder.xmin + fly->width / 2;
431                 fly->center_mval[1] = viewborder.ymin + fly->height / 2;
432         }
433         else {
434                 fly->width = fly->ar->winx;
435                 fly->height = fly->ar->winy;
436
437                 fly->center_mval[0] = fly->width / 2;
438                 fly->center_mval[1] = fly->height / 2;
439         }
440
441         /* center the mouse, probably the UI mafia are against this but without its quite annoying */
442         WM_cursor_warp(win, fly->ar->winrct.xmin + fly->center_mval[0], fly->ar->winrct.ymin + fly->center_mval[1]);
443
444         fly_update_header(C, op, fly);
445         return 1;
446 }
447
448 static int flyEnd(bContext *C, FlyInfo *fly)
449 {
450         wmWindow *win;
451         RegionView3D *rv3d;
452
453         if (fly->state == FLY_RUNNING)
454                 return OPERATOR_RUNNING_MODAL;
455
456 #ifdef NDOF_FLY_DEBUG
457         puts("\n-- fly end --");
458 #endif
459
460         win = CTX_wm_window(C);
461         rv3d = fly->rv3d;
462
463         WM_event_remove_timer(CTX_wm_manager(C), win, fly->timer);
464
465         ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
466
467         ED_view3d_cameracontrol_release(fly->v3d_camera_control, fly->state == FLY_CANCEL);
468
469         rv3d->rflag &= ~RV3D_NAVIGATING;
470
471 #ifdef WITH_INPUT_NDOF
472         if (fly->ndof)
473                 MEM_freeN(fly->ndof);
474 #endif
475
476         if (fly->state == FLY_CONFIRM) {
477                 MEM_freeN(fly);
478                 return OPERATOR_FINISHED;
479         }
480
481         MEM_freeN(fly);
482         return OPERATOR_CANCELLED;
483 }
484
485 static void flyEvent(bContext *C, wmOperator *op, FlyInfo *fly, const wmEvent *event)
486 {
487         if (event->type == TIMER && event->customdata == fly->timer) {
488                 fly->redraw = 1;
489         }
490         else if (event->type == MOUSEMOVE) {
491                 copy_v2_v2_int(fly->mval, event->mval);
492         }
493 #ifdef WITH_INPUT_NDOF
494         else if (event->type == NDOF_MOTION) {
495                 /* do these automagically get delivered? yes. */
496                 // puts("ndof motion detected in fly mode!");
497                 // static const char *tag_name = "3D mouse position";
498
499                 const wmNDOFMotionData *incoming_ndof = event->customdata;
500                 switch (incoming_ndof->progress) {
501                         case P_STARTING:
502                                 /* start keeping track of 3D mouse position */
503 #  ifdef NDOF_FLY_DEBUG
504                                 puts("start keeping track of 3D mouse position");
505 #  endif
506                                 /* fall-through */
507                         case P_IN_PROGRESS:
508                                 /* update 3D mouse position */
509 #  ifdef NDOF_FLY_DEBUG
510                                 putchar('.'); fflush(stdout);
511 #  endif
512                                 if (fly->ndof == NULL) {
513                                         // fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
514                                         fly->ndof = MEM_dupallocN(incoming_ndof);
515                                         // fly->ndof = malloc(sizeof(wmNDOFMotionData));
516                                 }
517                                 else {
518                                         memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
519                                 }
520                                 break;
521                         case P_FINISHING:
522                                 /* stop keeping track of 3D mouse position */
523 #  ifdef NDOF_FLY_DEBUG
524                                 puts("stop keeping track of 3D mouse position");
525 #  endif
526                                 if (fly->ndof) {
527                                         MEM_freeN(fly->ndof);
528                                         // free(fly->ndof);
529                                         fly->ndof = NULL;
530                                 }
531                                 /* update the time else the view will jump when 2D mouse/timer resume */
532                                 fly->time_lastdraw = PIL_check_seconds_timer();
533                                 break;
534                         default:
535                                 break; /* should always be one of the above 3 */
536                 }
537         }
538 #endif /* WITH_INPUT_NDOF */
539         /* handle modal keymap first */
540         else if (event->type == EVT_MODAL_MAP) {
541                 switch (event->val) {
542                         case FLY_MODAL_CANCEL:
543                                 fly->state = FLY_CANCEL;
544                                 break;
545                         case FLY_MODAL_CONFIRM:
546                                 fly->state = FLY_CONFIRM;
547                                 break;
548                                 
549                         /* speed adjusting with mousepan (trackpad) */
550                         case FLY_MODAL_SPEED:
551                         {
552                                 float fac = 0.02f * (event->prevy - event->y);
553                                 
554                                 /* allowing to brake immediate */
555                                 if (fac > 0.0f && fly->speed < 0.0f)
556                                         fly->speed = 0.0f;
557                                 else if (fac < 0.0f && fly->speed > 0.0f)
558                                         fly->speed = 0.0f;
559                                 else
560                                         fly->speed += fly->grid * fac;
561                                 
562                                 break;
563                         }
564                         case FLY_MODAL_ACCELERATE:
565                         {
566                                 double time_currwheel;
567                                 float time_wheel;
568
569                                 /* not quite correct but avoids confusion WASD/arrow keys 'locking up' */
570                                 if (fly->axis == -1) {
571                                         fly->axis = 2;
572                                         fly->speed = fabsf(fly->speed);
573                                 }
574
575                                 time_currwheel = PIL_check_seconds_timer();
576                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
577                                 fly->time_lastwheel = time_currwheel;
578                                 /* Mouse wheel delays range from (0.5 == slow) to (0.01 == fast) */
579                                 time_wheel = 1.0f + (10.0f - (20.0f * min_ff(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
580
581                                 if (fly->speed < 0.0f) {
582                                         fly->speed = 0.0f;
583                                 }
584                                 else {
585                                         fly->speed += fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
586                                 }
587                                 break;
588                         }
589                         case FLY_MODAL_DECELERATE:
590                         {
591                                 double time_currwheel;
592                                 float time_wheel;
593
594                                 /* not quite correct but avoids confusion WASD/arrow keys 'locking up' */
595                                 if (fly->axis == -1) {
596                                         fly->axis = 2;
597                                         fly->speed = -fabsf(fly->speed);
598                                 }
599
600                                 time_currwheel = PIL_check_seconds_timer();
601                                 time_wheel = (float)(time_currwheel - fly->time_lastwheel);
602                                 fly->time_lastwheel = time_currwheel;
603                                 time_wheel = 1.0f + (10.0f - (20.0f * min_ff(time_wheel, 0.5f))); /* 0-0.5 -> 0-5.0 */
604
605                                 if (fly->speed > 0.0f) {
606                                         fly->speed = 0;
607                                 }
608                                 else {
609                                         fly->speed -= fly->grid * time_wheel * (fly->use_precision ? 0.1f : 1.0f);
610                                 }
611                                 break;
612                         }
613                         case FLY_MODAL_PAN_ENABLE:
614                                 fly->pan_view = true;
615                                 break;
616                         case FLY_MODAL_PAN_DISABLE:
617                                 fly->pan_view = false;
618                                 break;
619
620                         /* implement WASD keys,
621                          * comments only for 'forward '*/
622                         case FLY_MODAL_DIR_FORWARD:
623                                 if (fly->axis == 2 && fly->speed < 0.0f) { /* reverse direction stops, tap again to continue */
624                                         fly->axis = -1;
625                                 }
626                                 else {
627                                         /* flip speed rather than stopping, game like motion,
628                                          * else increase like mousewheel if were already moving in that direction */
629                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
630                                         else if (fly->axis == 2) fly->speed += fly->grid;
631                                         fly->axis = 2;
632                                 }
633                                 break;
634                         case FLY_MODAL_DIR_BACKWARD:
635                                 if (fly->axis == 2 && fly->speed > 0.0f) {
636                                         fly->axis = -1;
637                                 }
638                                 else {
639                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
640                                         else if (fly->axis == 2) fly->speed -= fly->grid;
641
642                                         fly->axis = 2;
643                                 }
644                                 break;
645                         case FLY_MODAL_DIR_LEFT:
646                                 if (fly->axis == 0 && fly->speed < 0.0f) {
647                                         fly->axis = -1;
648                                 }
649                                 else {
650                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
651                                         else if (fly->axis == 0) fly->speed += fly->grid;
652
653                                         fly->axis = 0;
654                                 }
655                                 break;
656                         case FLY_MODAL_DIR_RIGHT:
657                                 if (fly->axis == 0 && fly->speed > 0.0f) {
658                                         fly->axis = -1;
659                                 }
660                                 else {
661                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
662                                         else if (fly->axis == 0) fly->speed -= fly->grid;
663
664                                         fly->axis = 0;
665                                 }
666                                 break;
667                         case FLY_MODAL_DIR_DOWN:
668                                 if (fly->axis == 1 && fly->speed < 0.0f) {
669                                         fly->axis = -1;
670                                 }
671                                 else {
672                                         if (fly->speed < 0.0f)   fly->speed = -fly->speed;
673                                         else if (fly->axis == 1) fly->speed += fly->grid;
674                                         fly->axis = 1;
675                                 }
676                                 break;
677                         case FLY_MODAL_DIR_UP:
678                                 if (fly->axis == 1 && fly->speed > 0.0f) {
679                                         fly->axis = -1;
680                                 }
681                                 else {
682                                         if (fly->speed > 0.0f)   fly->speed = -fly->speed;
683                                         else if (fly->axis == 1) fly->speed -= fly->grid;
684                                         fly->axis = 1;
685                                 }
686                                 break;
687
688                         case FLY_MODAL_AXIS_LOCK_X:
689                                 if (fly->xlock != FLY_AXISLOCK_STATE_OFF)
690                                         fly->xlock = FLY_AXISLOCK_STATE_OFF;
691                                 else {
692                                         fly->xlock = FLY_AXISLOCK_STATE_ACTIVE;
693                                         fly->xlock_momentum = 0.0;
694                                 }
695                                 fly_update_header(C, op, fly);
696                                 break;
697                         case FLY_MODAL_AXIS_LOCK_Z:
698                                 if (fly->zlock != FLY_AXISLOCK_STATE_OFF)
699                                         fly->zlock = FLY_AXISLOCK_STATE_OFF;
700                                 else {
701                                         fly->zlock = FLY_AXISLOCK_STATE_ACTIVE;
702                                         fly->zlock_momentum = 0.0;
703                                 }
704                                 fly_update_header(C, op, fly);
705                                 break;
706
707                         case FLY_MODAL_PRECISION_ENABLE:
708                                 fly->use_precision = true;
709                                 break;
710                         case FLY_MODAL_PRECISION_DISABLE:
711                                 fly->use_precision = false;
712                                 break;
713
714                         case FLY_MODAL_FREELOOK_ENABLE:
715                                 fly->use_freelook = true;
716                                 break;
717                         case FLY_MODAL_FREELOOK_DISABLE:
718                                 fly->use_freelook = false;
719                                 break;
720                 }
721         }
722 }
723
724 static void flyMoveCamera(bContext *C, FlyInfo *fly,
725                           const bool do_rotate, const bool do_translate)
726 {
727         ED_view3d_cameracontrol_update(fly->v3d_camera_control, true, C, do_rotate, do_translate);
728 }
729
730 static int flyApply(bContext *C, FlyInfo *fly)
731 {
732 #define FLY_ROTATE_FAC 10.0f /* more is faster */
733 #define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
734 #define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
735 #define FLY_SMOOTH_FAC 20.0f  /* higher value less lag */
736
737         /* fly mode - Shift+F
738          * a fly loop where the user can move move the view as if they are flying
739          */
740         RegionView3D *rv3d = fly->rv3d;
741
742         float mat[3][3]; /* 3x3 copy of the view matrix so we can move along the view axis */
743         float dvec[3] = {0, 0, 0}; /* this is the direction thast added to the view offset per redraw */
744
745         /* Camera Uprighting variables */
746         float moffset[2]; /* mouse offset from the views center */
747         float tmp_quat[4]; /* used for rotating the view */
748
749         int xmargin, ymargin; /* x and y margin are define the safe area where the mouses movement wont rotate the view */
750
751 #ifdef NDOF_FLY_DEBUG
752         {
753                 static unsigned int iteration = 1;
754                 printf("fly timer %d\n", iteration++);
755         }
756 #endif
757
758         xmargin = fly->width / 20.0f;
759         ymargin = fly->height / 20.0f;
760
761         {
762
763                 /* mouse offset from the center */
764                 moffset[0] = fly->mval[0] - fly->center_mval[0];
765                 moffset[1] = fly->mval[1] - fly->center_mval[1];
766
767                 /* enforce a view margin */
768                 if      (moffset[0] >  xmargin) moffset[0] -= xmargin;
769                 else if (moffset[0] < -xmargin) moffset[0] += xmargin;
770                 else                            moffset[0] =  0;
771
772                 if      (moffset[1] >  ymargin) moffset[1] -= ymargin;
773                 else if (moffset[1] < -ymargin) moffset[1] += ymargin;
774                 else                            moffset[1] =  0;
775
776
777                 /* scale the mouse movement by this value - scales mouse movement to the view size
778                  * moffset[0] / (ar->winx-xmargin * 2) - window size minus margin (same for y)
779                  *
780                  * the mouse moves isn't linear */
781
782                 if (moffset[0]) {
783                         moffset[0] /= fly->width - (xmargin * 2);
784                         moffset[0] *= fabsf(moffset[0]);
785                 }
786
787                 if (moffset[1]) {
788                         moffset[1] /= fly->height - (ymargin * 2);
789                         moffset[1] *= fabsf(moffset[1]);
790                 }
791
792                 /* Should we redraw? */
793                 if ((fly->speed != 0.0f) ||
794                     moffset[0] || moffset[1] ||
795                     (fly->zlock != FLY_AXISLOCK_STATE_OFF) ||
796                     (fly->xlock != FLY_AXISLOCK_STATE_OFF) ||
797                     dvec[0] || dvec[1] || dvec[2])
798                 {
799                         float dvec_tmp[3];
800
801                         /* time how fast it takes for us to redraw,
802                          * this is so simple scenes don't fly too fast */
803                         double time_current;
804                         float time_redraw;
805                         float time_redraw_clamped;
806 #ifdef NDOF_FLY_DRAW_TOOMUCH
807                         fly->redraw = 1;
808 #endif
809                         time_current = PIL_check_seconds_timer();
810                         time_redraw = (float)(time_current - fly->time_lastdraw);
811                         time_redraw_clamped = min_ff(0.05f, time_redraw); /* clamp redraw time to avoid jitter in roll correction */
812                         fly->time_lastdraw = time_current;
813
814                         /* Scale the time to use shift to scale the speed down- just like
815                          * shift slows many other areas of blender down */
816                         if (fly->use_precision)
817                                 fly->speed = fly->speed * (1.0f - time_redraw_clamped);
818
819                         copy_m3_m4(mat, rv3d->viewinv);
820
821                         if (fly->pan_view == true) {
822                                 /* pan only */
823                                 copy_v3_fl3(dvec_tmp, -moffset[0], -moffset[1], 0.0f);
824
825                                 if (fly->use_precision) {
826                                         dvec_tmp[0] *= 0.1f;
827                                         dvec_tmp[1] *= 0.1f;
828                                 }
829
830                                 mul_m3_v3(mat, dvec_tmp);
831                                 mul_v3_fl(dvec_tmp, time_redraw * 200.0f * fly->grid);
832                         }
833                         else {
834                                 float roll; /* similar to the angle between the camera's up and the Z-up,
835                                              * but its very rough so just roll */
836
837                                 /* rotate about the X axis- look up/down */
838                                 if (moffset[1]) {
839                                         float upvec[3];
840                                         copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
841                                         mul_m3_v3(mat, upvec);
842                                         /* Rotate about the relative up vec */
843                                         axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -FLY_ROTATE_FAC);
844                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
845
846                                         if (fly->xlock != FLY_AXISLOCK_STATE_OFF)
847                                                 fly->xlock = FLY_AXISLOCK_STATE_ACTIVE;  /* check for rotation */
848                                         if (fly->zlock != FLY_AXISLOCK_STATE_OFF)
849                                                 fly->zlock = FLY_AXISLOCK_STATE_ACTIVE;
850                                         fly->xlock_momentum = 0.0f;
851                                 }
852
853                                 /* rotate about the Y axis- look left/right */
854                                 if (moffset[0]) {
855                                         float upvec[3];
856                                         /* if we're upside down invert the moffset */
857                                         copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
858                                         mul_m3_v3(mat, upvec);
859
860                                         if (upvec[2] < 0.0f)
861                                                 moffset[0] = -moffset[0];
862
863                                         /* make the lock vectors */
864                                         if (fly->zlock) {
865                                                 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
866                                         }
867                                         else {
868                                                 copy_v3_fl3(upvec, 0.0f, 1.0f, 0.0f);
869                                                 mul_m3_v3(mat, upvec);
870                                         }
871
872                                         /* Rotate about the relative up vec */
873                                         axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * FLY_ROTATE_FAC);
874                                         mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
875
876                                         if (fly->xlock != FLY_AXISLOCK_STATE_OFF)
877                                                 fly->xlock = FLY_AXISLOCK_STATE_ACTIVE;  /* check for rotation */
878                                         if (fly->zlock != FLY_AXISLOCK_STATE_OFF)
879                                                 fly->zlock = FLY_AXISLOCK_STATE_ACTIVE;
880                                 }
881
882                                 if (fly->zlock == FLY_AXISLOCK_STATE_ACTIVE) {
883                                         float upvec[3];
884                                         copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
885                                         mul_m3_v3(mat, upvec);
886
887                                         /* make sure we have some z rolling */
888                                         if (fabsf(upvec[2]) > 0.00001f) {
889                                                 roll = upvec[2] * 5.0f;
890                                                 /* rotate the view about this axis */
891                                                 copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
892                                                 mul_m3_v3(mat, upvec);
893                                                 /* Rotate about the relative up vec */
894                                                 axis_angle_to_quat(tmp_quat, upvec,
895                                                                    roll * time_redraw_clamped * fly->zlock_momentum * FLY_ZUP_CORRECT_FAC);
896                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
897
898                                                 fly->zlock_momentum += FLY_ZUP_CORRECT_ACCEL;
899                                         }
900                                         else {
901                                                 fly->zlock = FLY_AXISLOCK_STATE_IDLE; /* don't check until the view rotates again */
902                                                 fly->zlock_momentum = 0.0f;
903                                         }
904                                 }
905
906                                 /* only apply xcorrect when mouse isn't applying x rot */
907                                 if (fly->xlock == FLY_AXISLOCK_STATE_ACTIVE && moffset[1] == 0) {
908                                         float upvec[3];
909                                         copy_v3_fl3(upvec, 0.0f, 0.0f, 1.0f);
910                                         mul_m3_v3(mat, upvec);
911                                         /* make sure we have some z rolling */
912                                         if (fabsf(upvec[2]) > 0.00001f) {
913                                                 roll = upvec[2] * -5.0f;
914                                                 /* rotate the view about this axis */
915                                                 copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f);
916                                                 mul_m3_v3(mat, upvec);
917
918                                                 /* Rotate about the relative up vec */
919                                                 axis_angle_to_quat(tmp_quat, upvec, roll * time_redraw_clamped * fly->xlock_momentum * 0.1f);
920                                                 mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
921
922                                                 fly->xlock_momentum += 0.05f;
923                                         }
924                                         else {
925                                                 fly->xlock = FLY_AXISLOCK_STATE_IDLE; /* see above */
926                                                 fly->xlock_momentum = 0.0f;
927                                         }
928                                 }
929
930                                 if (fly->axis == -1) {
931                                         /* pause */
932                                         zero_v3(dvec_tmp);
933                                 }
934                                 else if (!fly->use_freelook) {
935                                         /* Normal operation */
936                                         /* define dvec, view direction vector */
937                                         zero_v3(dvec_tmp);
938                                         /* move along the current axis */
939                                         dvec_tmp[fly->axis] = 1.0f;
940
941                                         mul_m3_v3(mat, dvec_tmp);
942                                 }
943                                 else {
944                                         normalize_v3_v3(dvec_tmp, fly->dvec_prev);
945                                         if (fly->speed < 0.0f) {
946                                                 negate_v3(dvec_tmp);
947                                         }
948                                 }
949
950                                 mul_v3_fl(dvec_tmp, fly->speed * time_redraw * 0.25f);
951                         }
952
953                         /* impose a directional lag */
954                         interp_v3_v3v3(dvec, dvec_tmp, fly->dvec_prev, (1.0f / (1.0f + (time_redraw * FLY_SMOOTH_FAC))));
955
956                         if (rv3d->persp == RV3D_CAMOB) {
957                                 Object *lock_ob = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
958                                 if (lock_ob->protectflag & OB_LOCK_LOCX) dvec[0] = 0.0;
959                                 if (lock_ob->protectflag & OB_LOCK_LOCY) dvec[1] = 0.0;
960                                 if (lock_ob->protectflag & OB_LOCK_LOCZ) dvec[2] = 0.0;
961                         }
962
963                         add_v3_v3(rv3d->ofs, dvec);
964
965                         if (rv3d->persp == RV3D_CAMOB) {
966                                 const bool do_rotate = ((fly->xlock != FLY_AXISLOCK_STATE_OFF) ||
967                                                         (fly->zlock != FLY_AXISLOCK_STATE_OFF) ||
968                                                         ((moffset[0] || moffset[1]) && !fly->pan_view));
969                                 const bool do_translate = (fly->speed != 0.0f || fly->pan_view);
970                                 flyMoveCamera(C, fly, do_rotate, do_translate);
971                         }
972
973                 }
974                 else {
975                         /* we're not redrawing but we need to update the time else the view will jump */
976                         fly->time_lastdraw = PIL_check_seconds_timer();
977                 }
978                 /* end drawing */
979                 copy_v3_v3(fly->dvec_prev, dvec);
980         }
981
982         return OPERATOR_FINISHED;
983 }
984
985 #ifdef WITH_INPUT_NDOF
986 static void flyApply_ndof(bContext *C, FlyInfo *fly)
987 {
988         Object *lock_ob = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
989         bool has_translate, has_rotate;
990
991         view3d_ndof_fly(fly->ndof,
992                         fly->v3d, fly->rv3d,
993                         fly->use_precision, lock_ob ? lock_ob->protectflag : 0,
994                         &has_translate, &has_rotate);
995
996         if (has_translate || has_rotate) {
997                 fly->redraw = true;
998
999                 if (fly->rv3d->persp == RV3D_CAMOB) {
1000                         flyMoveCamera(C, fly, has_rotate, has_translate);
1001                 }
1002         }
1003 }
1004 #endif /* WITH_INPUT_NDOF */
1005
1006 static int fly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1007 {
1008         RegionView3D *rv3d = CTX_wm_region_view3d(C);
1009         FlyInfo *fly;
1010
1011         if (rv3d->viewlock & RV3D_LOCKED)
1012                 return OPERATOR_CANCELLED;
1013
1014         fly = MEM_callocN(sizeof(FlyInfo), "FlyOperation");
1015
1016         op->customdata = fly;
1017
1018         if (initFlyInfo(C, fly, op, event) == false) {
1019                 MEM_freeN(op->customdata);
1020                 return OPERATOR_CANCELLED;
1021         }
1022
1023         flyEvent(C, op, fly, event);
1024
1025         WM_event_add_modal_handler(C, op);
1026
1027         return OPERATOR_RUNNING_MODAL;
1028 }
1029
1030 static void fly_cancel(bContext *C, wmOperator *op)
1031 {
1032         FlyInfo *fly = op->customdata;
1033
1034         fly->state = FLY_CANCEL;
1035         flyEnd(C, fly);
1036         op->customdata = NULL;
1037 }
1038
1039 static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
1040 {
1041         int exit_code;
1042         bool do_draw = false;
1043         FlyInfo *fly = op->customdata;
1044         RegionView3D *rv3d = fly->rv3d;
1045         Object *fly_object = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
1046
1047         fly->redraw = 0;
1048
1049         flyEvent(C, op, fly, event);
1050
1051 #ifdef WITH_INPUT_NDOF
1052         if (fly->ndof) { /* 3D mouse overrules [2D mouse + timer] */
1053                 if (event->type == NDOF_MOTION) {
1054                         flyApply_ndof(C, fly);
1055                 }
1056         }
1057         else
1058 #endif /* WITH_INPUT_NDOF */
1059         if (event->type == TIMER && event->customdata == fly->timer) {
1060                 flyApply(C, fly);
1061         }
1062
1063         do_draw |= fly->redraw;
1064
1065         exit_code = flyEnd(C, fly);
1066
1067         if (exit_code != OPERATOR_RUNNING_MODAL)
1068                 do_draw = true;
1069
1070         if (do_draw) {
1071                 if (rv3d->persp == RV3D_CAMOB) {
1072                         WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, fly_object);
1073                 }
1074
1075                 // puts("redraw!"); // too frequent, commented with NDOF_FLY_DRAW_TOOMUCH for now
1076                 ED_region_tag_redraw(CTX_wm_region(C));
1077         }
1078
1079         if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED))
1080                 ED_area_headerprint(CTX_wm_area(C), NULL);
1081
1082         return exit_code;
1083 }
1084
1085 void VIEW3D_OT_fly(wmOperatorType *ot)
1086 {
1087         /* identifiers */
1088         ot->name = "Fly Navigation";
1089         ot->description = "Interactively fly around the scene";
1090         ot->idname = "VIEW3D_OT_fly";
1091
1092         /* api callbacks */
1093         ot->invoke = fly_invoke;
1094         ot->cancel = fly_cancel;
1095         ot->modal = fly_modal;
1096         ot->poll = ED_operator_region_view3d_active;
1097
1098         /* flags */
1099         ot->flag = OPTYPE_BLOCKING;
1100 }