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
38 #include "DNA_listBase.h"
39 #include "DNA_screen_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_windowmanager_types.h"
42 #include "DNA_userdef_types.h"
44 #include "MEM_guardedalloc.h"
46 #include "GHOST_C-api.h"
48 #include "BLI_blenlib.h"
49 #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_screen.h"
65 #include "ED_view3d.h"
68 #include "RNA_access.h"
70 #include "UI_interface.h"
77 #include "wm_window.h"
78 #include "wm_event_system.h"
79 #include "wm_event_types.h"
82 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only);
84 /* ************ event management ************** */
86 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
88 wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
90 *event= *event_to_add;
91 BLI_addtail(&win->queue, event);
94 void wm_event_free(wmEvent *event)
96 if(event->customdata) {
97 if(event->customdatafree) {
98 /* note: pointer to listbase struct elsewhere */
99 if(event->custom==EVT_DATA_LISTBASE)
100 BLI_freelistN(event->customdata);
102 MEM_freeN(event->customdata);
108 void wm_event_free_all(wmWindow *win)
112 while((event= win->queue.first)) {
113 BLI_remlink(&win->queue, event);
114 wm_event_free(event);
118 /* ********************* notifiers, listeners *************** */
120 static int wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference)
124 for(note=wm->queue.first; note; note=note->next)
125 if((note->category|note->data|note->subtype|note->action) == type && note->reference == reference)
131 /* XXX: in future, which notifiers to send to other windows? */
132 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
134 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
136 note->wm= CTX_wm_manager(C);
137 BLI_addtail(¬e->wm->queue, note);
139 note->window= CTX_wm_window(C);
142 note->swinid= CTX_wm_region(C)->swinid;
144 note->category= type & NOTE_CATEGORY;
145 note->data= type & NOTE_DATA;
146 note->subtype= type & NOTE_SUBTYPE;
147 note->action= type & NOTE_ACTION;
149 note->reference= reference;
152 void WM_main_add_notifier(unsigned int type, void *reference)
155 wmWindowManager *wm= bmain->wm.first;
157 if(wm && !wm_test_duplicate_notifier(wm, type, reference)) {
158 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
161 BLI_addtail(¬e->wm->queue, note);
163 note->category= type & NOTE_CATEGORY;
164 note->data= type & NOTE_DATA;
165 note->subtype= type & NOTE_SUBTYPE;
166 note->action= type & NOTE_ACTION;
168 note->reference= reference;
172 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
174 wmNotifier *note= wm->queue.first;
176 if(note) BLI_remlink(&wm->queue, note);
180 /* called in mainloop */
181 void wm_event_do_notifiers(bContext *C)
183 wmWindowManager *wm= CTX_wm_manager(C);
184 wmNotifier *note, *next;
186 unsigned int win_combine_v3d_datamask= 0;
191 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
192 for(win= wm->windows.first; win; win= win->next) {
195 CTX_wm_window_set(C, win);
197 for(note= wm->queue.first; note; note= next) {
200 if(note->category==NC_WM) {
201 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
203 wm_window_title(wm, win);
205 else if(note->data==ND_DATACHANGED)
206 wm_window_title(wm, win);
208 if(note->window==win) {
209 if(note->category==NC_SCREEN) {
210 if(note->data==ND_SCREENBROWSE) {
211 ED_screen_set(C, note->reference); // XXX hrms, think this over!
213 printf("screen set %p\n", note->reference);
215 else if(note->data==ND_SCREENDELETE) {
216 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
218 printf("screen delete %p\n", note->reference);
223 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
224 if(note->category==NC_SCENE) {
225 if(note->data==ND_SCENEBROWSE) {
226 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
228 printf("scene set %p\n", note->reference);
230 else if(note->data==ND_FRAME)
233 if(note->action == NA_REMOVED) {
234 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
236 printf("scene delete %p\n", note->reference);
241 if(ELEM5(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE, NC_WM)) {
242 ED_info_stats_clear(CTX_data_scene(C));
243 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
248 /* XXX, quick frame changes can cause a crash if framechange and rendering
249 * collide (happens on slow scenes), scene_update_for_newframe can be called
250 * twice which can depgraph update the same object at once */
253 /* depsgraph gets called, might send more notifiers */
254 ED_update_for_newframe(CTX_data_main(C), win->screen->scene, win->screen, 1);
259 /* the notifiers are sent without context, to keep it clean */
260 while( (note=wm_notifier_next(wm)) ) {
261 for(win= wm->windows.first; win; win= win->next) {
263 /* filter out notifiers */
264 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
265 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
270 /* XXX context in notifiers? */
271 CTX_wm_window_set(C, win);
273 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
274 ED_screen_do_listen(C, note);
276 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
277 ED_region_do_listen(ar, note);
280 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
281 ED_area_do_listen(sa, note);
282 for(ar=sa->regionbase.first; ar; ar= ar->next) {
283 ED_region_do_listen(ar, note);
292 /* combine datamasks so 1 win doesn't disable UV's in another [#26448] */
293 for(win= wm->windows.first; win; win= win->next) {
294 win_combine_v3d_datamask |= ED_viewedit_datamask(win->screen);
297 /* cached: editor refresh callbacks now, they get context */
298 for(win= wm->windows.first; win; win= win->next) {
301 CTX_wm_window_set(C, win);
302 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
304 CTX_wm_area_set(C, sa);
305 ED_area_do_refresh(C, sa);
309 /* XXX make lock in future, or separated derivedmesh users in scene */
311 /* depsgraph & animation: update tagged datablocks */
313 /* copied to set's in scene_update_tagged_recursive() */
314 win->screen->scene->customdata_mask= win_combine_v3d_datamask;
316 /* XXX, hack so operators can enforce datamasks [#26482], gl render */
317 win->screen->scene->customdata_mask |= win->screen->scene->customdata_mask_modal;
319 scene_update_tagged(CTX_data_main(C), win->screen->scene);
323 CTX_wm_window_set(C, NULL);
326 static int wm_event_always_pass(wmEvent *event)
328 /* some events we always pass on, to ensure proper communication */
329 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
332 /* ********************* ui handler ******************* */
334 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
336 ScrArea *area= CTX_wm_area(C);
337 ARegion *region= CTX_wm_region(C);
338 ARegion *menu= CTX_wm_menu(C);
339 static int do_wheel_ui= 1;
340 int is_wheel= ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE);
343 /* UI is quite aggressive with swallowing events, like scrollwheel */
344 /* I realize this is not extremely nice code... when UI gets keymaps it can be maybe smarter */
347 return WM_HANDLER_CONTINUE;
348 else if(wm_event_always_pass(event)==0)
352 /* we set context to where ui handler came from */
353 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
354 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
355 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
357 retval= handler->ui_handle(C, event, handler->ui_userdata);
359 /* putting back screen context */
360 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
361 CTX_wm_area_set(C, area);
362 CTX_wm_region_set(C, region);
363 CTX_wm_menu_set(C, menu);
366 /* this special cases is for areas and regions that get removed */
367 CTX_wm_area_set(C, NULL);
368 CTX_wm_region_set(C, NULL);
369 CTX_wm_menu_set(C, NULL);
372 if(retval == WM_UI_HANDLER_BREAK)
373 return WM_HANDLER_BREAK;
375 /* event not handled in UI, if wheel then we temporarily disable it */
379 return WM_HANDLER_CONTINUE;
382 static void wm_handler_ui_cancel(bContext *C)
384 wmWindow *win= CTX_wm_window(C);
385 ARegion *ar= CTX_wm_region(C);
386 wmEventHandler *handler, *nexthandler;
391 for(handler= ar->handlers.first; handler; handler= nexthandler) {
392 nexthandler= handler->next;
394 if(handler->ui_handle) {
395 wmEvent event= *(win->eventstate);
396 event.type= EVT_BUT_CANCEL;
397 handler->ui_handle(C, &event, handler->ui_userdata);
402 /* ********************* operators ******************* */
404 int WM_operator_poll(bContext *C, wmOperatorType *ot)
406 wmOperatorTypeMacro *otmacro;
408 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
409 wmOperatorType *ot_macro= WM_operatortype_find(otmacro->idname, 0);
411 if(0==WM_operator_poll(C, ot_macro))
415 /* python needs operator type, so we added exception for it */
417 return ot->pyop_poll(C, ot);
424 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
425 int WM_operator_poll_context(bContext *C, wmOperatorType *ot, int context)
427 return wm_operator_call_internal(C, ot, NULL, NULL, context, TRUE);
430 static void wm_operator_print(bContext *C, wmOperator *op)
432 /* context is needed for enum function */
433 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
438 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, int popup)
441 if(op->reports->list.first)
442 uiPupMenuReports(C, op->reports);
444 if(retval & OPERATOR_FINISHED) {
446 wm_operator_print(C, op); /* todo - this print may double up, might want to check more flags then the FINISHED */
448 BKE_reports_print(op->reports, RPT_DEBUG); /* print out reports to console. */
449 if (op->type->flag & OPTYPE_REGISTER) {
450 if(G.background == 0) { /* ends up printing these in the terminal, gets annoying */
451 /* Report the python string representation of the operator */
452 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
453 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
459 /* if the caller owns them them handle this */
460 if (op->reports->list.first && (op->reports->flag & RPT_OP_HOLD) == 0) {
462 wmWindowManager *wm = CTX_wm_manager(C);
463 ReportList *wm_reports= CTX_wm_reports(C);
464 ReportTimerInfo *rti;
466 /* add reports to the global list, otherwise they are not seen */
467 BLI_movelisttolist(&wm_reports->list, &op->reports->list);
469 /* After adding reports to the global list, reset the report timer. */
470 WM_event_remove_timer(wm, NULL, wm_reports->reporttimer);
472 /* Records time since last report was added */
473 wm_reports->reporttimer= WM_event_add_timer(wm, CTX_wm_window(C), TIMERREPORT, 0.05);
475 rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
476 wm_reports->reporttimer->customdata = rti;
480 /* this function is mainly to check that the rules for freeing
481 * an operator are kept in sync.
483 static int wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
485 return wm && (wm->op_undo_depth == 0) && (ot->flag & OPTYPE_REGISTER);
488 static void wm_operator_finished(bContext *C, wmOperator *op, int repeat)
490 wmWindowManager *wm= CTX_wm_manager(C);
492 op->customdata= NULL;
494 /* we don't want to do undo pushes for operators that are being
495 called from operators that already do an undo push. usually
496 this will happen for python operators that call C operators */
497 if(wm->op_undo_depth == 0)
498 if(op->type->flag & OPTYPE_UNDO)
499 ED_undo_push_op(C, op);
503 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
504 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
508 if(wm_operator_register_check(wm, op->type))
509 wm_operator_register(C, op);
511 WM_operator_free(op);
515 /* if repeat is true, it doesn't register again, nor does it free */
516 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
518 wmWindowManager *wm= CTX_wm_manager(C);
519 int retval= OPERATOR_CANCELLED;
521 CTX_wm_operator_poll_msg_set(C, NULL);
523 if(op==NULL || op->type==NULL)
526 if(0==WM_operator_poll(C, op->type))
530 if(op->type->flag & OPTYPE_UNDO)
533 retval= op->type->exec(C, op);
535 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
539 if (retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED) && repeat == 0)
540 wm_operator_reports(C, op, retval, 0);
542 if(retval & OPERATOR_FINISHED)
543 wm_operator_finished(C, op, repeat);
545 WM_operator_free(op);
547 return retval | OPERATOR_HANDLED;
551 /* for running operators with frozen context (modal handlers, menus) */
552 int WM_operator_call(bContext *C, wmOperator *op)
554 return wm_operator_exec(C, op, 0);
557 /* do this operator again, put here so it can share above code */
558 int WM_operator_repeat(bContext *C, wmOperator *op)
560 return wm_operator_exec(C, op, 1);
562 /* TRUE if WM_operator_repeat can run
563 * simple check for now but may become more involved.
564 * To be sure the operator can run call WM_operator_poll(C, op->type) also, since this call
565 * checks if WM_operator_repeat() can run at all, not that it WILL run at any time. */
566 int WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
568 return op->type->exec != NULL;
571 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
573 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
575 /* XXX adding new operator could be function, only happens here now */
577 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
579 /* initialize properties, either copy or create */
580 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
581 if(properties && properties->data) {
582 op->properties= IDP_CopyProperty(properties->data);
585 IDPropertyTemplate val = {0};
586 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
588 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
590 /* initialize error reports */
592 op->reports= reports; /* must be initialized already */
595 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
596 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
599 /* recursive filling of operator macro list */
600 if(ot->macro.first) {
601 static wmOperator *motherop= NULL;
602 wmOperatorTypeMacro *otmacro;
605 /* ensure all ops are in execution order in 1 list */
612 /* if properties exist, it will contain everything needed */
614 otmacro= ot->macro.first;
616 RNA_STRUCT_BEGIN(properties, prop) {
621 /* skip invalid properties */
622 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
624 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
625 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
626 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
628 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
630 BLI_addtail(&motherop->macro, opm);
631 opm->opm= motherop; /* pointer to mom, for modal() */
633 otmacro= otmacro->next;
638 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
639 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
640 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
642 BLI_addtail(&motherop->macro, opm);
643 opm->opm= motherop; /* pointer to mom, for modal() */
651 WM_operator_properties_sanitize(op->ptr, 0);
656 static void wm_region_mouse_co(bContext *C, wmEvent *event)
658 ARegion *ar= CTX_wm_region(C);
660 /* compatibility convention */
661 event->mval[0]= event->x - ar->winrct.xmin;
662 event->mval[1]= event->y - ar->winrct.ymin;
665 /* these values are invalid (avoid odd behavior by relying on old mval values) */
671 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports, short poll_only)
673 wmWindowManager *wm= CTX_wm_manager(C);
674 int retval= OPERATOR_PASS_THROUGH;
676 /* this is done because complicated setup is done to call this function that is better not duplicated */
678 return WM_operator_poll(C, ot);
680 if(WM_operator_poll(C, ot)) {
681 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
683 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
684 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
686 if(op->type->invoke && event) {
687 wm_region_mouse_co(C, event);
689 if(op->type->flag & OPTYPE_UNDO)
692 retval= op->type->invoke(C, op, event);
694 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
697 else if(op->type->exec) {
698 if(op->type->flag & OPTYPE_UNDO)
701 retval= op->type->exec(C, op);
703 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
707 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
709 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
710 * currently python only uses this */
711 if (!(retval & OPERATOR_HANDLED) && retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
712 /* only show the report if the report list was not given in the function */
713 wm_operator_reports(C, op, retval, (reports==NULL));
715 if(retval & OPERATOR_HANDLED)
716 ; /* do nothing, wm_operator_exec() has been called somewhere */
717 else if(retval & OPERATOR_FINISHED) {
718 wm_operator_finished(C, op, 0);
720 else if(retval & OPERATOR_RUNNING_MODAL) {
721 /* grab cursor during blocking modal ops (X11)
722 * Also check for macro
724 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
725 int bounds[4] = {-1,-1,-1,-1};
729 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
731 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
734 /* exception, cont. grab in header is annoying */
736 ARegion *ar= CTX_wm_region(C);
737 if(ar && ar->regiontype == RGN_TYPE_HEADER) {
744 ARegion *ar= CTX_wm_region(C);
745 ScrArea *sa= CTX_wm_area(C);
747 if(ar && ar->regiontype == RGN_TYPE_WINDOW && event && BLI_in_rcti(&ar->winrct, event->x, event->y)) {
748 winrect= &ar->winrct;
751 winrect= &sa->totrct;
755 bounds[0]= winrect->xmin;
756 bounds[1]= winrect->ymax;
757 bounds[2]= winrect->xmax;
758 bounds[3]= winrect->ymin;
762 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
765 /* cancel UI handlers, typically tooltips that can hang around
766 while dragging the view or worse, that stay there permanently
767 after the modal operator has swallowed all events and passed
768 none to the UI handler */
769 wm_handler_ui_cancel(C);
772 WM_operator_free(op);
778 /* WM_operator_name_call is the main accessor function
779 * this is for python to access since its done the operator lookup
781 * invokes operator in context */
782 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only)
784 wmWindow *window= CTX_wm_window(C);
789 CTX_wm_operator_poll_msg_set(C, NULL);
794 case WM_OP_INVOKE_DEFAULT:
795 case WM_OP_INVOKE_REGION_WIN:
796 case WM_OP_INVOKE_AREA:
797 case WM_OP_INVOKE_SCREEN:
798 /* window is needed for invoke, cancel operator */
802 event= window->eventstate;
810 case WM_OP_EXEC_REGION_WIN:
811 case WM_OP_INVOKE_REGION_WIN:
812 case WM_OP_EXEC_REGION_CHANNELS:
813 case WM_OP_INVOKE_REGION_CHANNELS:
814 case WM_OP_EXEC_REGION_PREVIEW:
815 case WM_OP_INVOKE_REGION_PREVIEW:
817 /* forces operator to go to the region window/channels/preview, for header menus
818 * but we stay in the same region if we are already in one
820 ARegion *ar= CTX_wm_region(C);
821 ScrArea *area= CTX_wm_area(C);
822 int type = RGN_TYPE_WINDOW;
825 case WM_OP_EXEC_REGION_CHANNELS:
826 case WM_OP_INVOKE_REGION_CHANNELS:
827 type = RGN_TYPE_CHANNELS;
830 case WM_OP_EXEC_REGION_PREVIEW:
831 case WM_OP_INVOKE_REGION_PREVIEW:
832 type = RGN_TYPE_PREVIEW;
835 case WM_OP_EXEC_REGION_WIN:
836 case WM_OP_INVOKE_REGION_WIN:
838 type = RGN_TYPE_WINDOW;
842 if(!(ar && ar->regiontype == type) && area) {
843 ARegion *ar1= BKE_area_find_region_type(area, type);
845 CTX_wm_region_set(C, ar1);
848 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
850 /* set region back */
851 CTX_wm_region_set(C, ar);
855 case WM_OP_EXEC_AREA:
856 case WM_OP_INVOKE_AREA:
858 /* remove region from context */
859 ARegion *ar= CTX_wm_region(C);
861 CTX_wm_region_set(C, NULL);
862 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
863 CTX_wm_region_set(C, ar);
867 case WM_OP_EXEC_SCREEN:
868 case WM_OP_INVOKE_SCREEN:
870 /* remove region + area from context */
871 ARegion *ar= CTX_wm_region(C);
872 ScrArea *area= CTX_wm_area(C);
874 CTX_wm_region_set(C, NULL);
875 CTX_wm_area_set(C, NULL);
876 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
877 CTX_wm_region_set(C, ar);
878 CTX_wm_area_set(C, area);
882 case WM_OP_EXEC_DEFAULT:
883 case WM_OP_INVOKE_DEFAULT:
884 return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
892 /* invokes operator in context */
893 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
895 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
897 return wm_operator_call_internal(C, ot, properties, NULL, context, FALSE);
902 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
903 - wmOperatorType is used instead of operator name since python already has the operator type
904 - poll() must be called by python before this runs.
905 - reports can be passed to this function (so python can report them as exceptions)
907 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
909 int retval= OPERATOR_CANCELLED;
913 op= wm_operator_create(wm, ot, properties, reports);
915 if (op->type->exec) {
916 if(op->type->flag & OPTYPE_UNDO)
919 retval= op->type->exec(C, op);
921 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
925 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
928 retval= wm_operator_call_internal(C, ot, properties, reports, context, FALSE);
930 /* keep the reports around if needed later */
931 if ( (retval & OPERATOR_RUNNING_MODAL) ||
932 ((retval & OPERATOR_FINISHED) && wm_operator_register_check(CTX_wm_manager(C), ot))
934 reports->flag |= RPT_FREE; /* let blender manage freeing */
941 /* ********************* handlers *************** */
943 /* future extra customadata free? */
944 void wm_event_free_handler(wmEventHandler *handler)
949 /* only set context when area/region is part of screen */
950 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
952 bScreen *screen= CTX_wm_screen(C);
954 if(screen && handler->op) {
955 if(handler->op_area==NULL)
956 CTX_wm_area_set(C, NULL);
960 for(sa= screen->areabase.first; sa; sa= sa->next)
961 if(sa==handler->op_area)
964 /* when changing screen layouts with running modal handlers (like render display), this
965 is not an error to print */
966 if(handler->op==NULL)
967 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
971 CTX_wm_area_set(C, sa);
972 for(ar= sa->regionbase.first; ar; ar= ar->next)
973 if(ar==handler->op_region)
975 /* XXX no warning print here, after full-area and back regions are remade */
977 CTX_wm_region_set(C, ar);
983 /* called on exit or remove area, only here call cancel callback */
984 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
986 wmEventHandler *handler;
987 wmWindowManager *wm= CTX_wm_manager(C);
989 /* C is zero on freeing database, modal handlers then already were freed */
990 while((handler=handlers->first)) {
991 BLI_remlink(handlers, handler);
994 if(handler->op->type->cancel) {
995 ScrArea *area= CTX_wm_area(C);
996 ARegion *region= CTX_wm_region(C);
998 wm_handler_op_context(C, handler);
1000 if(handler->op->type->flag & OPTYPE_UNDO)
1001 wm->op_undo_depth++;
1003 handler->op->type->cancel(C, handler->op);
1005 if(handler->op->type->flag & OPTYPE_UNDO)
1006 wm->op_undo_depth--;
1008 CTX_wm_area_set(C, area);
1009 CTX_wm_region_set(C, region);
1012 WM_cursor_ungrab(CTX_wm_window(C));
1013 WM_operator_free(handler->op);
1015 else if(handler->ui_remove) {
1016 ScrArea *area= CTX_wm_area(C);
1017 ARegion *region= CTX_wm_region(C);
1018 ARegion *menu= CTX_wm_menu(C);
1020 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
1021 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
1022 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
1024 handler->ui_remove(C, handler->ui_userdata);
1026 CTX_wm_area_set(C, area);
1027 CTX_wm_region_set(C, region);
1028 CTX_wm_menu_set(C, menu);
1031 wm_event_free_handler(handler);
1035 /* do userdef mappings */
1036 int WM_userdef_event_map(int kmitype)
1040 if(U.flag & USER_LMOUSESELECT)
1046 if(U.flag & USER_LMOUSESELECT)
1052 if(U.uiflag & USER_WHEELZOOMDIR)
1053 return WHEELUPMOUSE;
1055 return WHEELDOWNMOUSE;
1058 if(U.uiflag & USER_WHEELZOOMDIR)
1059 return WHEELDOWNMOUSE;
1061 return WHEELUPMOUSE;
1064 if(U.flag & USER_LMOUSESELECT)
1070 if(U.flag & USER_LMOUSESELECT)
1079 static void wm_eventemulation(wmEvent *event)
1081 static int mmb_emulated = 0; /* this should be in a data structure somwhere */
1083 /* middlemouse emulation */
1084 if(U.flag & USER_TWOBUTTONMOUSE) {
1085 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
1086 event->type = MIDDLEMOUSE;
1088 mmb_emulated = event->val;
1093 /* rightmouse emulation */
1094 if(U.flag & USER_TWOBUTTONMOUSE) {
1095 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
1096 event->type = RIGHTMOUSE;
1098 mmb_emulated = event->val;
1103 /* numpad emulation */
1104 if(U.flag & USER_NONUMPAD) {
1105 switch(event->type) {
1106 case ZEROKEY: event->type = PAD0; break;
1107 case ONEKEY: event->type = PAD1; break;
1108 case TWOKEY: event->type = PAD2; break;
1109 case THREEKEY: event->type = PAD3; break;
1110 case FOURKEY: event->type = PAD4; break;
1111 case FIVEKEY: event->type = PAD5; break;
1112 case SIXKEY: event->type = PAD6; break;
1113 case SEVENKEY: event->type = PAD7; break;
1114 case EIGHTKEY: event->type = PAD8; break;
1115 case NINEKEY: event->type = PAD9; break;
1116 case MINUSKEY: event->type = PADMINUS; break;
1117 case EQUALKEY: event->type = PADPLUSKEY; break;
1118 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
1123 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
1125 int kmitype= WM_userdef_event_map(kmi->type);
1127 if(kmi->flag & KMI_INACTIVE) return 0;
1129 /* the matching rules */
1130 if(kmitype==KM_TEXTINPUT)
1131 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
1133 if(winevent->type!=kmitype) return 0;
1135 if(kmi->val!=KM_ANY)
1136 if(winevent->val!=kmi->val) return 0;
1138 /* modifiers also check bits, so it allows modifier order */
1139 if(kmi->shift!=KM_ANY)
1140 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
1141 if(kmi->ctrl!=KM_ANY)
1142 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
1143 if(kmi->alt!=KM_ANY)
1144 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
1145 if(kmi->oskey!=KM_ANY)
1146 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
1148 if(kmi->keymodifier)
1149 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1151 /* key modifiers always check when event has it */
1152 /* otherwise regular keypresses with keymodifier still work */
1153 if(winevent->keymodifier)
1154 if(ISTEXTINPUT(winevent->type))
1155 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1161 /* operator exists */
1162 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1164 /* support for modal keymap in macros */
1168 if(op->type->modalkeymap) {
1169 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1172 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1173 if(wm_eventmatch(event, kmi)) {
1175 event->type= EVT_MODAL_MAP;
1176 event->val= kmi->propvalue;
1182 /* Warning: this function removes a modal handler, when finished */
1183 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1185 int retval= OPERATOR_PASS_THROUGH;
1187 /* derived, modal or blocking operator */
1189 wmOperator *op= handler->op;
1190 wmOperatorType *ot= op->type;
1193 /* we set context to where modal handler came from */
1194 wmWindowManager *wm= CTX_wm_manager(C);
1195 ScrArea *area= CTX_wm_area(C);
1196 ARegion *region= CTX_wm_region(C);
1198 wm_handler_op_context(C, handler);
1199 wm_region_mouse_co(C, event);
1200 wm_event_modalkeymap(C, op, event);
1202 if(ot->flag & OPTYPE_UNDO)
1203 wm->op_undo_depth++;
1205 retval= ot->modal(C, op, event);
1207 if(ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1208 wm->op_undo_depth--;
1210 /* putting back screen context, reval can pass trough after modal failures! */
1211 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1212 CTX_wm_area_set(C, area);
1213 CTX_wm_region_set(C, region);
1216 /* this special cases is for areas and regions that get removed */
1217 CTX_wm_area_set(C, NULL);
1218 CTX_wm_region_set(C, NULL);
1221 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED))
1222 wm_operator_reports(C, op, retval, 0);
1224 if(retval & OPERATOR_FINISHED) {
1225 wm_operator_finished(C, op, 0);
1228 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1229 WM_operator_free(op);
1233 /* remove modal handler, operator itself should have been cancelled and freed */
1234 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1235 WM_cursor_ungrab(CTX_wm_window(C));
1237 BLI_remlink(handlers, handler);
1238 wm_event_free_handler(handler);
1240 /* prevent silly errors from operator users */
1241 //retval &= ~OPERATOR_PASS_THROUGH;
1246 printf("wm_handler_operator_call error\n");
1249 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1252 retval= wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
1255 /* Finished and pass through flag as handled */
1256 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1257 return WM_HANDLER_HANDLED;
1259 /* Modal unhandled, break */
1260 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1261 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1263 if(retval & OPERATOR_PASS_THROUGH)
1264 return WM_HANDLER_CONTINUE;
1266 return WM_HANDLER_BREAK;
1269 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1270 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1272 wmWindowManager *wm= CTX_wm_manager(C);
1274 int action= WM_HANDLER_CONTINUE;
1276 if(event->type != EVT_FILESELECT)
1278 if(handler->op != (wmOperator *)event->customdata)
1281 switch(event->val) {
1282 case EVT_FILESELECT_OPEN:
1283 case EVT_FILESELECT_FULL_OPEN:
1287 /* sa can be null when window A is active, but mouse is over window B */
1288 /* in this case, open file select in original window A */
1289 if (handler->op_area == NULL) {
1290 bScreen *screen = CTX_wm_screen(C);
1291 sa = (ScrArea *)screen->areabase.first;
1294 sa = handler->op_area;
1297 if(event->val==EVT_FILESELECT_OPEN) {
1298 ED_area_newspace(C, sa, SPACE_FILE); /* 'sa' is modified in-place */
1301 sa= ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1304 /* note, getting the 'sa' back from the context causes a nasty bug where the newly created
1305 * 'sa' != CTX_wm_area(C). removed the line below and set 'sa' in the 'if' above */
1306 /* sa = CTX_wm_area(C); */
1308 /* settings for filebrowser, sfile is not operator owner but sends events */
1309 sfile= (SpaceFile*)sa->spacedata.first;
1310 sfile->op= handler->op;
1312 ED_fileselect_set_params(sfile);
1314 action= WM_HANDLER_BREAK;
1318 case EVT_FILESELECT_EXEC:
1319 case EVT_FILESELECT_CANCEL:
1320 case EVT_FILESELECT_EXTERNAL_CANCEL:
1322 /* XXX validate area and region? */
1323 bScreen *screen= CTX_wm_screen(C);
1325 /* remlink now, for load file case before removing*/
1326 BLI_remlink(handlers, handler);
1328 if(event->val!=EVT_FILESELECT_EXTERNAL_CANCEL) {
1329 if(screen != handler->filescreen) {
1330 ED_screen_full_prevspace(C, CTX_wm_area(C));
1333 ED_area_prevspace(C, CTX_wm_area(C));
1337 wm_handler_op_context(C, handler);
1339 /* needed for uiPupMenuReports */
1341 if(event->val==EVT_FILESELECT_EXEC) {
1342 #if 0 // use REDALERT now
1344 /* a bit weak, might become arg for WM_event_fileselect? */
1345 /* XXX also extension code in image-save doesnt work for this yet */
1346 if (RNA_struct_find_property(handler->op->ptr, "check_existing") &&
1347 RNA_boolean_get(handler->op->ptr, "check_existing")) {
1348 char *path= RNA_string_get_alloc(handler->op->ptr, "filepath", NULL, 0);
1349 /* this gives ownership to pupmenu */
1350 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1359 if(handler->op->type->flag & OPTYPE_UNDO)
1360 wm->op_undo_depth++;
1362 retval= handler->op->type->exec(C, handler->op);
1364 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1365 if(handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1366 wm->op_undo_depth--;
1368 if (retval & OPERATOR_FINISHED)
1370 wm_operator_print(C, handler->op);
1372 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1373 if(CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
1374 if(handler->op->type->flag & OPTYPE_UNDO)
1375 ED_undo_push_op(C, handler->op);
1377 if(handler->op->reports->list.first) {
1379 /* FIXME, temp setting window, this is really bad!
1380 * only have because lib linking errors need to be seen by users :(
1381 * it can be removed without breaking anything but then no linking errors - campbell */
1382 wmWindow *win_prev= CTX_wm_window(C);
1384 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1386 handler->op->reports->printlevel = RPT_WARNING;
1387 uiPupMenuReports(C, handler->op->reports);
1389 /* XXX - copied from 'wm_operator_finished()' */
1390 /* add reports to the global list, otherwise they are not seen */
1391 BLI_movelisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
1393 CTX_wm_window_set(C, win_prev);
1396 WM_operator_free(handler->op);
1400 if(handler->op->type->cancel) {
1401 if(handler->op->type->flag & OPTYPE_UNDO)
1402 wm->op_undo_depth++;
1404 handler->op->type->cancel(C, handler->op);
1406 if(handler->op->type->flag & OPTYPE_UNDO)
1407 wm->op_undo_depth--;
1410 WM_operator_free(handler->op);
1413 CTX_wm_area_set(C, NULL);
1415 wm_event_free_handler(handler);
1417 action= WM_HANDLER_BREAK;
1425 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1427 if(handler->bbwin) {
1428 if(handler->bblocal) {
1429 rcti rect= *handler->bblocal;
1430 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1432 if(BLI_in_rcti(&rect, event->x, event->y))
1434 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1440 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1442 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1451 static int wm_action_not_handled(int action)
1453 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1456 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1458 wmWindowManager *wm= CTX_wm_manager(C);
1459 wmEventHandler *handler, *nexthandler;
1460 int action= WM_HANDLER_CONTINUE;
1463 if(handlers==NULL) return action;
1465 /* modal handlers can get removed in this loop, we keep the loop this way */
1466 for(handler= handlers->first; handler; handler= nexthandler) {
1468 nexthandler= handler->next;
1470 /* during this loop, ui handlers for nested menus can tag multiple handlers free */
1471 if(handler->flag & WM_HANDLER_DO_FREE);
1472 /* optional boundbox */
1473 else if(handler_boundbox_test(handler, event)) {
1474 /* in advance to avoid access to freed event on window close */
1475 always_pass= wm_event_always_pass(event);
1477 /* modal+blocking handler */
1478 if(handler->flag & WM_HANDLER_BLOCKING)
1479 action |= WM_HANDLER_BREAK;
1481 if(handler->keymap) {
1482 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1485 if(!keymap->poll || keymap->poll(C)) {
1486 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1487 if(wm_eventmatch(event, kmi)) {
1489 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1491 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1492 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1498 else if(handler->ui_handle) {
1499 action |= wm_handler_ui_call(C, handler, event, always_pass);
1501 else if(handler->type==WM_HANDLER_FILESELECT) {
1502 /* screen context changes here */
1503 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1505 else if(handler->dropboxes) {
1506 if(event->type==EVT_DROP) {
1507 wmDropBox *drop= handler->dropboxes->first;
1508 for(; drop; drop= drop->next) {
1509 /* other drop custom types allowed */
1510 if(event->custom==EVT_DATA_LISTBASE) {
1511 ListBase *lb= (ListBase *)event->customdata;
1514 for(drag= lb->first; drag; drag= drag->next) {
1515 if(drop->poll(C, drag, event)) {
1517 drop->copy(drag, drop);
1519 /* free the drags before calling operator */
1520 BLI_freelistN(event->customdata);
1521 event->customdata= NULL;
1524 WM_operator_name_call(C, drop->ot->idname, drop->opcontext, drop->ptr);
1525 action |= WM_HANDLER_BREAK;
1527 /* XXX fileread case */
1528 if(CTX_wm_window(C)==NULL)
1531 /* escape from drag loop, got freed */
1540 /* modal, swallows all */
1541 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1544 if(action & WM_HANDLER_BREAK) {
1546 action &= ~WM_HANDLER_BREAK;
1552 /* XXX fileread case, if the wm is freed then the handler's
1553 * will have been too so the code below need not run. */
1554 if(CTX_wm_window(C)==NULL) {
1558 /* XXX code this for all modal ops, and ensure free only happens here */
1560 /* modal ui handler can be tagged to be freed */
1561 if(BLI_findindex(handlers, handler) != -1) { /* could be free'd already by regular modal ops */
1562 if(handler->flag & WM_HANDLER_DO_FREE) {
1563 BLI_remlink(handlers, handler);
1564 wm_event_free_handler(handler);
1569 /* test for CLICK event */
1570 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1571 wmWindow *win = CTX_wm_window(C);
1573 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1574 /* test for double click first,
1575 * note1: this can be problematic because single click operators can get the
1576 * double click event but then with old mouse coords which is highly confusing,
1577 * so check for mouse moves too.
1578 * note2: the first click event will be handled but still used to create a
1579 * double click event if clicking again quickly.
1580 * If no double click events are found it will fallback to a single click.
1581 * So a double click event can result in 2 successive single click calls
1582 * if its not handled by the keymap - campbell */
1583 if ( (ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
1584 (ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
1585 ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time)
1587 event->val = KM_DBL_CLICK;
1588 /* removed this because in cases where we're this is used as a single click
1589 * event, this will give old coords, since the distance is checked above, using new coords should be ok. */
1590 // event->x = win->eventstate->prevclickx;
1591 // event->y = win->eventstate->prevclicky;
1592 action |= wm_handlers_do(C, event, handlers);
1595 if (wm_action_not_handled(action)) {
1596 event->val = KM_CLICK;
1597 action |= wm_handlers_do(C, event, handlers);
1601 /* revert value if not handled */
1602 if (wm_action_not_handled(action)) {
1603 event->val = KM_RELEASE;
1608 if(action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL))
1609 wm_cursor_arrow_move(CTX_wm_window(C), event);
1614 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1616 if(wm_event_always_pass(event))
1618 if(BLI_in_rcti(rect, event->x, event->y))
1620 if(event->type==MOUSEMOVE) {
1621 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1629 static ScrArea *area_event_inside(bContext *C, int x, int y)
1631 bScreen *screen= CTX_wm_screen(C);
1635 for(sa= screen->areabase.first; sa; sa= sa->next)
1636 if(BLI_in_rcti(&sa->totrct, x, y))
1641 static ARegion *region_event_inside(bContext *C, int x, int y)
1643 bScreen *screen= CTX_wm_screen(C);
1644 ScrArea *area= CTX_wm_area(C);
1648 for(ar= area->regionbase.first; ar; ar= ar->next)
1649 if(BLI_in_rcti(&ar->winrct, x, y))
1654 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1657 for(; pc; pc= pc->next) {
1658 if(pc->poll == NULL || pc->poll(C)) {
1659 wmWindow *win= CTX_wm_window(C);
1660 win->screen->do_draw_paintcursor= 1;
1661 wm_tag_redraw_overlay(win, ar);
1667 /* called on mousemove, check updates for paintcursors */
1668 /* context was set on active area and region */
1669 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1671 wmWindowManager *wm= CTX_wm_manager(C);
1673 if(wm->paintcursors.first) {
1674 ARegion *ar= CTX_wm_region(C);
1677 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1679 /* if previous position was not in current region, we have to set a temp new context */
1680 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1681 ScrArea *sa= CTX_wm_area(C);
1683 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1684 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1686 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1688 CTX_wm_area_set(C, sa);
1689 CTX_wm_region_set(C, ar);
1694 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1696 if(wm->drags.first==NULL) return;
1698 if(event->type==MOUSEMOVE)
1699 win->screen->do_draw_drag= 1;
1700 else if(event->type==ESCKEY) {
1701 BLI_freelistN(&wm->drags);
1702 win->screen->do_draw_drag= 1;
1704 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1705 event->type= EVT_DROP;
1707 /* create customdata, first free existing */
1708 if(event->customdata) {
1709 if(event->customdatafree)
1710 MEM_freeN(event->customdata);
1713 event->custom= EVT_DATA_LISTBASE;
1714 event->customdata= &wm->drags;
1715 event->customdatafree= 1;
1717 /* clear drop icon */
1718 win->screen->do_draw_drag= 1;
1720 /* restore cursor (disabled, see wm_dragdrop.c) */
1721 // WM_cursor_restore(win);
1724 /* overlap fails otherwise */
1725 if(win->screen->do_draw_drag)
1726 if(win->drawmethod == USER_DRAW_OVERLAP)
1727 win->screen->do_draw= 1;
1731 /* called in main loop */
1732 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1733 void wm_event_do_handlers(bContext *C)
1735 wmWindowManager *wm= CTX_wm_manager(C);
1738 for(win= wm->windows.first; win; win= win->next) {
1741 if( win->screen==NULL )
1742 wm_event_free_all(win);
1744 Scene* scene = win->screen->scene;
1747 int playing = sound_scene_playing(win->screen->scene);
1750 CTX_wm_window_set(C, win);
1751 CTX_wm_screen_set(C, win->screen);
1752 CTX_data_scene_set(C, scene);
1754 if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer))){
1755 ED_screen_animation_play(C, -1, 1);
1759 int ncfra = sound_sync_scene(scene) * (float)FPS + 0.5f;
1760 if(ncfra != scene->r.cfra) {
1761 scene->r.cfra = ncfra;
1762 ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
1763 WM_event_add_notifier(C, NC_WINDOW, NULL);
1767 CTX_data_scene_set(C, NULL);
1768 CTX_wm_screen_set(C, NULL);
1769 CTX_wm_window_set(C, NULL);
1774 while( (event= win->queue.first) ) {
1775 int action = WM_HANDLER_CONTINUE;
1777 if((G.f & G_DEBUG) && event && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1778 printf("pass on evt %d val %d\n", event->type, event->val);
1780 wm_eventemulation(event);
1782 CTX_wm_window_set(C, win);
1784 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1785 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1786 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1788 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1789 wm_window_make_drawable(C, win);
1791 wm_region_mouse_co(C, event);
1793 /* first we do priority handlers, modal + some limited keymaps */
1794 action |= wm_handlers_do(C, event, &win->modalhandlers);
1797 if(CTX_wm_window(C)==NULL)
1800 /* check dragging, creates new event or frees, adds draw tag */
1801 wm_event_drag_test(wm, win, event);
1803 /* builtin tweak, if action is break it removes tweak */
1804 wm_tweakevent_test(C, event, action);
1806 if((action & WM_HANDLER_BREAK) == 0) {
1811 /* Note: setting subwin active should be done here, after modal handlers have been done */
1812 if(event->type==MOUSEMOVE) {
1813 /* state variables in screen, cursors. Also used in wm_draw.c, fails for modal handlers though */
1814 ED_screen_set_subwinactive(C, event);
1815 /* for regions having custom cursors */
1816 wm_paintcursor_test(C, event);
1819 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1820 if(wm_event_inside_i(event, &sa->totrct)) {
1821 CTX_wm_area_set(C, sa);
1823 if((action & WM_HANDLER_BREAK) == 0) {
1824 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1825 if(wm_event_inside_i(event, &ar->winrct)) {
1826 CTX_wm_region_set(C, ar);
1828 /* call even on non mouse events, since the */
1829 wm_region_mouse_co(C, event);
1831 /* does polls for drop regions and checks uibuts */
1832 /* need to be here to make sure region context is true */
1833 if(ELEM(event->type, MOUSEMOVE, EVT_DROP)) {
1834 wm_drags_check_ops(C, event);
1837 action |= wm_handlers_do(C, event, &ar->handlers);
1839 /* fileread case (python), [#29489] */
1840 if(CTX_wm_window(C)==NULL)
1843 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1845 if(action & WM_HANDLER_BREAK)
1851 CTX_wm_region_set(C, NULL);
1853 if((action & WM_HANDLER_BREAK) == 0) {
1854 wm_region_mouse_co(C, event); /* only invalidates event->mval in this case */
1855 action |= wm_handlers_do(C, event, &sa->handlers);
1857 CTX_wm_area_set(C, NULL);
1859 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1863 if((action & WM_HANDLER_BREAK) == 0) {
1864 /* also some non-modal handlers need active area/region */
1865 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1866 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1868 wm_region_mouse_co(C, event);
1870 action |= wm_handlers_do(C, event, &win->handlers);
1873 if(CTX_wm_window(C)==NULL)
1877 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1878 doing it on ghost queue gives errors when mousemoves go over area borders */
1879 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1880 win->eventstate->prevx= event->x;
1881 win->eventstate->prevy= event->y;
1885 /* store last event for this window */
1886 /* mousemove and timer events don't overwrite last type */
1887 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1888 if (wm_action_not_handled(action)) {
1889 if (win->eventstate->prevtype == event->type) {
1890 /* set click time on first click (press -> release) */
1891 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1892 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1893 win->eventstate->prevclickx = event->x;
1894 win->eventstate->prevclicky = event->y;
1897 /* reset click time if event type not the same */
1898 win->eventstate->prevclicktime = 0;
1901 win->eventstate->prevval = event->val;
1902 win->eventstate->prevtype = event->type;
1903 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1904 win->eventstate->prevtype = event->type;
1905 win->eventstate->prevval = event->val;
1906 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1907 win->eventstate->prevclickx = event->x;
1908 win->eventstate->prevclicky = event->y;
1909 } else { /* reset if not */
1910 win->eventstate->prevtype = -1;
1911 win->eventstate->prevval = 0;
1912 win->eventstate->prevclicktime = 0;
1916 /* unlink and free here, blender-quit then frees all */
1917 BLI_remlink(&win->queue, event);
1918 wm_event_free(event);
1922 /* only add mousemove when queue was read entirely */
1923 if(win->addmousemove && win->eventstate) {
1924 wmEvent tevent= *(win->eventstate);
1925 tevent.type= MOUSEMOVE;
1926 tevent.prevx= tevent.x;
1927 tevent.prevy= tevent.y;
1928 wm_event_add(win, &tevent);
1929 win->addmousemove= 0;
1932 CTX_wm_window_set(C, NULL);
1936 /* ********** filesector handling ************ */
1938 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1940 /* add to all windows! */
1943 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1944 wmEvent event= *win->eventstate;
1946 event.type= EVT_FILESELECT;
1947 event.val= eventval;
1948 event.customdata= ophandle; // only as void pointer type check
1950 wm_event_add(win, &event);
1954 /* operator is supposed to have a filled "path" property */
1955 /* optional property: filetype (XXX enum?) */
1957 /* Idea is to keep a handler alive on window queue, owning the operator.
1958 The filewindow can send event to make it execute, thus ensuring
1959 executing happens outside of lower level queues, with UI refreshed.
1960 Should also allow multiwin solutions */
1962 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1964 wmEventHandler *handler, *handlernext;
1965 wmWindow *win= CTX_wm_window(C);
1966 int full= 1; // XXX preset?
1968 /* only allow 1 file selector open per window */
1969 for(handler= win->modalhandlers.first; handler; handler=handlernext) {
1970 handlernext= handler->next;
1972 if(handler->type == WM_HANDLER_FILESELECT) {
1974 WM_operator_free(handler->op);
1975 BLI_remlink(&win->modalhandlers, handler);
1976 wm_event_free_handler(handler);
1980 handler = MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1982 handler->type= WM_HANDLER_FILESELECT;
1984 handler->op_area= CTX_wm_area(C);
1985 handler->op_region= CTX_wm_region(C);
1986 handler->filescreen= CTX_wm_screen(C);
1988 BLI_addhead(&win->modalhandlers, handler);
1990 /* check props once before invoking if check is available
1991 * ensures initial properties are valid */
1992 if(op->type->check) {
1993 op->type->check(C, op); /* ignore return value */
1996 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
2000 /* lets not expose struct outside wm? */
2001 static void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
2003 handler->flag= flag;
2007 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
2009 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
2010 wmWindow *win= CTX_wm_window(C);
2012 /* operator was part of macro */
2014 /* give the mother macro to the handler */
2015 handler->op= op->opm;
2016 /* mother macro opm becomes the macro element */
2017 handler->op->opm= op;
2022 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
2023 handler->op_region= CTX_wm_region(C);
2025 BLI_addhead(&win->modalhandlers, handler);
2030 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
2032 wmEventHandler *handler;
2035 printf("WM_event_add_keymap_handler called with NULL keymap\n");
2039 /* only allow same keymap once */
2040 for(handler= handlers->first; handler; handler= handler->next)
2041 if(handler->keymap==keymap)
2044 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
2045 BLI_addtail(handlers, handler);
2046 handler->keymap= keymap;
2051 /* priorities not implemented yet, for time being just insert in begin of list */
2052 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
2054 wmEventHandler *handler;
2056 WM_event_remove_keymap_handler(handlers, keymap);
2058 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
2059 BLI_addhead(handlers, handler);
2060 handler->keymap= keymap;
2065 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
2067 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
2070 handler->bblocal= bblocal;
2071 handler->bbwin= bbwin;
2076 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
2078 wmEventHandler *handler;
2080 for(handler= handlers->first; handler; handler= handler->next) {
2081 if(handler->keymap==keymap) {
2082 BLI_remlink(handlers, handler);
2083 wm_event_free_handler(handler);
2089 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
2091 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
2092 handler->ui_handle= func;
2093 handler->ui_remove= remove;
2094 handler->ui_userdata= userdata;
2095 handler->ui_area= (C)? CTX_wm_area(C): NULL;
2096 handler->ui_region= (C)? CTX_wm_region(C): NULL;
2097 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
2099 BLI_addhead(handlers, handler);
2104 /* set "postpone" for win->modalhandlers, this is in a running for() loop in wm_handlers_do() */
2105 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata, int postpone)
2107 wmEventHandler *handler;
2109 for(handler= handlers->first; handler; handler= handler->next) {
2110 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
2111 /* handlers will be freed in wm_handlers_do() */
2113 handler->flag |= WM_HANDLER_DO_FREE;
2116 BLI_remlink(handlers, handler);
2117 wm_event_free_handler(handler);
2124 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
2126 wmEventHandler *handler;
2128 /* only allow same dropbox once */
2129 for(handler= handlers->first; handler; handler= handler->next)
2130 if(handler->dropboxes==dropboxes)
2133 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
2135 /* dropbox stored static, no free or copy */
2136 handler->dropboxes= dropboxes;
2137 BLI_addhead(handlers, handler);
2142 /* XXX solution works, still better check the real cause (ton) */
2143 void WM_event_remove_area_handler(ListBase *handlers, void *area)
2145 wmEventHandler *handler, *nexthandler;
2147 for(handler = handlers->first; handler; handler= nexthandler) {
2148 nexthandler = handler->next;
2149 if (handler->type != WM_HANDLER_FILESELECT) {
2150 if (handler->ui_area == area) {
2151 BLI_remlink(handlers, handler);
2152 wm_event_free_handler(handler);
2159 static void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
2161 BLI_remlink(handlers, handler);
2162 wm_event_free_handler(handler);
2166 void WM_event_add_mousemove(bContext *C)
2168 wmWindow *window= CTX_wm_window(C);
2170 window->addmousemove= 1;
2173 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
2174 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
2176 /* if the release-confirm userpref setting is enabled,
2177 * tweak events can be cancelled when mouse is released
2179 if (U.flag & USER_RELEASECONFIRM) {
2180 /* option on, so can exit with km-release */
2181 if (evt->val == KM_RELEASE) {
2182 switch (tweak_event) {
2190 /* if the initial event wasn't a tweak event then
2191 * ignore USER_RELEASECONFIRM setting: see [#26756] */
2192 if(ELEM3(tweak_event, EVT_TWEAK_L, EVT_TWEAK_M, EVT_TWEAK_R) == 0) {
2198 /* this is fine as long as not doing km-release, otherwise
2199 * some items (i.e. markers) being tweaked may end up getting
2202 if (evt->val != KM_RELEASE)
2209 /* ********************* ghost stuff *************** */
2211 static int convert_key(GHOST_TKey key)
2213 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
2214 return (AKEY + ((int) key - GHOST_kKeyA));
2215 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
2216 return (ZEROKEY + ((int) key - GHOST_kKey0));
2217 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
2218 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
2219 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF19) {
2220 return (F1KEY + ((int) key - GHOST_kKeyF1));
2223 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
2224 case GHOST_kKeyTab: return TABKEY;
2225 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
2226 case GHOST_kKeyClear: return 0;
2227 case GHOST_kKeyEnter: return RETKEY;
2229 case GHOST_kKeyEsc: return ESCKEY;
2230 case GHOST_kKeySpace: return SPACEKEY;
2231 case GHOST_kKeyQuote: return QUOTEKEY;
2232 case GHOST_kKeyComma: return COMMAKEY;
2233 case GHOST_kKeyMinus: return MINUSKEY;
2234 case GHOST_kKeyPeriod: return PERIODKEY;
2235 case GHOST_kKeySlash: return SLASHKEY;
2237 case GHOST_kKeySemicolon: return SEMICOLONKEY;
2238 case GHOST_kKeyEqual: return EQUALKEY;
2240 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
2241 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
2242 case GHOST_kKeyBackslash: return BACKSLASHKEY;
2243 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
2245 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
2246 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
2247 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
2248 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
2249 case GHOST_kKeyOS: return OSKEY;
2250 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
2251 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
2253 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
2254 case GHOST_kKeyNumLock: return 0;
2255 case GHOST_kKeyScrollLock: return 0;
2257 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
2258 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
2259 case GHOST_kKeyUpArrow: return UPARROWKEY;
2260 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
2262 case GHOST_kKeyPrintScreen: return 0;
2263 case GHOST_kKeyPause: return PAUSEKEY;
2265 case GHOST_kKeyInsert: return INSERTKEY;
2266 case GHOST_kKeyDelete: return DELKEY;
2267 case GHOST_kKeyHome: return HOMEKEY;
2268 case GHOST_kKeyEnd: return ENDKEY;
2269 case GHOST_kKeyUpPage: return PAGEUPKEY;
2270 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
2272 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
2273 case GHOST_kKeyNumpadEnter: return PADENTER;
2274 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
2275 case GHOST_kKeyNumpadMinus: return PADMINUS;
2276 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
2277 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
2279 case GHOST_kKeyGrLess: return GRLESSKEY;
2281 case GHOST_kKeyMediaPlay: return MEDIAPLAY;
2282 case GHOST_kKeyMediaStop: return MEDIASTOP;
2283 case GHOST_kKeyMediaFirst: return MEDIAFIRST;
2284 case GHOST_kKeyMediaLast: return MEDIALAST;
2287 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
2292 /* adds customdata to event */
2293 static void update_tablet_data(wmWindow *win, wmEvent *event)
2295 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2297 /* if there's tablet data from an active tablet device then add it */
2298 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2299 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2301 wmtab->Active = (int)td->Active;
2302 wmtab->Pressure = td->Pressure;
2303 wmtab->Xtilt = td->Xtilt;
2304 wmtab->Ytilt = td->Ytilt;
2306 event->custom= EVT_DATA_TABLET;
2307 event->customdata= wmtab;
2308 event->customdatafree= 1;
2312 /* imperfect but probably usable... draw/enable drags to other windows */
2313 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2315 int mx= evt->x, my= evt->y;
2317 if(wm->windows.first== wm->windows.last)
2320 /* top window bar... */
2321 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
2323 wmEventHandler *handler;
2325 /* let's skip windows having modal handlers now */
2326 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2327 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2328 if(handler->ui_handle || handler->op)
2331 /* to desktop space */
2332 mx += (int)win->posx;
2333 my += (int)win->posy;
2335 /* check other windows to see if it has mouse inside */
2336 for(owin= wm->windows.first; owin; owin= owin->next) {
2339 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2340 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2341 evt->x= mx - (int)owin->posx;
2342 evt->y= my - (int)owin->posy;
2352 /* windows store own event queues, no bContext here */
2353 /* time is in 1000s of seconds, from ghost */
2354 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int UNUSED(time), void *customdata)
2357 wmEvent event, *evt= win->eventstate;
2359 /* initialize and copy state (only mouse x y and modifiers) */
2364 case GHOST_kEventCursorMove: {
2366 GHOST_TEventCursorData *cd= customdata;
2367 wmEvent *lastevent= win->queue.last;
2370 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2372 evt->y= (win->sizey-1) - cy;
2377 event.type= MOUSEMOVE;
2379 /* some painting operators want accurate mouse events, they can
2380 handle in between mouse move moves, others can happily ignore
2381 them for better performance */
2382 if(lastevent && lastevent->type == MOUSEMOVE)
2383 lastevent->type = INBETWEEN_MOUSEMOVE;
2385 update_tablet_data(win, &event);
2386 wm_event_add(win, &event);
2388 /* also add to other window if event is there, this makes overdraws disappear nicely */
2389 /* it remaps mousecoord to other window in event */
2390 owin= wm_event_cursor_other_windows(wm, win, &event);
2392 wmEvent oevent= *(owin->eventstate);
2394 oevent.x=owin->eventstate->x= event.x;
2395 oevent.y=owin->eventstate->y= event.y;
2396 oevent.type= MOUSEMOVE;
2398 update_tablet_data(owin, &oevent);
2399 wm_event_add(owin, &oevent);
2405 case GHOST_kEventTrackpad: {
2406 GHOST_TEventTrackpadData * pd = customdata;
2407 switch (pd->subtype) {
2408 case GHOST_kTrackpadEventMagnify:
2409 event.type = MOUSEZOOM;
2411 case GHOST_kTrackpadEventRotate:
2412 event.type = MOUSEROTATE;
2414 case GHOST_kTrackpadEventScroll:
2416 event.type= MOUSEPAN;
2422 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2423 event.x= evt->x= cx;
2424 event.y= evt->y= (win->sizey-1) - cy;
2427 // Use prevx/prevy so we can calculate the delta later
2428 event.prevx= event.x - pd->deltaX;
2429 event.prevy= event.y - (-pd->deltaY);
2431 update_tablet_data(win, &event);
2432 wm_event_add(win, &event);
2436 case GHOST_kEventButtonDown:
2437 case GHOST_kEventButtonUp: {
2438 GHOST_TEventButtonData *bd= customdata;
2439 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 */
2441 if (bd->button == GHOST_kButtonMaskLeft)
2442 event.type= LEFTMOUSE;
2443 else if (bd->button == GHOST_kButtonMaskRight)
2444 event.type= RIGHTMOUSE;
2445 else if (bd->button == GHOST_kButtonMaskButton4)
2446 event.type= BUTTON4MOUSE;
2447 else if (bd->button == GHOST_kButtonMaskButton5)
2448 event.type= BUTTON5MOUSE;
2450 event.type= MIDDLEMOUSE;
2452 /* add to other window if event is there (not to both!) */
2453 owin= wm_event_cursor_other_windows(wm, win, &event);
2455 wmEvent oevent= *(owin->eventstate);
2459 oevent.type= event.type;
2460 oevent.val= event.val;
2462 update_tablet_data(owin, &oevent);
2463 wm_event_add(owin, &oevent);
2466 update_tablet_data(win, &event);
2467 wm_event_add(win, &event);
2473 case GHOST_kEventKeyDown:
2474 case GHOST_kEventKeyUp: {
2475 GHOST_TEventKeyData *kd= customdata;
2476 event.type= convert_key(kd->key);
2477 event.ascii= kd->ascii;
2478 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2480 /* exclude arrow keys, esc, etc from text input */
2481 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2485 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2486 event.shift= evt->shift= (event.val==KM_PRESS);
2487 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2488 event.shift= evt->shift = 3; // define?
2490 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2491 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2492 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2493 event.ctrl= evt->ctrl = 3; // define?
2495 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2496 event.alt= evt->alt= (event.val==KM_PRESS);
2497 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2498 event.alt= evt->alt = 3; // define?
2500 else if (event.type==OSKEY) {
2501 event.oskey= evt->oskey= (event.val==KM_PRESS);
2502 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2503 event.oskey= evt->oskey = 3; // define?
2506 if(event.val==KM_PRESS && event.keymodifier==0)
2507 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2508 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2509 event.keymodifier= evt->keymodifier= 0;
2512 /* this case happens on some systems that on holding a key pressed,
2513 generate press events without release, we still want to keep the
2514 modifier in win->eventstate, but for the press event of the same
2515 key we don't want the key modifier */
2516 if(event.keymodifier == event.type)
2517 event.keymodifier= 0;
2519 /* if test_break set, it catches this. XXX Keep global for now? */
2520 if(event.type==ESCKEY)
2523 wm_event_add(win, &event);
2528 case GHOST_kEventWheel: {
2529 GHOST_TEventWheelData* wheelData = customdata;
2531 if (wheelData->z > 0)
2532 event.type= WHEELUPMOUSE;
2534 event.type= WHEELDOWNMOUSE;
2536 event.val= KM_PRESS;
2537 wm_event_add(win, &event);
2541 case GHOST_kEventTimer: {
2543 event.custom= EVT_DATA_TIMER;
2544 event.customdata= customdata;
2545 wm_event_add(win, &event);
2550 case GHOST_kEventUnknown:
2551 case GHOST_kNumEventTypes:
2554 case GHOST_kEventWindowDeactivate: {
2555 event.type= WINDEACTIVATE;
2556 wm_event_add(win, &event);