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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 *****
32 #include "DNA_listBase.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_windowmanager_types.h"
36 #include "DNA_userdef_types.h"
38 #include "MEM_guardedalloc.h"
40 #include "GHOST_C-api.h"
42 #include "BLI_blenlib.h"
44 #include "BKE_blender.h"
45 #include "BKE_context.h"
46 #include "BKE_idprop.h"
47 #include "BKE_global.h"
49 #include "BKE_report.h"
50 #include "BKE_scene.h"
51 #include "BKE_utildefines.h"
53 #include "ED_fileselect.h"
55 #include "ED_screen.h"
56 #include "ED_space_api.h"
59 #include "RNA_access.h"
61 #include "UI_interface.h"
68 #include "wm_window.h"
69 #include "wm_event_system.h"
70 #include "wm_event_types.h"
72 /* ************ event management ************** */
74 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
76 wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
78 *event= *event_to_add;
79 BLI_addtail(&win->queue, event);
82 void wm_event_free(wmEvent *event)
84 if(event->customdata) {
85 if(event->customdatafree) {
86 /* note: pointer to listbase struct elsewhere */
87 if(event->custom==EVT_DATA_LISTBASE)
88 BLI_freelistN(event->customdata);
90 MEM_freeN(event->customdata);
96 void wm_event_free_all(wmWindow *win)
100 while((event= win->queue.first)) {
101 BLI_remlink(&win->queue, event);
102 wm_event_free(event);
106 /* ********************* notifiers, listeners *************** */
108 /* XXX: in future, which notifiers to send to other windows? */
109 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
111 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
113 note->wm= CTX_wm_manager(C);
114 BLI_addtail(¬e->wm->queue, note);
116 note->window= CTX_wm_window(C);
119 note->swinid= CTX_wm_region(C)->swinid;
121 note->category= type & NOTE_CATEGORY;
122 note->data= type & NOTE_DATA;
123 note->subtype= type & NOTE_SUBTYPE;
124 note->action= type & NOTE_ACTION;
126 note->reference= reference;
129 void WM_main_add_notifier(unsigned int type, void *reference)
132 wmWindowManager *wm= bmain->wm.first;
135 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
138 BLI_addtail(¬e->wm->queue, note);
140 note->category= type & NOTE_CATEGORY;
141 note->data= type & NOTE_DATA;
142 note->subtype= type & NOTE_SUBTYPE;
143 note->action= type & NOTE_ACTION;
145 note->reference= reference;
149 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
151 wmNotifier *note= wm->queue.first;
153 if(note) BLI_remlink(&wm->queue, note);
157 /* called in mainloop */
158 void wm_event_do_notifiers(bContext *C)
160 wmWindowManager *wm= CTX_wm_manager(C);
161 wmNotifier *note, *next;
167 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
168 for(win= wm->windows.first; win; win= win->next) {
171 CTX_wm_window_set(C, win);
173 for(note= wm->queue.first; note; note= next) {
176 if(note->category==NC_WM) {
177 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
179 wm_window_title(wm, win);
181 else if(note->data==ND_DATACHANGED)
182 wm_window_title(wm, win);
184 if(note->window==win) {
185 if(note->category==NC_SCREEN) {
186 if(note->data==ND_SCREENBROWSE) {
187 ED_screen_set(C, note->reference); // XXX hrms, think this over!
189 printf("screen set %p\n", note->reference);
191 else if(note->data==ND_SCREENDELETE) {
192 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
194 printf("screen delete %p\n", note->reference);
199 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
200 if(note->category==NC_SCENE) {
201 if(note->data==ND_SCENEBROWSE) {
202 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
204 printf("scene set %p\n", note->reference);
206 else if(note->data==ND_FRAME)
209 if(note->action == NA_REMOVED) {
210 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
212 printf("scene delete %p\n", note->reference);
217 if(ELEM4(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE)) {
218 ED_info_stats_clear(CTX_data_scene(C));
219 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
223 /* depsgraph gets called, might send more notifiers */
224 ED_update_for_newframe(C, 1);
228 /* the notifiers are sent without context, to keep it clean */
229 while( (note=wm_notifier_next(wm)) ) {
232 for(win= wm->windows.first; win; win= win->next) {
234 /* filter out notifiers */
235 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
236 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
241 /* XXX context in notifiers? */
242 CTX_wm_window_set(C, win);
244 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
245 ED_screen_do_listen(win, note);
247 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
248 ED_region_do_listen(ar, note);
251 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
252 ED_area_do_listen(sa, note);
253 for(ar=sa->regionbase.first; ar; ar= ar->next) {
254 ED_region_do_listen(ar, note);
263 /* cached: editor refresh callbacks now, they get context */
264 for(win= wm->windows.first; win; win= win->next) {
267 CTX_wm_window_set(C, win);
268 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
270 CTX_wm_area_set(C, sa);
271 ED_area_do_refresh(C, sa);
275 /* XXX make lock in future, or separated derivedmesh users in scene */
277 /* depsgraph & animation: update tagged datablocks */
278 scene_update_tagged(win->screen->scene);
281 CTX_wm_window_set(C, NULL);
284 /* ********************* operators ******************* */
286 int WM_operator_poll(bContext *C, wmOperatorType *ot)
288 wmOperatorTypeMacro *otmacro;
290 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
291 wmOperatorType *ot= WM_operatortype_find(otmacro->idname, 0);
293 if(0==WM_operator_poll(C, ot))
297 /* python needs operator type, so we added exception for it */
299 return ot->pyop_poll(C, ot);
306 /* if repeat is true, it doesn't register again, nor does it free */
307 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
309 int retval= OPERATOR_CANCELLED;
311 if(op==NULL || op->type==NULL)
314 if(0==WM_operator_poll(C, op->type))
318 retval= op->type->exec(C, op);
320 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
321 if(op->reports->list.first)
322 uiPupMenuReports(C, op->reports);
324 if(retval & OPERATOR_FINISHED) {
325 op->customdata= NULL;
327 if(op->type->flag & OPTYPE_UNDO)
328 ED_undo_push_op(C, op);
332 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
333 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
337 if((op->type->flag & OPTYPE_REGISTER))
338 wm_operator_register(C, op);
340 WM_operator_free(op);
344 WM_operator_free(op);
346 return retval | OPERATOR_HANDLED;
350 /* for running operators with frozen context (modal handlers, menus) */
351 int WM_operator_call(bContext *C, wmOperator *op)
353 return wm_operator_exec(C, op, 0);
356 /* do this operator again, put here so it can share above code */
357 int WM_operator_repeat(bContext *C, wmOperator *op)
359 return wm_operator_exec(C, op, 1);
362 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
364 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
366 /* XXX adding new operator could be function, only happens here now */
368 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
370 /* initialize properties, either copy or create */
371 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
372 if(properties && properties->data) {
373 op->properties= IDP_CopyProperty(properties->data);
376 IDPropertyTemplate val = {0};
377 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
379 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
381 /* initialize error reports */
383 op->reports= reports; /* must be initialized already */
386 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
387 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
390 /* recursive filling of operator macro list */
391 if(ot->macro.first) {
392 static wmOperator *motherop= NULL;
393 wmOperatorTypeMacro *otmacro;
396 /* ensure all ops are in execution order in 1 list */
403 /* if properties exist, it will contain everything needed */
405 otmacro= ot->macro.first;
407 RNA_STRUCT_BEGIN(properties, prop) {
412 /* skip invalid properties */
413 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
415 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
416 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
417 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
419 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
421 BLI_addtail(&motherop->macro, opm);
422 opm->opm= motherop; /* pointer to mom, for modal() */
424 otmacro= otmacro->next;
429 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
430 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
431 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
433 BLI_addtail(&motherop->macro, opm);
434 opm->opm= motherop; /* pointer to mom, for modal() */
445 static void wm_operator_print(wmOperator *op)
447 char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
452 static void wm_region_mouse_co(bContext *C, wmEvent *event)
454 ARegion *ar= CTX_wm_region(C);
456 /* compatibility convention */
457 event->mval[0]= event->x - ar->winrct.xmin;
458 event->mval[1]= event->y - ar->winrct.ymin;
462 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
464 wmWindowManager *wm= CTX_wm_manager(C);
465 int retval= OPERATOR_PASS_THROUGH;
467 if(WM_operator_poll(C, ot)) {
468 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
470 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
471 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
473 if(op->type->invoke && event) {
474 wm_region_mouse_co(C, event);
475 retval= op->type->invoke(C, op, event);
477 else if(op->type->exec)
478 retval= op->type->exec(C, op);
480 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
482 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
483 * currently python only uses this */
484 if((retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED)) && reports==NULL)
485 if(op->reports->list.first) /* only show the report if the report list was not given in the function */
486 uiPupMenuReports(C, op->reports);
488 if (retval & OPERATOR_FINISHED) { /* todo - this may conflict with the other wm_operator_print, if theres ever 2 prints for 1 action will may need to add modal check here */
490 wm_operator_print(op);
493 if(retval & OPERATOR_HANDLED)
494 ; /* do nothing, wm_operator_exec() has been called somewhere */
495 else if(retval & OPERATOR_FINISHED) {
496 op->customdata= NULL;
498 if(ot->flag & OPTYPE_UNDO)
499 ED_undo_push_op(C, op);
502 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
503 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
507 if((ot->flag & OPTYPE_REGISTER))
508 wm_operator_register(C, op);
510 WM_operator_free(op);
512 else if(retval & OPERATOR_RUNNING_MODAL) {
513 /* grab cursor during blocking modal ops (X11)
514 * Also check for macro
516 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
517 int bounds[4] = {-1,-1,-1,-1};
521 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
523 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
527 ARegion *ar= CTX_wm_region(C);
529 bounds[0]= ar->winrct.xmin;
530 bounds[1]= ar->winrct.ymax;
531 bounds[2]= ar->winrct.xmax;
532 bounds[3]= ar->winrct.ymin;
536 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
540 WM_operator_free(op);
546 /* WM_operator_name_call is the main accessor function
547 * this is for python to access since its done the operator lookup
549 * invokes operator in context */
550 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
552 wmWindow *window= CTX_wm_window(C);
560 case WM_OP_INVOKE_DEFAULT:
561 case WM_OP_INVOKE_REGION_WIN:
562 case WM_OP_INVOKE_AREA:
563 case WM_OP_INVOKE_SCREEN:
564 /* window is needed for invoke, cancel operator */
568 event= window->eventstate;
576 case WM_OP_EXEC_REGION_WIN:
577 case WM_OP_INVOKE_REGION_WIN:
579 /* forces operator to go to the region window, for header menus */
580 ARegion *ar= CTX_wm_region(C);
581 ScrArea *area= CTX_wm_area(C);
584 ARegion *ar1= area->regionbase.first;
585 for(; ar1; ar1= ar1->next)
586 if(ar1->regiontype==RGN_TYPE_WINDOW)
589 CTX_wm_region_set(C, ar1);
592 retval= wm_operator_invoke(C, ot, event, properties, reports);
594 /* set region back */
595 CTX_wm_region_set(C, ar);
599 case WM_OP_EXEC_AREA:
600 case WM_OP_INVOKE_AREA:
602 /* remove region from context */
603 ARegion *ar= CTX_wm_region(C);
605 CTX_wm_region_set(C, NULL);
606 retval= wm_operator_invoke(C, ot, event, properties, reports);
607 CTX_wm_region_set(C, ar);
611 case WM_OP_EXEC_SCREEN:
612 case WM_OP_INVOKE_SCREEN:
614 /* remove region + area from context */
615 ARegion *ar= CTX_wm_region(C);
616 ScrArea *area= CTX_wm_area(C);
618 CTX_wm_region_set(C, NULL);
619 CTX_wm_area_set(C, NULL);
620 retval= wm_operator_invoke(C, ot, event, properties, reports);
621 CTX_wm_region_set(C, ar);
622 CTX_wm_area_set(C, area);
626 case WM_OP_EXEC_DEFAULT:
627 case WM_OP_INVOKE_DEFAULT:
628 return wm_operator_invoke(C, ot, event, properties, reports);
636 /* invokes operator in context */
637 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
639 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
641 return wm_operator_call_internal(C, ot, context, properties, NULL);
646 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
647 - wmOperatorType is used instead of operator name since python alredy has the operator type
648 - poll() must be called by python before this runs.
649 - reports can be passed to this function (so python can report them as exceptions)
651 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
653 int retval= OPERATOR_CANCELLED;
657 wmWindowManager *wm= CTX_wm_manager(C);
658 op= wm_operator_create(wm, ot, properties, reports);
661 retval= op->type->exec(C, op);
663 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
666 retval= wm_operator_call_internal(C, ot, context, properties, reports);
668 /* keep the reports around if needed later */
669 if (retval & OPERATOR_RUNNING_MODAL || ot->flag & OPTYPE_REGISTER)
671 reports->flag |= RPT_FREE;
678 /* ********************* handlers *************** */
680 /* future extra customadata free? */
681 void wm_event_free_handler(wmEventHandler *handler)
686 /* only set context when area/region is part of screen */
687 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
689 bScreen *screen= CTX_wm_screen(C);
691 if(screen && handler->op) {
692 if(handler->op_area==NULL)
693 CTX_wm_area_set(C, NULL);
697 for(sa= screen->areabase.first; sa; sa= sa->next)
698 if(sa==handler->op_area)
701 /* when changing screen layouts with running modal handlers (like render display), this
702 is not an error to print */
703 if(handler->op==NULL)
704 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
708 CTX_wm_area_set(C, sa);
709 for(ar= sa->regionbase.first; ar; ar= ar->next)
710 if(ar==handler->op_region)
712 /* XXX no warning print here, after full-area and back regions are remade */
714 CTX_wm_region_set(C, ar);
720 /* called on exit or remove area, only here call cancel callback */
721 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
723 wmEventHandler *handler;
725 /* C is zero on freeing database, modal handlers then already were freed */
726 while((handler=handlers->first)) {
727 BLI_remlink(handlers, handler);
730 if(handler->op->type->cancel) {
731 ScrArea *area= CTX_wm_area(C);
732 ARegion *region= CTX_wm_region(C);
734 wm_handler_op_context(C, handler);
736 handler->op->type->cancel(C, handler->op);
738 CTX_wm_area_set(C, area);
739 CTX_wm_region_set(C, region);
742 WM_cursor_ungrab(CTX_wm_window(C));
743 WM_operator_free(handler->op);
745 else if(handler->ui_remove) {
746 ScrArea *area= CTX_wm_area(C);
747 ARegion *region= CTX_wm_region(C);
748 ARegion *menu= CTX_wm_menu(C);
750 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
751 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
752 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
754 handler->ui_remove(C, handler->ui_userdata);
756 CTX_wm_area_set(C, area);
757 CTX_wm_region_set(C, region);
758 CTX_wm_menu_set(C, menu);
761 wm_event_free_handler(handler);
765 /* do userdef mappings */
766 int WM_userdef_event_map(int kmitype)
770 if(U.flag & USER_LMOUSESELECT)
776 if(U.flag & USER_LMOUSESELECT)
782 if(U.uiflag & USER_WHEELZOOMDIR)
785 return WHEELDOWNMOUSE;
788 if(U.uiflag & USER_WHEELZOOMDIR)
789 return WHEELDOWNMOUSE;
794 if(U.flag & USER_LMOUSESELECT)
800 if(U.flag & USER_LMOUSESELECT)
809 static void wm_eventemulation(wmEvent *event)
811 /* middlemouse emulation */
812 if(U.flag & USER_TWOBUTTONMOUSE) {
813 if(event->type == LEFTMOUSE && event->alt) {
814 event->type = MIDDLEMOUSE;
820 /* rightmouse emulation */
821 if(U.flag & USER_TWOBUTTONMOUSE) {
822 if(event->type == LEFTMOUSE && event->oskey) {
823 event->type = RIGHTMOUSE;
829 /* numpad emulation */
830 if(U.flag & USER_NONUMPAD) {
831 switch(event->type) {
832 case ZEROKEY: event->type = PAD0; break;
833 case ONEKEY: event->type = PAD1; break;
834 case TWOKEY: event->type = PAD2; break;
835 case THREEKEY: event->type = PAD3; break;
836 case FOURKEY: event->type = PAD4; break;
837 case FIVEKEY: event->type = PAD5; break;
838 case SIXKEY: event->type = PAD6; break;
839 case SEVENKEY: event->type = PAD7; break;
840 case EIGHTKEY: event->type = PAD8; break;
841 case NINEKEY: event->type = PAD9; break;
842 case MINUSKEY: event->type = PADMINUS; break;
843 case EQUALKEY: event->type = PADPLUSKEY; break;
844 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
849 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
851 int kmitype= WM_userdef_event_map(kmi->type);
853 if(kmi->flag & KMI_INACTIVE) return 0;
855 /* the matching rules */
856 if(kmitype==KM_TEXTINPUT)
857 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
859 if(winevent->type!=kmitype) return 0;
862 if(winevent->val!=kmi->val) return 0;
864 /* modifiers also check bits, so it allows modifier order */
865 if(kmi->shift!=KM_ANY)
866 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
867 if(kmi->ctrl!=KM_ANY)
868 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
870 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
871 if(kmi->oskey!=KM_ANY)
872 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
875 if(winevent->keymodifier!=kmi->keymodifier) return 0;
877 /* key modifiers always check when event has it */
878 /* otherwise regular keypresses with keymodifier still work */
879 if(winevent->keymodifier)
880 if(ISTEXTINPUT(winevent->type))
881 if(winevent->keymodifier!=kmi->keymodifier) return 0;
886 static int wm_event_always_pass(wmEvent *event)
888 /* some events we always pass on, to ensure proper communication */
889 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
892 /* operator exists */
893 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
895 /* support for modal keymap in macros */
899 if(op->type->modalkeymap) {
900 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
903 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
904 if(wm_eventmatch(event, kmi)) {
906 event->type= EVT_MODAL_MAP;
907 event->val= kmi->propvalue;
913 /* Warning: this function removes a modal handler, when finished */
914 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
916 int retval= OPERATOR_PASS_THROUGH;
918 /* derived, modal or blocking operator */
920 wmOperator *op= handler->op;
921 wmOperatorType *ot= op->type;
924 /* we set context to where modal handler came from */
925 ScrArea *area= CTX_wm_area(C);
926 ARegion *region= CTX_wm_region(C);
928 wm_handler_op_context(C, handler);
929 wm_region_mouse_co(C, event);
930 wm_event_modalkeymap(C, op, event);
932 retval= ot->modal(C, op, event);
934 /* putting back screen context, reval can pass trough after modal failures! */
935 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
936 CTX_wm_area_set(C, area);
937 CTX_wm_region_set(C, region);
940 /* this special cases is for areas and regions that get removed */
941 CTX_wm_area_set(C, NULL);
942 CTX_wm_region_set(C, NULL);
945 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
946 if(op->reports->list.first)
947 uiPupMenuReports(C, op->reports);
949 if (retval & OPERATOR_FINISHED) {
951 wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
954 if(retval & OPERATOR_FINISHED) {
955 op->customdata= NULL;
957 if(ot->flag & OPTYPE_UNDO)
958 ED_undo_push_op(C, op);
961 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
962 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
966 if((ot->flag & OPTYPE_REGISTER))
967 wm_operator_register(C, op);
969 WM_operator_free(op);
972 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
973 WM_operator_free(op);
977 /* remove modal handler, operator itself should have been cancelled and freed */
978 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
979 WM_cursor_ungrab(CTX_wm_window(C));
981 BLI_remlink(handlers, handler);
982 wm_event_free_handler(handler);
984 /* prevent silly errors from operator users */
985 //retval &= ~OPERATOR_PASS_THROUGH;
990 printf("wm_handler_operator_call error\n");
993 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
996 retval= wm_operator_invoke(C, ot, event, properties, NULL);
999 /* Finished and pass through flag as handled */
1000 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1001 return WM_HANDLER_HANDLED;
1003 /* Modal unhandled, break */
1004 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1005 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1007 if(retval & OPERATOR_PASS_THROUGH)
1008 return WM_HANDLER_CONTINUE;
1010 return WM_HANDLER_BREAK;
1013 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event)
1015 ScrArea *area= CTX_wm_area(C);
1016 ARegion *region= CTX_wm_region(C);
1017 ARegion *menu= CTX_wm_menu(C);
1018 int retval, always_pass;
1020 /* we set context to where ui handler came from */
1021 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
1022 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
1023 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
1025 /* in advance to avoid access to freed event on window close */
1026 always_pass= wm_event_always_pass(event);
1028 retval= handler->ui_handle(C, event, handler->ui_userdata);
1030 /* putting back screen context */
1031 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
1032 CTX_wm_area_set(C, area);
1033 CTX_wm_region_set(C, region);
1034 CTX_wm_menu_set(C, menu);
1037 /* this special cases is for areas and regions that get removed */
1038 CTX_wm_area_set(C, NULL);
1039 CTX_wm_region_set(C, NULL);
1040 CTX_wm_menu_set(C, NULL);
1043 if(retval == WM_UI_HANDLER_BREAK)
1044 return WM_HANDLER_BREAK;
1046 return WM_HANDLER_CONTINUE;
1049 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1050 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1053 int action= WM_HANDLER_CONTINUE;
1055 if(event->type != EVT_FILESELECT)
1057 if(handler->op != (wmOperator *)event->customdata)
1060 switch(event->val) {
1061 case EVT_FILESELECT_OPEN:
1062 case EVT_FILESELECT_FULL_OPEN:
1066 /* sa can be null when window A is active, but mouse is over window B */
1067 /* in this case, open file select in original window A */
1068 if (handler->op_area == NULL) {
1069 bScreen *screen = CTX_wm_screen(C);
1070 sa = (ScrArea *)screen->areabase.first;
1072 sa = handler->op_area;
1074 if(event->val==EVT_FILESELECT_OPEN)
1075 ED_area_newspace(C, sa, SPACE_FILE);
1077 ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1079 /* settings for filebrowser, sfile is not operator owner but sends events */
1080 sa = CTX_wm_area(C);
1081 sfile= (SpaceFile*)sa->spacedata.first;
1082 sfile->op= handler->op;
1084 ED_fileselect_set_params(sfile);
1086 action= WM_HANDLER_BREAK;
1090 case EVT_FILESELECT_EXEC:
1091 case EVT_FILESELECT_CANCEL:
1093 /* XXX validate area and region? */
1094 bScreen *screen= CTX_wm_screen(C);
1095 char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
1097 if(screen != handler->filescreen)
1098 ED_screen_full_prevspace(C, CTX_wm_area(C));
1100 ED_area_prevspace(C, CTX_wm_area(C));
1102 /* remlink now, for load file case */
1103 BLI_remlink(handlers, handler);
1105 wm_handler_op_context(C, handler);
1107 /* needed for uiPupMenuReports */
1109 if(event->val==EVT_FILESELECT_EXEC) {
1110 /* a bit weak, might become arg for WM_event_fileselect? */
1111 /* XXX also extension code in image-save doesnt work for this yet */
1112 if(strncmp(handler->op->type->name, "Save", 4)==0) {
1113 /* this gives ownership to pupmenu */
1114 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1117 int retval= handler->op->type->exec(C, handler->op);
1119 if (retval & OPERATOR_FINISHED)
1121 wm_operator_print(handler->op);
1123 if(handler->op->reports->list.first) {
1125 /* FIXME, temp setting window, this is really bad!
1126 * only have because lib linking errors need to be seen by users :(
1127 * it can be removed without breaking anything but then no linking errors - campbell */
1128 wmWindow *win_prev= CTX_wm_window(C);
1130 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1132 handler->op->reports->printlevel = RPT_WARNING;
1133 uiPupMenuReports(C, handler->op->reports);
1135 CTX_wm_window_set(C, win_prev);
1138 WM_operator_free(handler->op);
1142 if(handler->op->type->cancel)
1143 handler->op->type->cancel(C, handler->op);
1145 WM_operator_free(handler->op);
1148 CTX_wm_area_set(C, NULL);
1150 wm_event_free_handler(handler);
1154 action= WM_HANDLER_BREAK;
1162 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1164 if(handler->bbwin) {
1165 if(handler->bblocal) {
1166 rcti rect= *handler->bblocal;
1167 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1169 if(BLI_in_rcti(&rect, event->x, event->y))
1171 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1177 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1179 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1188 static int wm_action_not_handled(int action)
1190 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1193 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1195 wmWindowManager *wm= CTX_wm_manager(C);
1196 wmEventHandler *handler, *nexthandler;
1197 int action= WM_HANDLER_CONTINUE;
1200 if(handlers==NULL) return action;
1202 /* modal handlers can get removed in this loop, we keep the loop this way */
1203 for(handler= handlers->first; handler; handler= nexthandler) {
1204 nexthandler= handler->next;
1206 /* optional boundbox */
1207 if(handler_boundbox_test(handler, event)) {
1208 /* in advance to avoid access to freed event on window close */
1209 always_pass= wm_event_always_pass(event);
1211 /* modal+blocking handler */
1212 if(handler->flag & WM_HANDLER_BLOCKING)
1213 action |= WM_HANDLER_BREAK;
1215 if(handler->keymap) {
1216 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1219 if(!keymap->poll || keymap->poll(C)) {
1220 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1221 if(wm_eventmatch(event, kmi)) {
1223 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1225 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1226 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1232 else if(handler->ui_handle) {
1233 action |= wm_handler_ui_call(C, handler, event);
1235 else if(handler->type==WM_HANDLER_FILESELECT) {
1236 /* screen context changes here */
1237 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1239 else if(handler->dropboxes) {
1240 if(event->type==EVT_DROP) {
1241 wmDropBox *drop= handler->dropboxes->first;
1242 for(; drop; drop= drop->next) {
1243 /* other drop custom types allowed */
1244 if(event->custom==EVT_DATA_LISTBASE) {
1245 ListBase *lb= (ListBase *)event->customdata;
1247 for(drag= lb->first; drag; drag= drag->next) {
1248 if(drop->poll(C, drag, event)) {
1249 drop->copy(drag, drop);
1251 wm_operator_invoke(C, drop->ot, event, drop->ptr, NULL);
1252 action |= WM_HANDLER_BREAK;
1260 /* modal, swallows all */
1261 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1264 if(action & WM_HANDLER_BREAK) {
1266 action &= ~WM_HANDLER_BREAK;
1273 if(CTX_wm_window(C)==NULL)
1277 /* test for CLICK event */
1278 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1279 wmWindow *win = CTX_wm_window(C);
1281 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1282 /* test for double click first */
1283 if ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time) {
1284 event->val = KM_DBL_CLICK;
1285 action |= wm_handlers_do(C, event, handlers);
1288 if (wm_action_not_handled(action)) {
1289 event->val = KM_CLICK;
1290 action |= wm_handlers_do(C, event, handlers);
1294 /* revert value if not handled */
1295 if (wm_action_not_handled(action)) {
1296 event->val = KM_RELEASE;
1304 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1306 if(wm_event_always_pass(event))
1308 if(BLI_in_rcti(rect, event->x, event->y))
1310 if(event->type==MOUSEMOVE) {
1311 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1319 static ScrArea *area_event_inside(bContext *C, int x, int y)
1321 bScreen *screen= CTX_wm_screen(C);
1325 for(sa= screen->areabase.first; sa; sa= sa->next)
1326 if(BLI_in_rcti(&sa->totrct, x, y))
1331 static ARegion *region_event_inside(bContext *C, int x, int y)
1333 bScreen *screen= CTX_wm_screen(C);
1334 ScrArea *area= CTX_wm_area(C);
1338 for(ar= area->regionbase.first; ar; ar= ar->next)
1339 if(BLI_in_rcti(&ar->winrct, x, y))
1344 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1347 for(; pc; pc= pc->next) {
1349 wmWindow *win= CTX_wm_window(C);
1350 win->screen->do_draw_paintcursor= 1;
1352 if(win->drawmethod != USER_DRAW_TRIPLE)
1353 ED_region_tag_redraw(ar);
1359 /* called on mousemove, check updates for paintcursors */
1360 /* context was set on active area and region */
1361 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1363 wmWindowManager *wm= CTX_wm_manager(C);
1365 if(wm->paintcursors.first) {
1366 ARegion *ar= CTX_wm_region(C);
1368 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1370 /* if previous position was not in current region, we have to set a temp new context */
1371 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1372 ScrArea *sa= CTX_wm_area(C);
1374 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1375 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1377 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1379 CTX_wm_area_set(C, sa);
1380 CTX_wm_region_set(C, ar);
1385 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1387 if(wm->drags.first==NULL) return;
1389 if(event->type==MOUSEMOVE)
1390 win->screen->do_draw_drag= 1;
1391 else if(event->type==ESCKEY) {
1392 BLI_freelistN(&wm->drags);
1393 win->screen->do_draw_drag= 1;
1395 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1396 event->type= EVT_DROP;
1398 /* create customdata, first free existing */
1399 if(event->customdata) {
1400 if(event->customdatafree)
1401 MEM_freeN(event->customdata);
1404 event->custom= EVT_DATA_LISTBASE;
1405 event->customdata= &wm->drags;
1406 event->customdatafree= 1;
1408 /* clear drop icon */
1409 win->screen->do_draw_drag= 1;
1411 /* restore cursor (disabled, see wm_dragdrop.c) */
1412 // WM_cursor_restore(win);
1415 /* overlap fails otherwise */
1416 if(win->screen->do_draw_drag)
1417 if(win->drawmethod == USER_DRAW_OVERLAP)
1418 win->screen->do_draw= 1;
1422 /* called in main loop */
1423 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1424 void wm_event_do_handlers(bContext *C)
1426 wmWindowManager *wm= CTX_wm_manager(C);
1429 for(win= wm->windows.first; win; win= win->next) {
1432 if( win->screen==NULL )
1433 wm_event_free_all(win);
1435 while( (event= win->queue.first) ) {
1436 int action = WM_HANDLER_CONTINUE;
1438 wm_eventemulation(event);
1440 CTX_wm_window_set(C, win);
1442 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1443 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1444 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1446 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1447 wm_window_make_drawable(C, win);
1449 /* first we do priority handlers, modal + some limited keymaps */
1450 action |= wm_handlers_do(C, event, &win->modalhandlers);
1453 if(CTX_wm_window(C)==NULL)
1456 /* check dragging, creates new event or frees, adds draw tag */
1457 wm_event_drag_test(wm, win, event);
1459 /* builtin tweak, if action is break it removes tweak */
1460 wm_tweakevent_test(C, event, action);
1462 if((action & WM_HANDLER_BREAK) == 0) {
1467 /* XXX to solve, here screen handlers? */
1468 if(event->type==MOUSEMOVE) {
1469 /* state variables in screen, cursors */
1470 ED_screen_set_subwinactive(win, event);
1471 /* for regions having custom cursors */
1472 wm_paintcursor_test(C, event);
1475 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1476 if(wm_event_inside_i(event, &sa->totrct)) {
1477 CTX_wm_area_set(C, sa);
1479 if((action & WM_HANDLER_BREAK) == 0) {
1480 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1481 if(wm_event_inside_i(event, &ar->winrct)) {
1482 CTX_wm_region_set(C, ar);
1484 /* does polls for drop regions and checks uibuts */
1485 /* need to be here to make sure region context is true */
1486 if(event->type==MOUSEMOVE) {
1487 wm_region_mouse_co(C, event);
1488 wm_drags_check_ops(C, event);
1491 action |= wm_handlers_do(C, event, &ar->handlers);
1493 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1495 if(action & WM_HANDLER_BREAK)
1501 CTX_wm_region_set(C, NULL);
1503 if((action & WM_HANDLER_BREAK) == 0)
1504 action |= wm_handlers_do(C, event, &sa->handlers);
1506 CTX_wm_area_set(C, NULL);
1508 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1512 if((action & WM_HANDLER_BREAK) == 0) {
1513 /* also some non-modal handlers need active area/region */
1514 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1515 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1517 action |= wm_handlers_do(C, event, &win->handlers);
1520 if(CTX_wm_window(C)==NULL)
1524 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1525 doing it on ghost queue gives errors when mousemoves go over area borders */
1526 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1527 win->eventstate->prevx= event->x;
1528 win->eventstate->prevy= event->y;
1532 /* store last event for this window */
1533 /* mousemove and timer events don't overwrite last type */
1534 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1535 if (wm_action_not_handled(action)) {
1536 if (win->eventstate->prevtype == event->type) {
1537 /* set click time on first click (press -> release) */
1538 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1539 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1542 /* reset click time if event type not the same */
1543 win->eventstate->prevclicktime = 0;
1546 win->eventstate->prevval = event->val;
1547 win->eventstate->prevtype = event->type;
1548 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1549 win->eventstate->prevtype = event->type;
1550 win->eventstate->prevval = event->val;
1551 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1552 } else { /* reset if not */
1553 win->eventstate->prevtype = -1;
1554 win->eventstate->prevval = 0;
1555 win->eventstate->prevclicktime = 0;
1559 /* unlink and free here, blender-quit then frees all */
1560 BLI_remlink(&win->queue, event);
1561 wm_event_free(event);
1565 /* only add mousemove when queue was read entirely */
1566 if(win->addmousemove && win->eventstate) {
1567 wmEvent event= *(win->eventstate);
1568 event.type= MOUSEMOVE;
1569 event.prevx= event.x;
1570 event.prevy= event.y;
1571 wm_event_add(win, &event);
1572 win->addmousemove= 0;
1575 CTX_wm_window_set(C, NULL);
1579 /* ********** filesector handling ************ */
1581 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1583 /* add to all windows! */
1586 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1587 wmEvent event= *win->eventstate;
1589 event.type= EVT_FILESELECT;
1590 event.val= eventval;
1591 event.customdata= ophandle; // only as void pointer type check
1593 wm_event_add(win, &event);
1597 /* operator is supposed to have a filled "path" property */
1598 /* optional property: filetype (XXX enum?) */
1600 /* Idea is to keep a handler alive on window queue, owning the operator.
1601 The filewindow can send event to make it execute, thus ensuring
1602 executing happens outside of lower level queues, with UI refreshed.
1603 Should also allow multiwin solutions */
1605 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1607 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1608 wmWindow *win= CTX_wm_window(C);
1609 int full= 1; // XXX preset?
1611 handler->type= WM_HANDLER_FILESELECT;
1613 handler->op_area= CTX_wm_area(C);
1614 handler->op_region= CTX_wm_region(C);
1615 handler->filescreen= CTX_wm_screen(C);
1617 BLI_addhead(&win->modalhandlers, handler);
1619 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1622 /* lets not expose struct outside wm? */
1623 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1625 handler->flag= flag;
1628 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1630 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1631 wmWindow *win= CTX_wm_window(C);
1633 /* operator was part of macro */
1635 /* give the mother macro to the handler */
1636 handler->op= op->opm;
1637 /* mother macro opm becomes the macro element */
1638 handler->op->opm= op;
1643 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
1644 handler->op_region= CTX_wm_region(C);
1646 BLI_addhead(&win->modalhandlers, handler);
1651 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1653 wmEventHandler *handler;
1656 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1660 /* only allow same keymap once */
1661 for(handler= handlers->first; handler; handler= handler->next)
1662 if(handler->keymap==keymap)
1665 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1666 BLI_addtail(handlers, handler);
1667 handler->keymap= keymap;
1672 /* priorities not implemented yet, for time being just insert in begin of list */
1673 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int priority)
1675 wmEventHandler *handler;
1677 WM_event_remove_keymap_handler(handlers, keymap);
1679 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1680 BLI_addhead(handlers, handler);
1681 handler->keymap= keymap;
1686 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1688 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1691 handler->bblocal= bblocal;
1692 handler->bbwin= bbwin;
1697 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1699 wmEventHandler *handler;
1701 for(handler= handlers->first; handler; handler= handler->next) {
1702 if(handler->keymap==keymap) {
1703 BLI_remlink(handlers, handler);
1704 wm_event_free_handler(handler);
1710 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1712 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1713 handler->ui_handle= func;
1714 handler->ui_remove= remove;
1715 handler->ui_userdata= userdata;
1716 handler->ui_area= (C)? CTX_wm_area(C): NULL;
1717 handler->ui_region= (C)? CTX_wm_region(C): NULL;
1718 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1720 BLI_addhead(handlers, handler);
1725 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1727 wmEventHandler *handler;
1729 for(handler= handlers->first; handler; handler= handler->next) {
1730 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1731 BLI_remlink(handlers, handler);
1732 wm_event_free_handler(handler);
1738 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
1740 wmEventHandler *handler;
1742 /* only allow same dropbox once */
1743 for(handler= handlers->first; handler; handler= handler->next)
1744 if(handler->dropboxes==dropboxes)
1747 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
1749 /* dropbox stored static, no free or copy */
1750 handler->dropboxes= dropboxes;
1751 BLI_addhead(handlers, handler);
1756 /* XXX solution works, still better check the real cause (ton) */
1757 void WM_event_remove_area_handler(ListBase *handlers, void *area)
1759 wmEventHandler *handler, *nexthandler;
1761 for(handler = handlers->first; handler; handler= nexthandler) {
1762 nexthandler = handler->next;
1763 if (handler->type != WM_HANDLER_FILESELECT) {
1764 if (handler->ui_area == area) {
1765 BLI_remlink(handlers, handler);
1766 wm_event_free_handler(handler);
1772 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
1774 BLI_remlink(handlers, handler);
1775 wm_event_free_handler(handler);
1778 void WM_event_add_mousemove(bContext *C)
1780 wmWindow *window= CTX_wm_window(C);
1782 window->addmousemove= 1;
1785 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
1786 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1788 /* user preset or keymap? dunno... */
1789 int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1791 switch(tweak_event) {
1795 if(evt->val==tweak_modal)
1798 /* this case is when modal callcback didnt get started with a tweak */
1805 /* ********************* ghost stuff *************** */
1807 static int convert_key(GHOST_TKey key)
1809 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1810 return (AKEY + ((int) key - GHOST_kKeyA));
1811 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1812 return (ZEROKEY + ((int) key - GHOST_kKey0));
1813 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1814 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1815 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1816 return (F1KEY + ((int) key - GHOST_kKeyF1));
1819 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
1820 case GHOST_kKeyTab: return TABKEY;
1821 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
1822 case GHOST_kKeyClear: return 0;
1823 case GHOST_kKeyEnter: return RETKEY;
1825 case GHOST_kKeyEsc: return ESCKEY;
1826 case GHOST_kKeySpace: return SPACEKEY;
1827 case GHOST_kKeyQuote: return QUOTEKEY;
1828 case GHOST_kKeyComma: return COMMAKEY;
1829 case GHOST_kKeyMinus: return MINUSKEY;
1830 case GHOST_kKeyPeriod: return PERIODKEY;
1831 case GHOST_kKeySlash: return SLASHKEY;
1833 case GHOST_kKeySemicolon: return SEMICOLONKEY;
1834 case GHOST_kKeyEqual: return EQUALKEY;
1836 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
1837 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
1838 case GHOST_kKeyBackslash: return BACKSLASHKEY;
1839 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
1841 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
1842 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
1843 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
1844 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
1845 case GHOST_kKeyCommand: return COMMANDKEY;
1846 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
1847 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
1849 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
1850 case GHOST_kKeyNumLock: return 0;
1851 case GHOST_kKeyScrollLock: return 0;
1853 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
1854 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
1855 case GHOST_kKeyUpArrow: return UPARROWKEY;
1856 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
1858 case GHOST_kKeyPrintScreen: return 0;
1859 case GHOST_kKeyPause: return PAUSEKEY;
1861 case GHOST_kKeyInsert: return INSERTKEY;
1862 case GHOST_kKeyDelete: return DELKEY;
1863 case GHOST_kKeyHome: return HOMEKEY;
1864 case GHOST_kKeyEnd: return ENDKEY;
1865 case GHOST_kKeyUpPage: return PAGEUPKEY;
1866 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
1868 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
1869 case GHOST_kKeyNumpadEnter: return PADENTER;
1870 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
1871 case GHOST_kKeyNumpadMinus: return PADMINUS;
1872 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
1873 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
1875 case GHOST_kKeyGrLess: return GRLESSKEY;
1878 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
1883 /* adds customdata to event */
1884 static void update_tablet_data(wmWindow *win, wmEvent *event)
1886 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
1888 /* if there's tablet data from an active tablet device then add it */
1889 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
1890 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
1892 wmtab->Active = (int)td->Active;
1893 wmtab->Pressure = td->Pressure;
1894 wmtab->Xtilt = td->Xtilt;
1895 wmtab->Ytilt = td->Ytilt;
1897 event->custom= EVT_DATA_TABLET;
1898 event->customdata= wmtab;
1899 event->customdatafree= 1;
1903 /* imperfect but probably usable... draw/enable drags to other windows */
1904 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
1906 short mx= evt->x, my= evt->y;
1908 if(wm->windows.first== wm->windows.last)
1911 /* top window bar... */
1912 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
1915 /* to desktop space */
1919 /* check other windows to see if it has mouse inside */
1920 for(owin= wm->windows.first; owin; owin= owin->next) {
1922 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
1923 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
1924 evt->x= mx-owin->posx;
1925 evt->y= my-owin->posy;
1935 /* windows store own event queues, no bContext here */
1936 /* time is in 1000s of seconds, from ghost */
1937 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int time, void *customdata)
1940 wmEvent event, *evt= win->eventstate;
1942 /* initialize and copy state (only mouse x y and modifiers) */
1947 case GHOST_kEventCursorMove: {
1949 GHOST_TEventCursorData *cd= customdata;
1951 #if defined(__APPLE__) && defined(GHOST_COCOA)
1952 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
1958 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
1960 evt->y= (win->sizey-1) - cy;
1966 event.type= MOUSEMOVE;
1968 update_tablet_data(win, &event);
1969 wm_event_add(win, &event);
1971 /* also add to other window if event is there, this makes overdraws disappear nicely */
1972 /* it remaps mousecoord to other window in event */
1973 owin= wm_event_cursor_other_windows(wm, win, &event);
1975 wmEvent oevent= *(owin->eventstate);
1979 oevent.type= MOUSEMOVE;
1981 *(owin->eventstate)= oevent;
1982 update_tablet_data(owin, &oevent);
1983 wm_event_add(owin, &oevent);
1989 case GHOST_kEventTrackpad: {
1991 GHOST_TEventTrackpadData * pd = customdata;
1992 switch (pd->subtype) {
1993 case GHOST_kTrackpadEventMagnify:
1994 event.type = MOUSEZOOM;
1996 case GHOST_kTrackpadEventRotate:
1997 event.type = MOUSEROTATE;
1999 case GHOST_kTrackpadEventScroll:
2001 event.type= MOUSEPAN;
2004 #if defined(__APPLE__) && defined(GHOST_COCOA)
2005 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2006 event.x= evt->x = pd->x;
2007 event.y = evt->y = pd->y;
2011 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2012 event.x= evt->x= cx;
2013 event.y= evt->y= (win->sizey-1) - cy;
2016 // Use prevx/prevy so we can calculate the delta later
2017 event.prevx= event.x - pd->deltaX;
2018 event.prevy= event.y - pd->deltaY;
2020 update_tablet_data(win, &event);
2021 wm_event_add(win, &event);
2026 case GHOST_kEventButtonDown:
2027 case GHOST_kEventButtonUp: {
2028 GHOST_TEventButtonData *bd= customdata;
2029 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 */
2031 if (bd->button == GHOST_kButtonMaskLeft)
2032 event.type= LEFTMOUSE;
2033 else if (bd->button == GHOST_kButtonMaskRight)
2034 event.type= RIGHTMOUSE;
2035 else if (bd->button == GHOST_kButtonMaskButton4)
2036 event.type= BUTTON4MOUSE;
2037 else if (bd->button == GHOST_kButtonMaskButton5)
2038 event.type= BUTTON5MOUSE;
2040 event.type= MIDDLEMOUSE;
2042 /* add to other window if event is there (not to both!) */
2043 owin= wm_event_cursor_other_windows(wm, win, &event);
2045 wmEvent oevent= *(owin->eventstate);
2049 oevent.type= event.type;
2050 oevent.val= event.val;
2052 update_tablet_data(owin, &oevent);
2053 wm_event_add(owin, &oevent);
2056 update_tablet_data(win, &event);
2057 wm_event_add(win, &event);
2063 case GHOST_kEventKeyDown:
2064 case GHOST_kEventKeyUp: {
2065 GHOST_TEventKeyData *kd= customdata;
2066 event.type= convert_key(kd->key);
2067 event.ascii= kd->ascii;
2068 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2070 /* exclude arrow keys, esc, etc from text input */
2071 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2075 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2076 event.shift= evt->shift= (event.val==KM_PRESS);
2077 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2078 event.shift= evt->shift = 3; // define?
2080 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2081 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2082 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2083 event.ctrl= evt->ctrl = 3; // define?
2085 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2086 event.alt= evt->alt= (event.val==KM_PRESS);
2087 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2088 event.alt= evt->alt = 3; // define?
2090 else if (event.type==COMMANDKEY) {
2091 event.oskey= evt->oskey= (event.val==KM_PRESS);
2092 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2093 event.oskey= evt->oskey = 3; // define?
2096 if(event.val==KM_PRESS && event.keymodifier==0)
2097 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2098 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2099 event.keymodifier= evt->keymodifier= 0;
2102 /* this case happens on some systems that on holding a key pressed,
2103 generate press events without release, we still want to keep the
2104 modifier in win->eventstate, but for the press event of the same
2105 key we don't want the key modifier */
2106 if(event.keymodifier == event.type)
2107 event.keymodifier= 0;
2109 /* if test_break set, it catches this. XXX Keep global for now? */
2110 if(event.type==ESCKEY)
2113 wm_event_add(win, &event);
2118 case GHOST_kEventWheel: {
2119 GHOST_TEventWheelData* wheelData = customdata;
2121 if (wheelData->z > 0)
2122 event.type= WHEELUPMOUSE;
2124 event.type= WHEELDOWNMOUSE;
2126 event.val= KM_PRESS;
2127 wm_event_add(win, &event);
2131 case GHOST_kEventTimer: {
2133 event.custom= EVT_DATA_TIMER;
2134 event.customdata= customdata;
2135 wm_event_add(win, &event);
2140 case GHOST_kEventUnknown:
2141 case GHOST_kNumEventTypes:
2144 case GHOST_kEventWindowDeactivate: {
2145 event.type= WINDEACTIVATE;
2146 wm_event_add(win, &event);