4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2007 Blender Foundation.
21 * All rights reserved.
24 * Contributor(s): Blender Foundation
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file blender/windowmanager/intern/wm_event_system.c
37 #include "DNA_listBase.h"
38 #include "DNA_screen_types.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_windowmanager_types.h"
41 #include "DNA_userdef_types.h"
43 #include "MEM_guardedalloc.h"
45 #include "GHOST_C-api.h"
47 #include "BLI_blenlib.h"
48 #include "BLI_utildefines.h"
51 #include "BKE_blender.h"
52 #include "BKE_context.h"
53 #include "BKE_idprop.h"
54 #include "BKE_global.h"
56 #include "BKE_report.h"
57 #include "BKE_scene.h"
58 #include "BKE_screen.h"
60 #include "BKE_sound.h"
62 #include "ED_fileselect.h"
64 #include "ED_render.h"
65 #include "ED_screen.h"
66 #include "ED_view3d.h"
69 #include "RNA_access.h"
71 #include "UI_interface.h"
78 #include "wm_window.h"
79 #include "wm_event_system.h"
80 #include "wm_event_types.h"
84 # include "RNA_enum_types.h"
87 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only);
89 /* ************ event management ************** */
91 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
93 wmEvent *event= MEM_callocN(sizeof(wmEvent), "wmEvent");
95 *event= *event_to_add;
96 BLI_addtail(&win->queue, event);
99 void wm_event_free(wmEvent *event)
101 if(event->customdata) {
102 if(event->customdatafree) {
103 /* note: pointer to listbase struct elsewhere */
104 if(event->custom==EVT_DATA_LISTBASE)
105 BLI_freelistN(event->customdata);
107 MEM_freeN(event->customdata);
113 void wm_event_free_all(wmWindow *win)
117 while((event= win->queue.first)) {
118 BLI_remlink(&win->queue, event);
119 wm_event_free(event);
123 /* ********************* notifiers, listeners *************** */
125 static int wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference)
129 for(note=wm->queue.first; note; note=note->next)
130 if((note->category|note->data|note->subtype|note->action) == type && note->reference == reference)
136 /* XXX: in future, which notifiers to send to other windows? */
137 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
139 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
141 note->wm= CTX_wm_manager(C);
142 BLI_addtail(¬e->wm->queue, note);
144 note->window= CTX_wm_window(C);
147 note->swinid= CTX_wm_region(C)->swinid;
149 note->category= type & NOTE_CATEGORY;
150 note->data= type & NOTE_DATA;
151 note->subtype= type & NOTE_SUBTYPE;
152 note->action= type & NOTE_ACTION;
154 note->reference= reference;
157 void WM_main_add_notifier(unsigned int type, void *reference)
160 wmWindowManager *wm= bmain->wm.first;
162 if(wm && !wm_test_duplicate_notifier(wm, type, reference)) {
163 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
166 BLI_addtail(¬e->wm->queue, note);
168 note->category= type & NOTE_CATEGORY;
169 note->data= type & NOTE_DATA;
170 note->subtype= type & NOTE_SUBTYPE;
171 note->action= type & NOTE_ACTION;
173 note->reference= reference;
177 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
179 wmNotifier *note= wm->queue.first;
181 if(note) BLI_remlink(&wm->queue, note);
185 /* called in mainloop */
186 void wm_event_do_notifiers(bContext *C)
188 wmWindowManager *wm= CTX_wm_manager(C);
189 wmNotifier *note, *next;
191 unsigned int win_combine_v3d_datamask= 0;
196 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
197 for(win= wm->windows.first; win; win= win->next) {
200 CTX_wm_window_set(C, win);
202 for(note= wm->queue.first; note; note= next) {
205 if(note->category==NC_WM) {
206 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
208 wm_window_title(wm, win);
210 else if(note->data==ND_DATACHANGED)
211 wm_window_title(wm, win);
213 if(note->window==win) {
214 if(note->category==NC_SCREEN) {
215 if(note->data==ND_SCREENBROWSE) {
216 ED_screen_set(C, note->reference); // XXX hrms, think this over!
218 printf("screen set %p\n", note->reference);
220 else if(note->data==ND_SCREENDELETE) {
221 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
223 printf("screen delete %p\n", note->reference);
228 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
229 if(note->category==NC_SCENE) {
230 if(note->data==ND_SCENEBROWSE) {
231 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
233 printf("scene set %p\n", note->reference);
235 else if(note->data==ND_FRAME)
238 if(note->action == NA_REMOVED) {
239 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
241 printf("scene delete %p\n", note->reference);
246 if(ELEM5(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE, NC_WM)) {
247 ED_info_stats_clear(CTX_data_scene(C));
248 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
253 /* XXX, quick frame changes can cause a crash if framechange and rendering
254 * collide (happens on slow scenes), scene_update_for_newframe can be called
255 * twice which can depgraph update the same object at once */
258 /* depsgraph gets called, might send more notifiers */
259 ED_update_for_newframe(CTX_data_main(C), win->screen->scene, win->screen, 1);
264 /* the notifiers are sent without context, to keep it clean */
265 while( (note=wm_notifier_next(wm)) ) {
266 for(win= wm->windows.first; win; win= win->next) {
268 /* filter out notifiers */
269 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
270 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
275 /* XXX context in notifiers? */
276 CTX_wm_window_set(C, win);
278 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
279 ED_screen_do_listen(C, note);
281 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
282 ED_region_do_listen(ar, note);
285 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
286 ED_area_do_listen(sa, note);
287 for(ar=sa->regionbase.first; ar; ar= ar->next) {
288 ED_region_do_listen(ar, note);
297 /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */
298 for(win= wm->windows.first; win; win= win->next) {
299 win_combine_v3d_datamask |= ED_viewedit_datamask(win->screen);
302 /* cached: editor refresh callbacks now, they get context */
303 for(win= wm->windows.first; win; win= win->next) {
306 CTX_wm_window_set(C, win);
307 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
309 CTX_wm_area_set(C, sa);
310 ED_area_do_refresh(C, sa);
314 /* XXX make lock in future, or separated derivedmesh users in scene */
316 /* depsgraph & animation: update tagged datablocks */
317 Main *bmain = CTX_data_main(C);
319 /* copied to set's in scene_update_tagged_recursive() */
320 win->screen->scene->customdata_mask= win_combine_v3d_datamask;
322 /* XXX, hack so operators can enforce datamasks [#26482], gl render */
323 win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal;
325 scene_update_tagged(bmain, win->screen->scene);
327 ED_render_engine_update_tagged(C, bmain);
329 scene_clear_tagged(bmain, win->screen->scene);
333 CTX_wm_window_set(C, NULL);
336 static int wm_event_always_pass(wmEvent *event)
338 /* some events we always pass on, to ensure proper communication */
339 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
342 /* ********************* ui handler ******************* */
344 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
346 ScrArea *area= CTX_wm_area(C);
347 ARegion *region= CTX_wm_region(C);
348 ARegion *menu= CTX_wm_menu(C);
349 static int do_wheel_ui= 1;
350 int is_wheel= ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
353 /* UI is quite aggressive with swallowing events, like scrollwheel */
354 /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
357 return WM_HANDLER_CONTINUE;
358 else if(wm_event_always_pass(event)==0)
362 /* we set context to where ui handler came from */
363 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
364 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
365 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
367 retval= handler->ui_handle(C, event, handler->ui_userdata);
369 /* putting back screen context */
370 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
371 CTX_wm_area_set(C, area);
372 CTX_wm_region_set(C, region);
373 CTX_wm_menu_set(C, menu);
376 /* this special cases is for areas and regions that get removed */
377 CTX_wm_area_set(C, NULL);
378 CTX_wm_region_set(C, NULL);
379 CTX_wm_menu_set(C, NULL);
382 if(retval == WM_UI_HANDLER_BREAK)
383 return WM_HANDLER_BREAK;
385 /* event not handled in UI, if wheel then we temporarily disable it */
389 return WM_HANDLER_CONTINUE;
392 static void wm_handler_ui_cancel(bContext *C)
394 wmWindow *win= CTX_wm_window(C);
395 ARegion *ar= CTX_wm_region(C);
396 wmEventHandler *handler, *nexthandler;
401 for(handler= ar->handlers.first; handler; handler= nexthandler) {
402 nexthandler= handler->next;
404 if(handler->ui_handle) {
405 wmEvent event= *(win->eventstate);
406 event.type= EVT_BUT_CANCEL;
407 handler->ui_handle(C, &event, handler->ui_userdata);
412 /* ********************* operators ******************* */
414 int WM_operator_poll(bContext *C, wmOperatorType *ot)
416 wmOperatorTypeMacro *otmacro;
418 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
419 wmOperatorType *ot_macro= WM_operatortype_find(otmacro->idname, 0);
421 if(0==WM_operator_poll(C, ot_macro))
425 /* python needs operator type, so we added exception for it */
427 return ot->pyop_poll(C, ot);
434 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
435 int WM_operator_poll_context(bContext *C, wmOperatorType *ot, int context)
437 return wm_operator_call_internal(C, ot, NULL, NULL, context, TRUE);
440 static void wm_operator_print(bContext *C, wmOperator *op)
442 /* context is needed for enum function */
443 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
448 /* for debugging only, getting inspecting events manually is tedious */
451 void WM_event_print(wmEvent *event)
454 const char *unknown= "UNKNOWN";
455 const char *type_id= unknown;
456 const char *val_id= unknown;
458 RNA_enum_identifier(event_type_items, event->type, &type_id);
459 RNA_enum_identifier(event_value_items, event->val, &val_id);
461 printf("wmEvent - type:%d/%s, val:%d/%s, "
462 "shift:%d, ctrl:%d, alt:%d, oskey:%d, keymodifier:%d, "
463 "mouse:(%d,%d), ascii:'%c', utf8:'%.6s', "
464 "keymap_idname:%s, pointer:%p\n",
465 event->type, type_id, event->val, val_id,
466 event->shift, event->ctrl, event->alt, event->oskey, event->keymodifier,
467 event->x, event->y, event->ascii, event->utf8_buf,
468 event->keymap_idname, (void *)event);
471 printf("wmEvent - NULL\n");
477 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, int popup)
480 if(op->reports->list.first) {
481 /* FIXME, temp setting window, see other call to uiPupMenuReports for why */
482 wmWindow *win_prev= CTX_wm_window(C);
483 ScrArea *area_prev= CTX_wm_area(C);
484 ARegion *ar_prev= CTX_wm_region(C);
487 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
489 uiPupMenuReports(C, op->reports);
491 CTX_wm_window_set(C, win_prev);
492 CTX_wm_area_set(C, area_prev);
493 CTX_wm_region_set(C, ar_prev);
497 if(retval & OPERATOR_FINISHED) {
499 wm_operator_print(C, op); /* todo - this print may double up, might want to check more flags then the FINISHED */
501 BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
502 if (op->type->flag & OPTYPE_REGISTER) {
503 if(G.background == 0) { /* ends up printing these in the terminal, gets annoying */
504 /* Report the python string representation of the operator */
505 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
506 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
512 /* if the caller owns them them handle this */
513 if (op->reports->list.first && (op->reports->flag & RPT_OP_HOLD) == 0) {
515 wmWindowManager *wm = CTX_wm_manager(C);
516 ReportList *wm_reports= CTX_wm_reports(C);
517 ReportTimerInfo *rti;
519 /* add reports to the global list, otherwise they are not seen */
520 BLI_movelisttolist(&wm_reports->list, &op->reports->list);
522 /* After adding reports to the global list, reset the report timer. */
523 WM_event_remove_timer(wm, NULL, wm_reports->reporttimer);
525 /* Records time since last report was added */
526 wm_reports->reporttimer= WM_event_add_timer(wm, CTX_wm_window(C), TIMERREPORT, 0.05);
528 rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
529 wm_reports->reporttimer->customdata = rti;
533 /* this function is mainly to check that the rules for freeing
534 * an operator are kept in sync.
536 static int wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
538 return wm && (wm->op_undo_depth == 0) && (ot->flag & OPTYPE_REGISTER);
541 static void wm_operator_finished(bContext *C, wmOperator *op, int repeat)
543 wmWindowManager *wm= CTX_wm_manager(C);
545 op->customdata= NULL;
547 /* we don't want to do undo pushes for operators that are being
548 called from operators that already do an undo push. usually
549 this will happen for python operators that call C operators */
550 if(wm->op_undo_depth == 0)
551 if(op->type->flag & OPTYPE_UNDO)
552 ED_undo_push_op(C, op);
556 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
557 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
561 if(wm_operator_register_check(wm, op->type))
562 wm_operator_register(C, op);
564 WM_operator_free(op);
568 /* if repeat is true, it doesn't register again, nor does it free */
569 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
571 wmWindowManager *wm= CTX_wm_manager(C);
572 int retval= OPERATOR_CANCELLED;
574 CTX_wm_operator_poll_msg_set(C, NULL);
576 if(op==NULL || op->type==NULL)
579 if(0==WM_operator_poll(C, op->type))
583 if(op->type->flag & OPTYPE_UNDO)
586 retval= op->type->exec(C, op);
587 OPERATOR_RETVAL_CHECK(retval);
589 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
593 if (retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED) && repeat == 0)
594 wm_operator_reports(C, op, retval, 0);
596 if(retval & OPERATOR_FINISHED)
597 wm_operator_finished(C, op, repeat);
599 WM_operator_free(op);
601 return retval | OPERATOR_HANDLED;
605 /* for running operators with frozen context (modal handlers, menus) */
606 int WM_operator_call(bContext *C, wmOperator *op)
608 return wm_operator_exec(C, op, 0);
611 /* do this operator again, put here so it can share above code */
612 int WM_operator_repeat(bContext *C, wmOperator *op)
614 return wm_operator_exec(C, op, 1);
616 /* TRUE if WM_operator_repeat can run
617 * simple check for now but may become more involved.
618 * To be sure the operator can run call WM_operator_poll(C, op->type) also, since this call
619 * checks if WM_operator_repeat() can run at all, not that it WILL run at any time. */
620 int WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
622 return op->type->exec != NULL;
625 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
627 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
629 /* XXX adding new operator could be function, only happens here now */
631 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
633 /* initialize properties, either copy or create */
634 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
635 if(properties && properties->data) {
636 op->properties= IDP_CopyProperty(properties->data);
639 IDPropertyTemplate val = {0};
640 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
642 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
644 /* initialize error reports */
646 op->reports= reports; /* must be initialized already */
649 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
650 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
653 /* recursive filling of operator macro list */
654 if(ot->macro.first) {
655 static wmOperator *motherop= NULL;
656 wmOperatorTypeMacro *otmacro;
659 /* ensure all ops are in execution order in 1 list */
666 /* if properties exist, it will contain everything needed */
668 otmacro= ot->macro.first;
670 RNA_STRUCT_BEGIN(properties, prop) {
675 /* skip invalid properties */
676 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
678 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
679 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
680 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
682 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
684 BLI_addtail(&motherop->macro, opm);
685 opm->opm= motherop; /* pointer to mom, for modal() */
687 otmacro= otmacro->next;
692 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
693 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
694 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
696 BLI_addtail(&motherop->macro, opm);
697 opm->opm= motherop; /* pointer to mom, for modal() */
705 WM_operator_properties_sanitize(op->ptr, 0);
710 static void wm_region_mouse_co(bContext *C, wmEvent *event)
712 ARegion *ar= CTX_wm_region(C);
714 /* compatibility convention */
715 event->mval[0]= event->x - ar->winrct.xmin;
716 event->mval[1]= event->y - ar->winrct.ymin;
719 /* these values are invalid (avoid odd behavior by relying on old mval values) */
725 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports, short poll_only)
727 wmWindowManager *wm= CTX_wm_manager(C);
728 int retval= OPERATOR_PASS_THROUGH;
730 /* this is done because complicated setup is done to call this function that is better not duplicated */
732 return WM_operator_poll(C, ot);
734 if(WM_operator_poll(C, ot)) {
735 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
737 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
738 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
740 if(op->type->invoke && event) {
741 wm_region_mouse_co(C, event);
743 if(op->type->flag & OPTYPE_UNDO)
746 retval= op->type->invoke(C, op, event);
747 OPERATOR_RETVAL_CHECK(retval);
749 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
752 else if(op->type->exec) {
753 if(op->type->flag & OPTYPE_UNDO)
756 retval= op->type->exec(C, op);
757 OPERATOR_RETVAL_CHECK(retval);
759 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
763 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
765 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
766 * currently python only uses this */
767 if (!(retval & OPERATOR_HANDLED) && retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
768 /* only show the report if the report list was not given in the function */
769 wm_operator_reports(C, op, retval, (reports==NULL));
771 if(retval & OPERATOR_HANDLED)
772 ; /* do nothing, wm_operator_exec() has been called somewhere */
773 else if(retval & OPERATOR_FINISHED) {
774 wm_operator_finished(C, op, 0);
776 else if(retval & OPERATOR_RUNNING_MODAL) {
777 /* grab cursor during blocking modal ops (X11)
778 * Also check for macro
780 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
781 int bounds[4] = {-1,-1,-1,-1};
785 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
787 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
790 /* exception, cont. grab in header is annoying */
792 ARegion *ar= CTX_wm_region(C);
793 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
800 ARegion *ar= CTX_wm_region(C);
801 ScrArea *sa= CTX_wm_area(C);
803 if(ar && ar->regiontype == RGN_TYPE_WINDOW && event && BLI_in_rcti(&ar->winrct, event->x, event->y)) {
804 winrect= &ar->winrct;
807 winrect= &sa->totrct;
811 bounds[0]= winrect->xmin;
812 bounds[1]= winrect->ymax;
813 bounds[2]= winrect->xmax;
814 bounds[3]= winrect->ymin;
818 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
821 /* cancel UI handlers, typically tooltips that can hang around
822 while dragging the view or worse, that stay there permanently
823 after the modal operator has swallowed all events and passed
824 none to the UI handler */
825 wm_handler_ui_cancel(C);
828 WM_operator_free(op);
834 /* WM_operator_name_call is the main accessor function
835 * this is for python to access since its done the operator lookup
837 * invokes operator in context */
838 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only)
840 wmWindow *window= CTX_wm_window(C);
845 CTX_wm_operator_poll_msg_set(C, NULL);
850 case WM_OP_INVOKE_DEFAULT:
851 case WM_OP_INVOKE_REGION_WIN:
852 case WM_OP_INVOKE_AREA:
853 case WM_OP_INVOKE_SCREEN:
854 /* window is needed for invoke, cancel operator */
858 event= window->eventstate;
866 case WM_OP_EXEC_REGION_WIN:
867 case WM_OP_INVOKE_REGION_WIN:
868 case WM_OP_EXEC_REGION_CHANNELS:
869 case WM_OP_INVOKE_REGION_CHANNELS:
870 case WM_OP_EXEC_REGION_PREVIEW:
871 case WM_OP_INVOKE_REGION_PREVIEW:
873 /* forces operator to go to the region window/channels/preview, for header menus
874 * but we stay in the same region if we are already in one
876 ARegion *ar= CTX_wm_region(C);
877 ScrArea *area= CTX_wm_area(C);
878 int type = RGN_TYPE_WINDOW;
881 case WM_OP_EXEC_REGION_CHANNELS:
882 case WM_OP_INVOKE_REGION_CHANNELS:
883 type = RGN_TYPE_CHANNELS;
886 case WM_OP_EXEC_REGION_PREVIEW:
887 case WM_OP_INVOKE_REGION_PREVIEW:
888 type = RGN_TYPE_PREVIEW;
891 case WM_OP_EXEC_REGION_WIN:
892 case WM_OP_INVOKE_REGION_WIN:
894 type = RGN_TYPE_WINDOW;
898 if(!(ar && ar->regiontype == type) && area) {
899 ARegion *ar1= BKE_area_find_region_type(area, type);
901 CTX_wm_region_set(C, ar1);
904 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
906 /* set region back */
907 CTX_wm_region_set(C, ar);
911 case WM_OP_EXEC_AREA:
912 case WM_OP_INVOKE_AREA:
914 /* remove region from context */
915 ARegion *ar= CTX_wm_region(C);
917 CTX_wm_region_set(C, NULL);
918 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
919 CTX_wm_region_set(C, ar);
923 case WM_OP_EXEC_SCREEN:
924 case WM_OP_INVOKE_SCREEN:
926 /* remove region + area from context */
927 ARegion *ar= CTX_wm_region(C);
928 ScrArea *area= CTX_wm_area(C);
930 CTX_wm_region_set(C, NULL);
931 CTX_wm_area_set(C, NULL);
932 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
933 CTX_wm_area_set(C, area);
934 CTX_wm_region_set(C, ar);
938 case WM_OP_EXEC_DEFAULT:
939 case WM_OP_INVOKE_DEFAULT:
940 return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
948 /* invokes operator in context */
949 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
951 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
953 return wm_operator_call_internal(C, ot, properties, NULL, context, FALSE);
958 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
959 - wmOperatorType is used instead of operator name since python already has the operator type
960 - poll() must be called by python before this runs.
961 - reports can be passed to this function (so python can report them as exceptions)
963 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
965 int retval= OPERATOR_CANCELLED;
969 op= wm_operator_create(wm, ot, properties, reports);
971 if (op->type->exec) {
972 if(op->type->flag & OPTYPE_UNDO)
975 retval= op->type->exec(C, op);
976 OPERATOR_RETVAL_CHECK(retval);
978 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
982 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
985 retval= wm_operator_call_internal(C, ot, properties, reports, context, FALSE);
987 /* keep the reports around if needed later */
988 if ( (retval & OPERATOR_RUNNING_MODAL) ||
989 ((retval & OPERATOR_FINISHED) && wm_operator_register_check(CTX_wm_manager(C), ot))
991 reports->flag |= RPT_FREE; /* let blender manage freeing */
998 /* ********************* handlers *************** */
1000 /* future extra customadata free? */
1001 void wm_event_free_handler(wmEventHandler *handler)
1006 /* only set context when area/region is part of screen */
1007 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
1009 bScreen *screen= CTX_wm_screen(C);
1011 if(screen && handler->op) {
1012 if(handler->op_area==NULL)
1013 CTX_wm_area_set(C, NULL);
1017 for(sa= screen->areabase.first; sa; sa= sa->next)
1018 if(sa==handler->op_area)
1021 /* when changing screen layouts with running modal handlers (like render display), this
1022 is not an error to print */
1023 if(handler->op==NULL)
1024 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
1028 CTX_wm_area_set(C, sa);
1029 for(ar= sa->regionbase.first; ar; ar= ar->next)
1030 if(ar==handler->op_region)
1032 /* XXX no warning print here, after full-area and back regions are remade */
1034 CTX_wm_region_set(C, ar);
1040 /* called on exit or remove area, only here call cancel callback */
1041 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
1043 wmEventHandler *handler;
1044 wmWindowManager *wm= CTX_wm_manager(C);
1046 /* C is zero on freeing database, modal handlers then already were freed */
1047 while((handler=handlers->first)) {
1048 BLI_remlink(handlers, handler);
1051 if(handler->op->type->cancel) {
1052 ScrArea *area= CTX_wm_area(C);
1053 ARegion *region= CTX_wm_region(C);
1055 wm_handler_op_context(C, handler);
1057 if(handler->op->type->flag & OPTYPE_UNDO)
1058 wm->op_undo_depth++;
1060 handler->op->type->cancel(C, handler->op);
1062 if(handler->op->type->flag & OPTYPE_UNDO)
1063 wm->op_undo_depth--;
1065 CTX_wm_area_set(C, area);
1066 CTX_wm_region_set(C, region);
1069 WM_cursor_ungrab(CTX_wm_window(C));
1070 WM_operator_free(handler->op);
1072 else if(handler->ui_remove) {
1073 ScrArea *area= CTX_wm_area(C);
1074 ARegion *region= CTX_wm_region(C);
1075 ARegion *menu= CTX_wm_menu(C);
1077 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
1078 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
1079 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
1081 handler->ui_remove(C, handler->ui_userdata);
1083 CTX_wm_area_set(C, area);
1084 CTX_wm_region_set(C, region);
1085 CTX_wm_menu_set(C, menu);
1088 wm_event_free_handler(handler);
1092 /* do userdef mappings */
1093 int WM_userdef_event_map(int kmitype)
1097 if(U.flag & USER_LMOUSESELECT)
1103 if(U.flag & USER_LMOUSESELECT)
1109 if(U.uiflag & USER_WHEELZOOMDIR)
1110 return WHEELUPMOUSE;
1112 return WHEELDOWNMOUSE;
1115 if(U.uiflag & USER_WHEELZOOMDIR)
1116 return WHEELDOWNMOUSE;
1118 return WHEELUPMOUSE;
1121 if(U.flag & USER_LMOUSESELECT)
1127 if(U.flag & USER_LMOUSESELECT)
1136 static void wm_eventemulation(wmEvent *event)
1138 static int mmb_emulated = 0; /* this should be in a data structure somwhere */
1140 /* middlemouse emulation */
1141 if(U.flag & USER_TWOBUTTONMOUSE) {
1142 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
1143 event->type = MIDDLEMOUSE;
1145 mmb_emulated = event->val;
1150 /* rightmouse emulation */
1151 if(U.flag & USER_TWOBUTTONMOUSE) {
1152 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
1153 event->type = RIGHTMOUSE;
1155 mmb_emulated = event->val;
1160 /* numpad emulation */
1161 if(U.flag & USER_NONUMPAD) {
1162 switch(event->type) {
1163 case ZEROKEY: event->type = PAD0; break;
1164 case ONEKEY: event->type = PAD1; break;
1165 case TWOKEY: event->type = PAD2; break;
1166 case THREEKEY: event->type = PAD3; break;
1167 case FOURKEY: event->type = PAD4; break;
1168 case FIVEKEY: event->type = PAD5; break;
1169 case SIXKEY: event->type = PAD6; break;
1170 case SEVENKEY: event->type = PAD7; break;
1171 case EIGHTKEY: event->type = PAD8; break;
1172 case NINEKEY: event->type = PAD9; break;
1173 case MINUSKEY: event->type = PADMINUS; break;
1174 case EQUALKEY: event->type = PADPLUSKEY; break;
1175 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
1180 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
1182 int kmitype= WM_userdef_event_map(kmi->type);
1184 if(kmi->flag & KMI_INACTIVE) return 0;
1186 /* the matching rules */
1187 if(kmitype==KM_TEXTINPUT)
1188 if(ISTEXTINPUT(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) return 1;
1190 if(winevent->type!=kmitype) return 0;
1192 if(kmi->val!=KM_ANY)
1193 if(winevent->val!=kmi->val) return 0;
1195 /* modifiers also check bits, so it allows modifier order */
1196 if(kmi->shift!=KM_ANY)
1197 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
1198 if(kmi->ctrl!=KM_ANY)
1199 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
1200 if(kmi->alt!=KM_ANY)
1201 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
1202 if(kmi->oskey!=KM_ANY)
1203 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
1205 if(kmi->keymodifier)
1206 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1208 /* key modifiers always check when event has it */
1209 /* otherwise regular keypresses with keymodifier still work */
1210 if(winevent->keymodifier)
1211 if(ISTEXTINPUT(winevent->type))
1212 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1218 /* operator exists */
1219 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1221 /* support for modal keymap in macros */
1225 if(op->type->modalkeymap) {
1226 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1229 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1230 if(wm_eventmatch(event, kmi)) {
1232 event->type= EVT_MODAL_MAP;
1233 event->val= kmi->propvalue;
1239 /* Warning: this function removes a modal handler, when finished */
1240 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1242 int retval= OPERATOR_PASS_THROUGH;
1244 /* derived, modal or blocking operator */
1246 wmOperator *op= handler->op;
1247 wmOperatorType *ot= op->type;
1250 /* we set context to where modal handler came from */
1251 wmWindowManager *wm= CTX_wm_manager(C);
1252 ScrArea *area= CTX_wm_area(C);
1253 ARegion *region= CTX_wm_region(C);
1255 wm_handler_op_context(C, handler);
1256 wm_region_mouse_co(C, event);
1257 wm_event_modalkeymap(C, op, event);
1259 if(ot->flag & OPTYPE_UNDO)
1260 wm->op_undo_depth++;
1262 retval= ot->modal(C, op, event);
1263 OPERATOR_RETVAL_CHECK(retval);
1265 /* when this is _not_ the case the modal modifier may have loaded
1266 * a new blend file (demo mode does this), so we have to assume
1267 * the event, operator etc have all been freed. - campbell */
1268 if(CTX_wm_manager(C) == wm) {
1270 if(ot->flag & OPTYPE_UNDO)
1271 wm->op_undo_depth--;
1273 /* putting back screen context, reval can pass trough after modal failures! */
1274 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1275 CTX_wm_area_set(C, area);
1276 CTX_wm_region_set(C, region);
1279 /* this special cases is for areas and regions that get removed */
1280 CTX_wm_area_set(C, NULL);
1281 CTX_wm_region_set(C, NULL);
1284 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED))
1285 wm_operator_reports(C, op, retval, 0);
1287 if(retval & OPERATOR_FINISHED) {
1288 wm_operator_finished(C, op, 0);
1291 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1292 WM_operator_free(op);
1296 /* remove modal handler, operator itself should have been cancelled and freed */
1297 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1298 WM_cursor_ungrab(CTX_wm_window(C));
1300 BLI_remlink(handlers, handler);
1301 wm_event_free_handler(handler);
1303 /* prevent silly errors from operator users */
1304 //retval &= ~OPERATOR_PASS_THROUGH;
1310 printf("wm_handler_operator_call error\n");
1313 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1316 retval= wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
1318 /* Finished and pass through flag as handled */
1320 /* Finished and pass through flag as handled */
1321 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1322 return WM_HANDLER_HANDLED;
1324 /* Modal unhandled, break */
1325 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1326 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1328 if(retval & OPERATOR_PASS_THROUGH)
1329 return WM_HANDLER_CONTINUE;
1331 return WM_HANDLER_BREAK;
1334 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1335 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1337 wmWindowManager *wm= CTX_wm_manager(C);
1339 int action= WM_HANDLER_CONTINUE;
1341 if(event->type != EVT_FILESELECT)
1343 if(handler->op != (wmOperator *)event->customdata)
1346 switch(event->val) {
1347 case EVT_FILESELECT_OPEN:
1348 case EVT_FILESELECT_FULL_OPEN:
1352 /* sa can be null when window A is active, but mouse is over window B */
1353 /* in this case, open file select in original window A */
1354 if (handler->op_area == NULL) {
1355 bScreen *screen = CTX_wm_screen(C);
1356 sa = (ScrArea *)screen->areabase.first;
1359 sa = handler->op_area;
1362 if(event->val==EVT_FILESELECT_OPEN) {
1363 ED_area_newspace(C, sa, SPACE_FILE); /* 'sa' is modified in-place */
1366 sa= ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1369 /* note, getting the 'sa' back from the context causes a nasty bug where the newly created
1370 * 'sa' != CTX_wm_area(C). removed the line below and set 'sa' in the 'if' above */
1371 /* sa = CTX_wm_area(C); */
1373 /* settings for filebrowser, sfile is not operator owner but sends events */
1374 sfile= (SpaceFile*)sa->spacedata.first;
1375 sfile->op= handler->op;
1377 ED_fileselect_set_params(sfile);
1379 action= WM_HANDLER_BREAK;
1383 case EVT_FILESELECT_EXEC:
1384 case EVT_FILESELECT_CANCEL:
1385 case EVT_FILESELECT_EXTERNAL_CANCEL:
1387 /* XXX validate area and region? */
1388 bScreen *screen= CTX_wm_screen(C);
1390 /* remlink now, for load file case before removing*/
1391 BLI_remlink(handlers, handler);
1393 if(event->val!=EVT_FILESELECT_EXTERNAL_CANCEL) {
1394 if(screen != handler->filescreen) {
1395 ED_screen_full_prevspace(C, CTX_wm_area(C));
1398 ED_area_prevspace(C, CTX_wm_area(C));
1402 wm_handler_op_context(C, handler);
1404 /* needed for uiPupMenuReports */
1406 if(event->val==EVT_FILESELECT_EXEC) {
1407 #if 0 // use REDALERT now
1409 /* a bit weak, might become arg for WM_event_fileselect? */
1410 /* XXX also extension code in image-save doesnt work for this yet */
1411 if (RNA_struct_find_property(handler->op->ptr, "check_existing") &&
1412 RNA_boolean_get(handler->op->ptr, "check_existing")) {
1413 char *path= RNA_string_get_alloc(handler->op->ptr, "filepath", NULL, 0);
1414 /* this gives ownership to pupmenu */
1415 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1424 if(handler->op->type->flag & OPTYPE_UNDO)
1425 wm->op_undo_depth++;
1427 retval= handler->op->type->exec(C, handler->op);
1429 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1430 if(handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1431 wm->op_undo_depth--;
1433 if (retval & OPERATOR_FINISHED)
1435 wm_operator_print(C, handler->op);
1437 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1438 if(CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
1439 if(handler->op->type->flag & OPTYPE_UNDO)
1440 ED_undo_push_op(C, handler->op);
1442 if(handler->op->reports->list.first) {
1444 /* FIXME, temp setting window, this is really bad!
1445 * only have because lib linking errors need to be seen by users :(
1446 * it can be removed without breaking anything but then no linking errors - campbell */
1447 wmWindow *win_prev= CTX_wm_window(C);
1448 ScrArea *area_prev= CTX_wm_area(C);
1449 ARegion *ar_prev= CTX_wm_region(C);
1452 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1454 handler->op->reports->printlevel = RPT_WARNING;
1455 uiPupMenuReports(C, handler->op->reports);
1457 /* XXX - copied from 'wm_operator_finished()' */
1458 /* add reports to the global list, otherwise they are not seen */
1459 BLI_movelisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
1461 CTX_wm_window_set(C, win_prev);
1462 CTX_wm_area_set(C, area_prev);
1463 CTX_wm_region_set(C, ar_prev);
1466 WM_operator_free(handler->op);
1470 if(handler->op->type->cancel) {
1471 if(handler->op->type->flag & OPTYPE_UNDO)
1472 wm->op_undo_depth++;
1474 handler->op->type->cancel(C, handler->op);
1476 if(handler->op->type->flag & OPTYPE_UNDO)
1477 wm->op_undo_depth--;
1480 WM_operator_free(handler->op);
1483 CTX_wm_area_set(C, NULL);
1485 wm_event_free_handler(handler);
1487 action= WM_HANDLER_BREAK;
1495 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1497 if(handler->bbwin) {
1498 if(handler->bblocal) {
1499 rcti rect= *handler->bblocal;
1500 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1502 if(BLI_in_rcti(&rect, event->x, event->y))
1504 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1510 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1512 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1521 static int wm_action_not_handled(int action)
1523 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1526 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1528 wmWindowManager *wm= CTX_wm_manager(C);
1529 wmEventHandler *handler, *nexthandler;
1530 int action= WM_HANDLER_CONTINUE;
1533 if(handlers==NULL) return action;
1535 /* modal handlers can get removed in this loop, we keep the loop this way */
1536 for(handler= handlers->first; handler; handler= nexthandler) {
1538 nexthandler= handler->next;
1540 /* during this loop, ui handlers for nested menus can tag multiple handlers free */
1541 if(handler->flag & WM_HANDLER_DO_FREE);
1542 /* optional boundbox */
1543 else if(handler_boundbox_test(handler, event)) {
1544 /* in advance to avoid access to freed event on window close */
1545 always_pass= wm_event_always_pass(event);
1547 /* modal+blocking handler */
1548 if(handler->flag & WM_HANDLER_BLOCKING)
1549 action |= WM_HANDLER_BREAK;
1551 if(handler->keymap) {
1552 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1555 if(!keymap->poll || keymap->poll(C)) {
1556 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1557 if(wm_eventmatch(event, kmi)) {
1559 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1561 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1562 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1568 else if(handler->ui_handle) {
1569 action |= wm_handler_ui_call(C, handler, event, always_pass);
1571 else if(handler->type==WM_HANDLER_FILESELECT) {
1572 /* screen context changes here */
1573 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1575 else if(handler->dropboxes) {
1576 if(event->type==EVT_DROP) {
1577 wmDropBox *drop= handler->dropboxes->first;
1578 for(; drop; drop= drop->next) {
1579 /* other drop custom types allowed */
1580 if(event->custom==EVT_DATA_LISTBASE) {
1581 ListBase *lb= (ListBase *)event->customdata;
1584 for(drag= lb->first; drag; drag= drag->next) {
1585 if(drop->poll(C, drag, event)) {
1587 drop->copy(drag, drop);
1589 /* free the drags before calling operator */
1590 BLI_freelistN(event->customdata);
1591 event->customdata= NULL;
1594 WM_operator_name_call(C, drop->ot->idname, drop->opcontext, drop->ptr);
1595 action |= WM_HANDLER_BREAK;
1597 /* XXX fileread case */
1598 if(CTX_wm_window(C)==NULL)
1601 /* escape from drag loop, got freed */
1610 /* modal, swallows all */
1611 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1614 if(action & WM_HANDLER_BREAK) {
1616 action &= ~WM_HANDLER_BREAK;
1622 /* XXX fileread case, if the wm is freed then the handler's
1623 * will have been too so the code below need not run. */
1624 if(CTX_wm_window(C)==NULL) {
1628 /* XXX code this for all modal ops, and ensure free only happens here */
1630 /* modal ui handler can be tagged to be freed */
1631 if(BLI_findindex(handlers, handler) != -1) { /* could be free'd already by regular modal ops */
1632 if(handler->flag & WM_HANDLER_DO_FREE) {
1633 BLI_remlink(handlers, handler);
1634 wm_event_free_handler(handler);
1639 /* test for CLICK event */
1640 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1641 wmWindow *win = CTX_wm_window(C);
1643 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1644 /* test for double click first,
1645 * note1: this can be problematic because single click operators can get the
1646 * double click event but then with old mouse coords which is highly confusing,
1647 * so check for mouse moves too.
1648 * note2: the first click event will be handled but still used to create a
1649 * double click event if clicking again quickly.
1650 * If no double click events are found it will fallback to a single click.
1651 * So a double click event can result in 2 successive single click calls
1652 * if its not handled by the keymap - campbell */
1653 if ( (ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
1654 (ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
1655 ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time)
1657 event->val = KM_DBL_CLICK;
1658 /* removed this because in cases where we're this is used as a single click
1659 * event, this will give old coords, since the distance is checked above, using new coords should be ok. */
1660 // event->x = win->eventstate->prevclickx;
1661 // event->y = win->eventstate->prevclicky;
1662 action |= wm_handlers_do(C, event, handlers);
1665 if (wm_action_not_handled(action)) {
1666 event->val = KM_CLICK;
1667 action |= wm_handlers_do(C, event, handlers);
1671 /* revert value if not handled */
1672 if (wm_action_not_handled(action)) {
1673 event->val = KM_RELEASE;
1678 if(action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL))
1679 wm_cursor_arrow_move(CTX_wm_window(C), event);
1684 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1686 if(wm_event_always_pass(event))
1688 if(BLI_in_rcti(rect, event->x, event->y))
1690 if(event->type==MOUSEMOVE) {
1691 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1699 static ScrArea *area_event_inside(bContext *C, int x, int y)
1701 bScreen *screen= CTX_wm_screen(C);
1705 for(sa= screen->areabase.first; sa; sa= sa->next)
1706 if(BLI_in_rcti(&sa->totrct, x, y))
1711 static ARegion *region_event_inside(bContext *C, int x, int y)
1713 bScreen *screen= CTX_wm_screen(C);
1714 ScrArea *area= CTX_wm_area(C);
1718 for(ar= area->regionbase.first; ar; ar= ar->next)
1719 if(BLI_in_rcti(&ar->winrct, x, y))
1724 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1727 for(; pc; pc= pc->next) {
1728 if(pc->poll == NULL || pc->poll(C)) {
1729 wmWindow *win= CTX_wm_window(C);
1730 win->screen->do_draw_paintcursor= 1;
1731 wm_tag_redraw_overlay(win, ar);
1737 /* called on mousemove, check updates for paintcursors */
1738 /* context was set on active area and region */
1739 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1741 wmWindowManager *wm= CTX_wm_manager(C);
1743 if(wm->paintcursors.first) {
1744 ARegion *ar= CTX_wm_region(C);
1747 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1749 /* if previous position was not in current region, we have to set a temp new context */
1750 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1751 ScrArea *sa= CTX_wm_area(C);
1753 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1754 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1756 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1758 CTX_wm_area_set(C, sa);
1759 CTX_wm_region_set(C, ar);
1764 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1766 if(wm->drags.first==NULL) return;
1768 if(event->type==MOUSEMOVE)
1769 win->screen->do_draw_drag= 1;
1770 else if(event->type==ESCKEY) {
1771 BLI_freelistN(&wm->drags);
1772 win->screen->do_draw_drag= 1;
1774 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1775 event->type= EVT_DROP;
1777 /* create customdata, first free existing */
1778 if(event->customdata) {
1779 if(event->customdatafree)
1780 MEM_freeN(event->customdata);
1783 event->custom= EVT_DATA_LISTBASE;
1784 event->customdata= &wm->drags;
1785 event->customdatafree= 1;
1787 /* clear drop icon */
1788 win->screen->do_draw_drag= 1;
1790 /* restore cursor (disabled, see wm_dragdrop.c) */
1791 // WM_cursor_restore(win);
1794 /* overlap fails otherwise */
1795 if(win->screen->do_draw_drag)
1796 if(win->drawmethod == USER_DRAW_OVERLAP)
1797 win->screen->do_draw= 1;
1801 /* called in main loop */
1802 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1803 void wm_event_do_handlers(bContext *C)
1805 wmWindowManager *wm= CTX_wm_manager(C);
1808 /* update key configuration before handling events */
1809 WM_keyconfig_update(wm);
1811 for(win= wm->windows.first; win; win= win->next) {
1814 if( win->screen==NULL )
1815 wm_event_free_all(win);
1817 Scene* scene = win->screen->scene;
1820 int playing = sound_scene_playing(win->screen->scene);
1823 CTX_wm_window_set(C, win);
1824 CTX_wm_screen_set(C, win->screen);
1825 CTX_data_scene_set(C, scene);
1827 if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer))){
1828 ED_screen_animation_play(C, -1, 1);
1832 float time = sound_sync_scene(scene);
1834 int ncfra = sound_sync_scene(scene) * (float)FPS + 0.5f;
1835 if(ncfra != scene->r.cfra) {
1836 scene->r.cfra = ncfra;
1837 ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
1838 WM_event_add_notifier(C, NC_WINDOW, NULL);
1843 CTX_data_scene_set(C, NULL);
1844 CTX_wm_screen_set(C, NULL);
1845 CTX_wm_window_set(C, NULL);
1850 while( (event= win->queue.first) ) {
1851 int action = WM_HANDLER_CONTINUE;
1853 if((G.f & G_DEBUG) && event && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1854 printf("pass on evt %d val %d\n", event->type, event->val);
1856 wm_eventemulation(event);
1858 CTX_wm_window_set(C, win);
1860 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1861 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1862 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1864 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1865 wm_window_make_drawable(C, win);
1867 wm_region_mouse_co(C, event);
1869 /* first we do priority handlers, modal + some limited keymaps */
1870 action |= wm_handlers_do(C, event, &win->modalhandlers);
1873 if(CTX_wm_window(C)==NULL)
1876 /* check dragging, creates new event or frees, adds draw tag */
1877 wm_event_drag_test(wm, win, event);
1879 /* builtin tweak, if action is break it removes tweak */
1880 wm_tweakevent_test(C, event, action);
1882 if((action & WM_HANDLER_BREAK) == 0) {
1887 /* Note: setting subwin active should be done here, after modal handlers have been done */
1888 if(event->type==MOUSEMOVE) {
1889 /* state variables in screen, cursors. Also used in wm_draw.c, fails for modal handlers though */
1890 ED_screen_set_subwinactive(C, event);
1891 /* for regions having custom cursors */
1892 wm_paintcursor_test(C, event);
1894 else if (event->type==NDOF_MOTION) {
1895 win->addmousemove = TRUE;
1898 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1899 if(wm_event_inside_i(event, &sa->totrct)) {
1900 CTX_wm_area_set(C, sa);
1902 if((action & WM_HANDLER_BREAK) == 0) {
1903 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1904 if(wm_event_inside_i(event, &ar->winrct)) {
1905 CTX_wm_region_set(C, ar);
1907 /* call even on non mouse events, since the */
1908 wm_region_mouse_co(C, event);
1910 /* does polls for drop regions and checks uibuts */
1911 /* need to be here to make sure region context is true */
1912 if(ELEM(event->type, MOUSEMOVE, EVT_DROP)) {
1913 wm_drags_check_ops(C, event);
1916 action |= wm_handlers_do(C, event, &ar->handlers);
1918 /* fileread case (python), [#29489] */
1919 if(CTX_wm_window(C)==NULL)
1922 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1924 if(action & WM_HANDLER_BREAK)
1930 CTX_wm_region_set(C, NULL);
1932 if((action & WM_HANDLER_BREAK) == 0) {
1933 wm_region_mouse_co(C, event); /* only invalidates event->mval in this case */
1934 action |= wm_handlers_do(C, event, &sa->handlers);
1936 CTX_wm_area_set(C, NULL);
1938 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1942 if((action & WM_HANDLER_BREAK) == 0) {
1943 /* also some non-modal handlers need active area/region */
1944 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1945 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1947 wm_region_mouse_co(C, event);
1949 action |= wm_handlers_do(C, event, &win->handlers);
1952 if(CTX_wm_window(C)==NULL)
1956 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1957 doing it on ghost queue gives errors when mousemoves go over area borders */
1958 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1959 win->eventstate->prevx= event->x;
1960 win->eventstate->prevy= event->y;
1961 //printf("win->eventstate->prev = %d %d\n", event->x, event->y);
1964 ;//printf("not setting prev to %d %d\n", event->x, event->y);
1967 /* store last event for this window */
1968 /* mousemove and timer events don't overwrite last type */
1969 if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) && !ISTIMER(event->type)) {
1970 if (wm_action_not_handled(action)) {
1971 if (win->eventstate->prevtype == event->type) {
1972 /* set click time on first click (press -> release) */
1973 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1974 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1975 win->eventstate->prevclickx = event->x;
1976 win->eventstate->prevclicky = event->y;
1979 /* reset click time if event type not the same */
1980 win->eventstate->prevclicktime = 0;
1983 win->eventstate->prevval = event->val;
1984 win->eventstate->prevtype = event->type;
1985 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1986 win->eventstate->prevtype = event->type;
1987 win->eventstate->prevval = event->val;
1988 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1989 win->eventstate->prevclickx = event->x;
1990 win->eventstate->prevclicky = event->y;
1991 } else { /* reset if not */
1992 win->eventstate->prevtype = -1;
1993 win->eventstate->prevval = 0;
1994 win->eventstate->prevclicktime = 0;
1998 /* unlink and free here, blender-quit then frees all */
1999 BLI_remlink(&win->queue, event);
2000 wm_event_free(event);
2004 /* only add mousemove when queue was read entirely */
2005 if(win->addmousemove && win->eventstate) {
2006 wmEvent tevent= *(win->eventstate);
2007 //printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
2008 tevent.type= MOUSEMOVE;
2009 tevent.prevx= tevent.x;
2010 tevent.prevy= tevent.y;
2011 wm_event_add(win, &tevent);
2012 win->addmousemove= 0;
2015 CTX_wm_window_set(C, NULL);
2018 /* update key configuration after handling events */
2019 WM_keyconfig_update(wm);
2022 /* ********** filesector handling ************ */
2024 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
2026 /* add to all windows! */
2029 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
2030 wmEvent event= *win->eventstate;
2032 event.type= EVT_FILESELECT;
2033 event.val= eventval;
2034 event.customdata= ophandle; // only as void pointer type check
2036 wm_event_add(win, &event);
2040 /* operator is supposed to have a filled "path" property */
2041 /* optional property: filetype (XXX enum?) */
2043 /* Idea is to keep a handler alive on window queue, owning the operator.
2044 The filewindow can send event to make it execute, thus ensuring
2045 executing happens outside of lower level queues, with UI refreshed.
2046 Should also allow multiwin solutions */
2048 void WM_event_add_fileselect(bContext *C, wmOperator *op)
2050 wmEventHandler *handler, *handlernext;
2051 wmWindow *win= CTX_wm_window(C);
2052 int full= 1; // XXX preset?
2054 /* only allow 1 file selector open per window */
2055 for(handler= win->modalhandlers.first; handler; handler=handlernext) {
2056 handlernext= handler->next;
2058 if(handler->type == WM_HANDLER_FILESELECT) {
2060 WM_operator_free(handler->op);
2061 BLI_remlink(&win->modalhandlers, handler);
2062 wm_event_free_handler(handler);
2066 handler = MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
2068 handler->type= WM_HANDLER_FILESELECT;
2070 handler->op_area= CTX_wm_area(C);
2071 handler->op_region= CTX_wm_region(C);
2072 handler->filescreen= CTX_wm_screen(C);
2074 BLI_addhead(&win->modalhandlers, handler);
2076 /* check props once before invoking if check is available
2077 * ensures initial properties are valid */
2078 if(op->type->check) {
2079 op->type->check(C, op); /* ignore return value */
2082 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
2086 /* lets not expose struct outside wm? */
2087 static void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
2089 handler->flag= flag;
2093 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
2095 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
2096 wmWindow *win= CTX_wm_window(C);
2098 /* operator was part of macro */
2100 /* give the mother macro to the handler */
2101 handler->op= op->opm;
2102 /* mother macro opm becomes the macro element */
2103 handler->op->opm= op;
2108 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
2109 handler->op_region= CTX_wm_region(C);
2111 BLI_addhead(&win->modalhandlers, handler);
2116 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
2118 wmEventHandler *handler;
2121 printf("WM_event_add_keymap_handler called with NULL keymap\n");
2125 /* only allow same keymap once */
2126 for(handler= handlers->first; handler; handler= handler->next)
2127 if(handler->keymap==keymap)
2130 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
2131 BLI_addtail(handlers, handler);
2132 handler->keymap= keymap;
2137 /* priorities not implemented yet, for time being just insert in begin of list */
2138 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
2140 wmEventHandler *handler;
2142 WM_event_remove_keymap_handler(handlers, keymap);
2144 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
2145 BLI_addhead(handlers, handler);
2146 handler->keymap= keymap;
2151 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
2153 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
2156 handler->bblocal= bblocal;
2157 handler->bbwin= bbwin;
2162 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
2164 wmEventHandler *handler;
2166 for(handler= handlers->first; handler; handler= handler->next) {
2167 if(handler->keymap==keymap) {
2168 BLI_remlink(handlers, handler);
2169 wm_event_free_handler(handler);
2175 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
2177 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
2178 handler->ui_handle= func;
2179 handler->ui_remove= remove;
2180 handler->ui_userdata= userdata;
2181 handler->ui_area= (C)? CTX_wm_area(C): NULL;
2182 handler->ui_region= (C)? CTX_wm_region(C): NULL;
2183 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
2185 BLI_addhead(handlers, handler);
2190 /* set "postpone" for win->modalhandlers, this is in a running for() loop in wm_handlers_do() */
2191 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata, int postpone)
2193 wmEventHandler *handler;
2195 for(handler= handlers->first; handler; handler= handler->next) {
2196 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
2197 /* handlers will be freed in wm_handlers_do() */
2199 handler->flag |= WM_HANDLER_DO_FREE;
2202 BLI_remlink(handlers, handler);
2203 wm_event_free_handler(handler);
2210 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
2212 wmEventHandler *handler;
2214 /* only allow same dropbox once */
2215 for(handler= handlers->first; handler; handler= handler->next)
2216 if(handler->dropboxes==dropboxes)
2219 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
2221 /* dropbox stored static, no free or copy */
2222 handler->dropboxes= dropboxes;
2223 BLI_addhead(handlers, handler);
2228 /* XXX solution works, still better check the real cause (ton) */
2229 void WM_event_remove_area_handler(ListBase *handlers, void *area)
2231 wmEventHandler *handler, *nexthandler;
2233 for(handler = handlers->first; handler; handler= nexthandler) {
2234 nexthandler = handler->next;
2235 if (handler->type != WM_HANDLER_FILESELECT) {
2236 if (handler->ui_area == area) {
2237 BLI_remlink(handlers, handler);
2238 wm_event_free_handler(handler);
2245 static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
2247 BLI_remlink(handlers, handler);
2248 wm_event_free_handler(handler);
2252 void WM_event_add_mousemove(bContext *C)
2254 wmWindow *window= CTX_wm_window(C);
2256 window->addmousemove= 1;
2259 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
2260 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
2262 /* if the release-confirm userpref setting is enabled,
2263 * tweak events can be cancelled when mouse is released
2265 if (U.flag & USER_RELEASECONFIRM) {
2266 /* option on, so can exit with km-release */
2267 if (evt->val == KM_RELEASE) {
2268 switch (tweak_event) {
2276 /* if the initial event wasn't a tweak event then
2277 * ignore USER_RELEASECONFIRM setting: see [#26756] */
2278 if(ELEM3(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) {
2284 /* this is fine as long as not doing km-release, otherwise
2285 * some items (i.e. markers) being tweaked may end up getting
2288 if (evt->val != KM_RELEASE)
2295 /* ********************* ghost stuff *************** */
2297 static int convert_key(GHOST_TKey key)
2299 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
2300 return (AKEY + ((int) key - GHOST_kKeyA));
2301 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
2302 return (ZEROKEY + ((int) key - GHOST_kKey0));
2303 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
2304 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
2305 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF19) {
2306 return (F1KEY + ((int) key - GHOST_kKeyF1));
2309 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
2310 case GHOST_kKeyTab: return TABKEY;
2311 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
2312 case GHOST_kKeyClear: return 0;
2313 case GHOST_kKeyEnter: return RETKEY;
2315 case GHOST_kKeyEsc: return ESCKEY;
2316 case GHOST_kKeySpace: return SPACEKEY;
2317 case GHOST_kKeyQuote: return QUOTEKEY;
2318 case GHOST_kKeyComma: return COMMAKEY;
2319 case GHOST_kKeyMinus: return MINUSKEY;
2320 case GHOST_kKeyPeriod: return PERIODKEY;
2321 case GHOST_kKeySlash: return SLASHKEY;
2323 case GHOST_kKeySemicolon: return SEMICOLONKEY;
2324 case GHOST_kKeyEqual: return EQUALKEY;
2326 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
2327 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
2328 case GHOST_kKeyBackslash: return BACKSLASHKEY;
2329 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
2331 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
2332 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
2333 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
2334 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
2335 case GHOST_kKeyOS: return OSKEY;
2336 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
2337 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
2339 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
2340 case GHOST_kKeyNumLock: return 0;
2341 case GHOST_kKeyScrollLock: return 0;
2343 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
2344 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
2345 case GHOST_kKeyUpArrow: return UPARROWKEY;
2346 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
2348 case GHOST_kKeyPrintScreen: return 0;
2349 case GHOST_kKeyPause: return PAUSEKEY;
2351 case GHOST_kKeyInsert: return INSERTKEY;
2352 case GHOST_kKeyDelete: return DELKEY;
2353 case GHOST_kKeyHome: return HOMEKEY;
2354 case GHOST_kKeyEnd: return ENDKEY;
2355 case GHOST_kKeyUpPage: return PAGEUPKEY;
2356 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
2358 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
2359 case GHOST_kKeyNumpadEnter: return PADENTER;
2360 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
2361 case GHOST_kKeyNumpadMinus: return PADMINUS;
2362 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
2363 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
2365 case GHOST_kKeyGrLess: return GRLESSKEY;
2367 case GHOST_kKeyMediaPlay: return MEDIAPLAY;
2368 case GHOST_kKeyMediaStop: return MEDIASTOP;
2369 case GHOST_kKeyMediaFirst: return MEDIAFIRST;
2370 case GHOST_kKeyMediaLast: return MEDIALAST;
2373 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
2378 /* adds customdata to event */
2379 static void update_tablet_data(wmWindow *win, wmEvent *event)
2381 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2383 /* if there's tablet data from an active tablet device then add it */
2384 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2385 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2387 wmtab->Active = (int)td->Active;
2388 wmtab->Pressure = td->Pressure;
2389 wmtab->Xtilt = td->Xtilt;
2390 wmtab->Ytilt = td->Ytilt;
2392 event->custom= EVT_DATA_TABLET;
2393 event->customdata= wmtab;
2394 event->customdatafree= 1;
2398 /* adds customdata to event */
2399 static void attach_ndof_data(wmEvent* event, const GHOST_TEventNDOFMotionData* ghost)
2401 wmNDOFMotionData* data = MEM_mallocN(sizeof(wmNDOFMotionData), "customdata NDOF");
2403 const float s = U.ndof_sensitivity;
2405 data->tx = s * ghost->tx;
2407 data->rx = s * ghost->rx;
2408 data->ry = s * ghost->ry;
2409 data->rz = s * ghost->rz;
2411 if (U.ndof_flag & NDOF_ZOOM_UPDOWN)
2413 /* rotate so Y is where Z was */
2414 data->ty = s * ghost->tz;
2415 data->tz = s * ghost->ty;
2416 /* maintain handed-ness? or just do what feels right? */
2418 /* should this affect rotation also?
2419 * initial guess is 'yes', but get user feedback immediately!
2422 /* after turning this on, my guess becomes 'no' */
2423 data->ry = s * ghost->rz;
2424 data->rz = s * ghost->ry;
2429 data->ty = s * ghost->ty;
2430 data->tz = s * ghost->tz;
2433 data->dt = ghost->dt;
2435 data->progress = (wmProgress) ghost->progress;
2437 event->custom = EVT_DATA_NDOF_MOTION;
2438 event->customdata = data;
2439 event->customdatafree = 1;
2442 /* imperfect but probably usable... draw/enable drags to other windows */
2443 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2445 int mx= evt->x, my= evt->y;
2447 if(wm->windows.first== wm->windows.last)
2450 /* top window bar... */
2451 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
2453 wmEventHandler *handler;
2455 /* let's skip windows having modal handlers now */
2456 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2457 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2458 if(handler->ui_handle || handler->op)
2461 /* to desktop space */
2462 mx += (int)win->posx;
2463 my += (int)win->posy;
2465 /* check other windows to see if it has mouse inside */
2466 for(owin= wm->windows.first; owin; owin= owin->next) {
2469 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2470 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2471 evt->x= mx - (int)owin->posx;
2472 evt->y= my - (int)owin->posy;
2482 /* windows store own event queues, no bContext here */
2483 /* time is in 1000s of seconds, from ghost */
2484 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int UNUSED(time), void *customdata)
2487 wmEvent event, *evt= win->eventstate;
2489 /* initialize and copy state (only mouse x y and modifiers) */
2494 case GHOST_kEventCursorMove: {
2496 GHOST_TEventCursorData *cd= customdata;
2497 wmEvent *lastevent= win->queue.last;
2500 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2502 evt->y= (win->sizey-1) - cy;
2507 event.type= MOUSEMOVE;
2509 /* some painting operators want accurate mouse events, they can
2510 handle in between mouse move moves, others can happily ignore
2511 them for better performance */
2512 if(lastevent && lastevent->type == MOUSEMOVE)
2513 lastevent->type = INBETWEEN_MOUSEMOVE;
2515 update_tablet_data(win, &event);
2516 wm_event_add(win, &event);
2518 //printf("sending MOUSEMOVE %d %d\n", event.x, event.y);
2520 /* also add to other window if event is there, this makes overdraws disappear nicely */
2521 /* it remaps mousecoord to other window in event */
2522 owin= wm_event_cursor_other_windows(wm, win, &event);
2524 wmEvent oevent= *(owin->eventstate);
2526 oevent.x=owin->eventstate->x= event.x;
2527 oevent.y=owin->eventstate->y= event.y;
2528 oevent.type= MOUSEMOVE;
2530 update_tablet_data(owin, &oevent);
2531 wm_event_add(owin, &oevent);
2537 case GHOST_kEventTrackpad: {
2538 GHOST_TEventTrackpadData * pd = customdata;
2539 switch (pd->subtype) {
2540 case GHOST_kTrackpadEventMagnify:
2541 event.type = MOUSEZOOM;
2543 case GHOST_kTrackpadEventRotate:
2544 event.type = MOUSEROTATE;
2546 case GHOST_kTrackpadEventScroll:
2548 event.type= MOUSEPAN;
2554 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2555 event.x= evt->x= cx;
2556 event.y= evt->y= (win->sizey-1) - cy;
2559 // Use prevx/prevy so we can calculate the delta later
2560 event.prevx= event.x - pd->deltaX;
2561 event.prevy= event.y - (-pd->deltaY);
2563 update_tablet_data(win, &event);
2564 wm_event_add(win, &event);
2568 case GHOST_kEventButtonDown:
2569 case GHOST_kEventButtonUp: {
2570 GHOST_TEventButtonData *bd= customdata;
2571 event.val= (type==GHOST_kEventButtonDown) ? KM_PRESS:KM_RELEASE; /* Note!, this starts as 0/1 but later is converted to KM_PRESS/KM_RELEASE by tweak */
2573 if (bd->button == GHOST_kButtonMaskLeft)
2574 event.type= LEFTMOUSE;
2575 else if (bd->button == GHOST_kButtonMaskRight)
2576 event.type= RIGHTMOUSE;
2577 else if (bd->button == GHOST_kButtonMaskButton4)
2578 event.type= BUTTON4MOUSE;
2579 else if (bd->button == GHOST_kButtonMaskButton5)
2580 event.type= BUTTON5MOUSE;
2582 event.type= MIDDLEMOUSE;
2584 if(win->active==0) {
2587 /* entering window, update mouse pos. (ghost sends win-activate *after* the mouseclick in window!) */
2588 wm_get_cursor_position(win, &cx, &cy);
2590 event.x= evt->x= cx;
2591 event.y= evt->y= cy;
2594 /* add to other window if event is there (not to both!) */
2595 owin= wm_event_cursor_other_windows(wm, win, &event);
2597 wmEvent oevent= *(owin->eventstate);
2601 oevent.type= event.type;
2602 oevent.val= event.val;
2604 update_tablet_data(owin, &oevent);
2605 wm_event_add(owin, &oevent);
2608 update_tablet_data(win, &event);
2609 wm_event_add(win, &event);
2615 case GHOST_kEventKeyDown:
2616 case GHOST_kEventKeyUp: {
2617 GHOST_TEventKeyData *kd= customdata;
2618 event.type= convert_key(kd->key);
2619 event.ascii= kd->ascii;
2620 strcpy(event.utf8_buf, kd->utf8_buf);
2621 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2623 /* exclude arrow keys, esc, etc from text input */
2624 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2628 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2629 event.shift= evt->shift= (event.val==KM_PRESS);
2630 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2631 event.shift= evt->shift = 3; // define?
2633 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2634 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2635 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2636 event.ctrl= evt->ctrl = 3; // define?
2638 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2639 event.alt= evt->alt= (event.val==KM_PRESS);
2640 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2641 event.alt= evt->alt = 3; // define?
2643 else if (event.type==OSKEY) {
2644 event.oskey= evt->oskey= (event.val==KM_PRESS);
2645 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2646 event.oskey= evt->oskey = 3; // define?
2649 if(event.val==KM_PRESS && event.keymodifier==0)
2650 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2651 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2652 event.keymodifier= evt->keymodifier= 0;
2655 /* this case happens on some systems that on holding a key pressed,
2656 generate press events without release, we still want to keep the
2657 modifier in win->eventstate, but for the press event of the same
2658 key we don't want the key modifier */
2659 if(event.keymodifier == event.type)
2660 event.keymodifier= 0;
2662 /* if test_break set, it catches this. XXX Keep global for now? */
2663 if(event.type==ESCKEY)
2666 wm_event_add(win, &event);
2671 case GHOST_kEventWheel: {
2672 GHOST_TEventWheelData* wheelData = customdata;
2674 if (wheelData->z > 0)
2675 event.type= WHEELUPMOUSE;
2677 event.type= WHEELDOWNMOUSE;
2679 event.val= KM_PRESS;
2680 wm_event_add(win, &event);
2684 case GHOST_kEventTimer: {
2686 event.custom= EVT_DATA_TIMER;