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 *****
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_screen.h"
52 #include "BKE_utildefines.h"
53 #include "BKE_sound.h"
55 #include "ED_fileselect.h"
57 #include "ED_screen.h"
58 #include "ED_space_api.h"
61 #include "RNA_access.h"
63 #include "UI_interface.h"
70 #include "wm_window.h"
71 #include "wm_event_system.h"
72 #include "wm_event_types.h"
74 /* ************ event management ************** */
76 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
78 wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
80 *event= *event_to_add;
81 BLI_addtail(&win->queue, event);
84 void wm_event_free(wmEvent *event)
86 if(event->customdata) {
87 if(event->customdatafree) {
88 /* note: pointer to listbase struct elsewhere */
89 if(event->custom==EVT_DATA_LISTBASE)
90 BLI_freelistN(event->customdata);
92 MEM_freeN(event->customdata);
98 void wm_event_free_all(wmWindow *win)
102 while((event= win->queue.first)) {
103 BLI_remlink(&win->queue, event);
104 wm_event_free(event);
108 /* ********************* notifiers, listeners *************** */
110 /* XXX: in future, which notifiers to send to other windows? */
111 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
113 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
115 note->wm= CTX_wm_manager(C);
116 BLI_addtail(¬e->wm->queue, note);
118 note->window= CTX_wm_window(C);
121 note->swinid= CTX_wm_region(C)->swinid;
123 note->category= type & NOTE_CATEGORY;
124 note->data= type & NOTE_DATA;
125 note->subtype= type & NOTE_SUBTYPE;
126 note->action= type & NOTE_ACTION;
128 note->reference= reference;
131 void WM_main_add_notifier(unsigned int type, void *reference)
134 wmWindowManager *wm= bmain->wm.first;
137 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
140 BLI_addtail(¬e->wm->queue, note);
142 note->category= type & NOTE_CATEGORY;
143 note->data= type & NOTE_DATA;
144 note->subtype= type & NOTE_SUBTYPE;
145 note->action= type & NOTE_ACTION;
147 note->reference= reference;
151 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
153 wmNotifier *note= wm->queue.first;
155 if(note) BLI_remlink(&wm->queue, note);
159 /* called in mainloop */
160 void wm_event_do_notifiers(bContext *C)
162 wmWindowManager *wm= CTX_wm_manager(C);
163 wmNotifier *note, *next;
169 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
170 for(win= wm->windows.first; win; win= win->next) {
173 CTX_wm_window_set(C, win);
175 for(note= wm->queue.first; note; note= next) {
178 if(note->category==NC_WM) {
179 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
181 wm_window_title(wm, win);
183 else if(note->data==ND_DATACHANGED)
184 wm_window_title(wm, win);
186 if(note->window==win) {
187 if(note->category==NC_SCREEN) {
188 if(note->data==ND_SCREENBROWSE) {
189 ED_screen_set(C, note->reference); // XXX hrms, think this over!
191 printf("screen set %p\n", note->reference);
193 else if(note->data==ND_SCREENDELETE) {
194 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
196 printf("screen delete %p\n", note->reference);
201 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
202 if(note->category==NC_SCENE) {
203 if(note->data==ND_SCENEBROWSE) {
204 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
206 printf("scene set %p\n", note->reference);
208 else if(note->data==ND_FRAME)
211 if(note->action == NA_REMOVED) {
212 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
214 printf("scene delete %p\n", note->reference);
219 if(ELEM4(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE)) {
220 ED_info_stats_clear(CTX_data_scene(C));
221 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
225 /* depsgraph gets called, might send more notifiers */
226 ED_update_for_newframe(C, 1);
230 /* the notifiers are sent without context, to keep it clean */
231 while( (note=wm_notifier_next(wm)) ) {
234 for(win= wm->windows.first; win; win= win->next) {
236 /* filter out notifiers */
237 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
238 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
243 /* XXX context in notifiers? */
244 CTX_wm_window_set(C, win);
246 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
247 ED_screen_do_listen(win, note);
249 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
250 ED_region_do_listen(ar, note);
253 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
254 ED_area_do_listen(sa, note);
255 for(ar=sa->regionbase.first; ar; ar= ar->next) {
256 ED_region_do_listen(ar, note);
265 /* cached: editor refresh callbacks now, they get context */
266 for(win= wm->windows.first; win; win= win->next) {
269 CTX_wm_window_set(C, win);
270 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
272 CTX_wm_area_set(C, sa);
273 ED_area_do_refresh(C, sa);
277 /* XXX make lock in future, or separated derivedmesh users in scene */
279 /* depsgraph & animation: update tagged datablocks */
280 scene_update_tagged(win->screen->scene);
283 CTX_wm_window_set(C, NULL);
286 /* ********************* ui handler ******************* */
288 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
290 ScrArea *area= CTX_wm_area(C);
291 ARegion *region= CTX_wm_region(C);
292 ARegion *menu= CTX_wm_menu(C);
295 /* we set context to where ui handler came from */
296 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
297 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
298 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
300 retval= handler->ui_handle(C, event, handler->ui_userdata);
302 /* putting back screen context */
303 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
304 CTX_wm_area_set(C, area);
305 CTX_wm_region_set(C, region);
306 CTX_wm_menu_set(C, menu);
309 /* this special cases is for areas and regions that get removed */
310 CTX_wm_area_set(C, NULL);
311 CTX_wm_region_set(C, NULL);
312 CTX_wm_menu_set(C, NULL);
315 if(retval == WM_UI_HANDLER_BREAK)
316 return WM_HANDLER_BREAK;
318 return WM_HANDLER_CONTINUE;
321 static void wm_handler_ui_cancel(bContext *C)
323 wmWindow *win= CTX_wm_window(C);
324 ARegion *ar= CTX_wm_region(C);
325 wmEventHandler *handler, *nexthandler;
330 for(handler= ar->handlers.first; handler; handler= nexthandler) {
331 nexthandler= handler->next;
333 if(handler->ui_handle) {
334 wmEvent event= *(win->eventstate);
335 event.type= EVT_BUT_CANCEL;
336 handler->ui_handle(C, &event, handler->ui_userdata);
341 /* ********************* operators ******************* */
343 int WM_operator_poll(bContext *C, wmOperatorType *ot)
345 wmOperatorTypeMacro *otmacro;
347 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
348 wmOperatorType *ot= WM_operatortype_find(otmacro->idname, 0);
350 if(0==WM_operator_poll(C, ot))
354 /* python needs operator type, so we added exception for it */
356 return ot->pyop_poll(C, ot);
363 static void wm_operator_finished(bContext *C, wmOperator *op, int repeat)
365 wmWindowManager *wm= CTX_wm_manager(C);
367 op->customdata= NULL;
369 /* we don't want to do undo pushes for operators that are being
370 called from operators that already do an undo push. usually
371 this will happen for python operators that call C operators */
372 if(wm->op_undo_depth == 0)
373 if(op->type->flag & OPTYPE_UNDO)
374 ED_undo_push_op(C, op);
378 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
379 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
383 if((wm->op_undo_depth == 0) && (op->type->flag & OPTYPE_REGISTER))
384 wm_operator_register(C, op);
386 WM_operator_free(op);
390 /* if repeat is true, it doesn't register again, nor does it free */
391 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
393 wmWindowManager *wm= CTX_wm_manager(C);
394 int retval= OPERATOR_CANCELLED;
396 if(op==NULL || op->type==NULL)
399 if(0==WM_operator_poll(C, op->type))
403 if(op->type->flag & OPTYPE_UNDO)
406 retval= op->type->exec(C, op);
408 if(op->type->flag & OPTYPE_UNDO)
412 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
413 if(op->reports->list.first)
414 uiPupMenuReports(C, op->reports);
416 if(retval & OPERATOR_FINISHED)
417 wm_operator_finished(C, op, repeat);
419 WM_operator_free(op);
421 return retval | OPERATOR_HANDLED;
425 /* for running operators with frozen context (modal handlers, menus) */
426 int WM_operator_call(bContext *C, wmOperator *op)
428 return wm_operator_exec(C, op, 0);
431 /* do this operator again, put here so it can share above code */
432 int WM_operator_repeat(bContext *C, wmOperator *op)
434 return wm_operator_exec(C, op, 1);
437 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
439 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
441 /* XXX adding new operator could be function, only happens here now */
443 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
445 /* initialize properties, either copy or create */
446 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
447 if(properties && properties->data) {
448 op->properties= IDP_CopyProperty(properties->data);
451 IDPropertyTemplate val = {0};
452 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
454 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
456 /* initialize error reports */
458 op->reports= reports; /* must be initialized already */
461 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
462 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
465 /* recursive filling of operator macro list */
466 if(ot->macro.first) {
467 static wmOperator *motherop= NULL;
468 wmOperatorTypeMacro *otmacro;
471 /* ensure all ops are in execution order in 1 list */
478 /* if properties exist, it will contain everything needed */
480 otmacro= ot->macro.first;
482 RNA_STRUCT_BEGIN(properties, prop) {
487 /* skip invalid properties */
488 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
490 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
491 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
492 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
494 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
496 BLI_addtail(&motherop->macro, opm);
497 opm->opm= motherop; /* pointer to mom, for modal() */
499 otmacro= otmacro->next;
504 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
505 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
506 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
508 BLI_addtail(&motherop->macro, opm);
509 opm->opm= motherop; /* pointer to mom, for modal() */
517 WM_operator_properties_sanitize(op->ptr, 0);
522 static void wm_operator_print(wmOperator *op)
524 char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
529 static void wm_region_mouse_co(bContext *C, wmEvent *event)
531 ARegion *ar= CTX_wm_region(C);
533 /* compatibility convention */
534 event->mval[0]= event->x - ar->winrct.xmin;
535 event->mval[1]= event->y - ar->winrct.ymin;
539 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
541 wmWindowManager *wm= CTX_wm_manager(C);
542 int retval= OPERATOR_PASS_THROUGH;
544 if(WM_operator_poll(C, ot)) {
545 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
547 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
548 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
550 if(op->type->invoke && event) {
551 wm_region_mouse_co(C, event);
553 if(op->type->flag & OPTYPE_UNDO)
556 retval= op->type->invoke(C, op, event);
558 if(op->type->flag & OPTYPE_UNDO)
561 else if(op->type->exec) {
562 if(op->type->flag & OPTYPE_UNDO)
565 retval= op->type->exec(C, op);
567 if(op->type->flag & OPTYPE_UNDO)
571 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
573 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
574 * currently python only uses this */
575 if((retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED)) && reports==NULL)
576 if(op->reports->list.first) /* only show the report if the report list was not given in the function */
577 uiPupMenuReports(C, op->reports);
579 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 */
581 wm_operator_print(op);
584 if(retval & OPERATOR_HANDLED)
585 ; /* do nothing, wm_operator_exec() has been called somewhere */
586 else if(retval & OPERATOR_FINISHED) {
587 wm_operator_finished(C, op, 0);
589 else if(retval & OPERATOR_RUNNING_MODAL) {
590 /* grab cursor during blocking modal ops (X11)
591 * Also check for macro
593 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
594 int bounds[4] = {-1,-1,-1,-1};
598 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
600 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
604 ARegion *ar= CTX_wm_region(C);
606 bounds[0]= ar->winrct.xmin;
607 bounds[1]= ar->winrct.ymax;
608 bounds[2]= ar->winrct.xmax;
609 bounds[3]= ar->winrct.ymin;
613 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
616 /* cancel UI handlers, typically tooltips that can hang around
617 while dragging the view or worse, that stay there permanently
618 after the modal operator has swallowed all events and passed
619 none to the UI handler */
620 wm_handler_ui_cancel(C);
623 WM_operator_free(op);
629 /* WM_operator_name_call is the main accessor function
630 * this is for python to access since its done the operator lookup
632 * invokes operator in context */
633 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
635 wmWindow *window= CTX_wm_window(C);
643 case WM_OP_INVOKE_DEFAULT:
644 case WM_OP_INVOKE_REGION_WIN:
645 case WM_OP_INVOKE_AREA:
646 case WM_OP_INVOKE_SCREEN:
647 /* window is needed for invoke, cancel operator */
651 event= window->eventstate;
659 case WM_OP_EXEC_REGION_WIN:
660 case WM_OP_INVOKE_REGION_WIN:
661 case WM_OP_EXEC_REGION_CHANNELS:
662 case WM_OP_INVOKE_REGION_CHANNELS:
663 case WM_OP_EXEC_REGION_PREVIEW:
664 case WM_OP_INVOKE_REGION_PREVIEW:
666 /* forces operator to go to the region window/channels/preview, for header menus
667 * but we stay in the same region if we are already in one
669 ARegion *ar= CTX_wm_region(C);
670 ScrArea *area= CTX_wm_area(C);
671 int type = RGN_TYPE_WINDOW;
674 case WM_OP_EXEC_REGION_CHANNELS:
675 case WM_OP_INVOKE_REGION_CHANNELS:
676 type = RGN_TYPE_CHANNELS;
678 case WM_OP_EXEC_REGION_PREVIEW:
679 case WM_OP_INVOKE_REGION_PREVIEW:
680 type = RGN_TYPE_PREVIEW;
683 case WM_OP_EXEC_REGION_WIN:
684 case WM_OP_INVOKE_REGION_WIN:
686 type = RGN_TYPE_WINDOW;
690 if(!(ar && ar->regiontype == type) && area) {
691 ARegion *ar1= BKE_area_find_region_type(area, type);
693 CTX_wm_region_set(C, ar1);
696 retval= wm_operator_invoke(C, ot, event, properties, reports);
698 /* set region back */
699 CTX_wm_region_set(C, ar);
703 case WM_OP_EXEC_AREA:
704 case WM_OP_INVOKE_AREA:
706 /* remove region from context */
707 ARegion *ar= CTX_wm_region(C);
709 CTX_wm_region_set(C, NULL);
710 retval= wm_operator_invoke(C, ot, event, properties, reports);
711 CTX_wm_region_set(C, ar);
715 case WM_OP_EXEC_SCREEN:
716 case WM_OP_INVOKE_SCREEN:
718 /* remove region + area from context */
719 ARegion *ar= CTX_wm_region(C);
720 ScrArea *area= CTX_wm_area(C);
722 CTX_wm_region_set(C, NULL);
723 CTX_wm_area_set(C, NULL);
724 retval= wm_operator_invoke(C, ot, event, properties, reports);
725 CTX_wm_region_set(C, ar);
726 CTX_wm_area_set(C, area);
730 case WM_OP_EXEC_DEFAULT:
731 case WM_OP_INVOKE_DEFAULT:
732 return wm_operator_invoke(C, ot, event, properties, reports);
740 /* invokes operator in context */
741 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
743 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
745 return wm_operator_call_internal(C, ot, context, properties, NULL);
750 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
751 - wmOperatorType is used instead of operator name since python alredy has the operator type
752 - poll() must be called by python before this runs.
753 - reports can be passed to this function (so python can report them as exceptions)
755 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
757 int retval= OPERATOR_CANCELLED;
761 wmWindowManager *wm= CTX_wm_manager(C);
762 op= wm_operator_create(wm, ot, properties, reports);
764 if (op->type->exec) {
765 if(op->type->flag & OPTYPE_UNDO)
768 retval= op->type->exec(C, op);
770 if(op->type->flag & OPTYPE_UNDO)
774 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
777 retval= wm_operator_call_internal(C, ot, context, properties, reports);
779 /* keep the reports around if needed later */
780 if (retval & OPERATOR_RUNNING_MODAL || ot->flag & OPTYPE_REGISTER)
782 reports->flag |= RPT_FREE;
789 /* ********************* handlers *************** */
791 /* future extra customadata free? */
792 void wm_event_free_handler(wmEventHandler *handler)
797 /* only set context when area/region is part of screen */
798 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
800 bScreen *screen= CTX_wm_screen(C);
802 if(screen && handler->op) {
803 if(handler->op_area==NULL)
804 CTX_wm_area_set(C, NULL);
808 for(sa= screen->areabase.first; sa; sa= sa->next)
809 if(sa==handler->op_area)
812 /* when changing screen layouts with running modal handlers (like render display), this
813 is not an error to print */
814 if(handler->op==NULL)
815 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
819 CTX_wm_area_set(C, sa);
820 for(ar= sa->regionbase.first; ar; ar= ar->next)
821 if(ar==handler->op_region)
823 /* XXX no warning print here, after full-area and back regions are remade */
825 CTX_wm_region_set(C, ar);
831 /* called on exit or remove area, only here call cancel callback */
832 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
834 wmEventHandler *handler;
835 wmWindowManager *wm= CTX_wm_manager(C);
837 /* C is zero on freeing database, modal handlers then already were freed */
838 while((handler=handlers->first)) {
839 BLI_remlink(handlers, handler);
842 if(handler->op->type->cancel) {
843 ScrArea *area= CTX_wm_area(C);
844 ARegion *region= CTX_wm_region(C);
846 wm_handler_op_context(C, handler);
848 if(handler->op->type->flag & OPTYPE_UNDO)
851 handler->op->type->cancel(C, handler->op);
853 if(handler->op->type->flag & OPTYPE_UNDO)
856 CTX_wm_area_set(C, area);
857 CTX_wm_region_set(C, region);
860 WM_cursor_ungrab(CTX_wm_window(C));
861 WM_operator_free(handler->op);
863 else if(handler->ui_remove) {
864 ScrArea *area= CTX_wm_area(C);
865 ARegion *region= CTX_wm_region(C);
866 ARegion *menu= CTX_wm_menu(C);
868 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
869 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
870 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
872 handler->ui_remove(C, handler->ui_userdata);
874 CTX_wm_area_set(C, area);
875 CTX_wm_region_set(C, region);
876 CTX_wm_menu_set(C, menu);
879 wm_event_free_handler(handler);
883 /* do userdef mappings */
884 int WM_userdef_event_map(int kmitype)
888 if(U.flag & USER_LMOUSESELECT)
894 if(U.flag & USER_LMOUSESELECT)
900 if(U.uiflag & USER_WHEELZOOMDIR)
903 return WHEELDOWNMOUSE;
906 if(U.uiflag & USER_WHEELZOOMDIR)
907 return WHEELDOWNMOUSE;
912 if(U.flag & USER_LMOUSESELECT)
918 if(U.flag & USER_LMOUSESELECT)
927 static void wm_eventemulation(wmEvent *event)
929 static int mmb_emulated = 0; /* this should be in a data structure somwhere */
931 /* middlemouse emulation */
932 if(U.flag & USER_TWOBUTTONMOUSE) {
933 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
934 event->type = MIDDLEMOUSE;
936 mmb_emulated = event->val;
941 /* rightmouse emulation */
942 if(U.flag & USER_TWOBUTTONMOUSE) {
943 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
944 event->type = RIGHTMOUSE;
946 mmb_emulated = event->val;
951 /* numpad emulation */
952 if(U.flag & USER_NONUMPAD) {
953 switch(event->type) {
954 case ZEROKEY: event->type = PAD0; break;
955 case ONEKEY: event->type = PAD1; break;
956 case TWOKEY: event->type = PAD2; break;
957 case THREEKEY: event->type = PAD3; break;
958 case FOURKEY: event->type = PAD4; break;
959 case FIVEKEY: event->type = PAD5; break;
960 case SIXKEY: event->type = PAD6; break;
961 case SEVENKEY: event->type = PAD7; break;
962 case EIGHTKEY: event->type = PAD8; break;
963 case NINEKEY: event->type = PAD9; break;
964 case MINUSKEY: event->type = PADMINUS; break;
965 case EQUALKEY: event->type = PADPLUSKEY; break;
966 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
971 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
973 int kmitype= WM_userdef_event_map(kmi->type);
975 if(kmi->flag & KMI_INACTIVE) return 0;
977 /* the matching rules */
978 if(kmitype==KM_TEXTINPUT)
979 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
981 if(winevent->type!=kmitype) return 0;
984 if(winevent->val!=kmi->val) return 0;
986 /* modifiers also check bits, so it allows modifier order */
987 if(kmi->shift!=KM_ANY)
988 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
989 if(kmi->ctrl!=KM_ANY)
990 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
992 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
993 if(kmi->oskey!=KM_ANY)
994 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
997 if(winevent->keymodifier!=kmi->keymodifier) return 0;
999 /* key modifiers always check when event has it */
1000 /* otherwise regular keypresses with keymodifier still work */
1001 if(winevent->keymodifier)
1002 if(ISTEXTINPUT(winevent->type))
1003 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1008 static int wm_event_always_pass(wmEvent *event)
1010 /* some events we always pass on, to ensure proper communication */
1011 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
1014 /* operator exists */
1015 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1017 /* support for modal keymap in macros */
1021 if(op->type->modalkeymap) {
1022 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1025 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1026 if(wm_eventmatch(event, kmi)) {
1028 event->type= EVT_MODAL_MAP;
1029 event->val= kmi->propvalue;
1035 /* Warning: this function removes a modal handler, when finished */
1036 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1038 int retval= OPERATOR_PASS_THROUGH;
1040 /* derived, modal or blocking operator */
1042 wmOperator *op= handler->op;
1043 wmOperatorType *ot= op->type;
1046 /* we set context to where modal handler came from */
1047 wmWindowManager *wm= CTX_wm_manager(C);
1048 ScrArea *area= CTX_wm_area(C);
1049 ARegion *region= CTX_wm_region(C);
1051 wm_handler_op_context(C, handler);
1052 wm_region_mouse_co(C, event);
1053 wm_event_modalkeymap(C, op, event);
1055 if(ot->flag & OPTYPE_UNDO)
1056 wm->op_undo_depth++;
1058 retval= ot->modal(C, op, event);
1060 if(ot->flag & OPTYPE_UNDO)
1061 wm->op_undo_depth--;
1063 /* putting back screen context, reval can pass trough after modal failures! */
1064 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1065 CTX_wm_area_set(C, area);
1066 CTX_wm_region_set(C, region);
1069 /* this special cases is for areas and regions that get removed */
1070 CTX_wm_area_set(C, NULL);
1071 CTX_wm_region_set(C, NULL);
1074 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
1075 if(op->reports->list.first)
1076 uiPupMenuReports(C, op->reports);
1078 if (retval & OPERATOR_FINISHED) {
1080 wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
1083 if(retval & OPERATOR_FINISHED) {
1084 wm_operator_finished(C, op, 0);
1087 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1088 WM_operator_free(op);
1092 /* remove modal handler, operator itself should have been cancelled and freed */
1093 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1094 WM_cursor_ungrab(CTX_wm_window(C));
1096 BLI_remlink(handlers, handler);
1097 wm_event_free_handler(handler);
1099 /* prevent silly errors from operator users */
1100 //retval &= ~OPERATOR_PASS_THROUGH;
1105 printf("wm_handler_operator_call error\n");
1108 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1111 retval= wm_operator_invoke(C, ot, event, properties, NULL);
1114 /* Finished and pass through flag as handled */
1115 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1116 return WM_HANDLER_HANDLED;
1118 /* Modal unhandled, break */
1119 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1120 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1122 if(retval & OPERATOR_PASS_THROUGH)
1123 return WM_HANDLER_CONTINUE;
1125 return WM_HANDLER_BREAK;
1128 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1129 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1131 wmWindowManager *wm= CTX_wm_manager(C);
1133 int action= WM_HANDLER_CONTINUE;
1135 if(event->type != EVT_FILESELECT)
1137 if(handler->op != (wmOperator *)event->customdata)
1140 switch(event->val) {
1141 case EVT_FILESELECT_OPEN:
1142 case EVT_FILESELECT_FULL_OPEN:
1146 /* sa can be null when window A is active, but mouse is over window B */
1147 /* in this case, open file select in original window A */
1148 if (handler->op_area == NULL) {
1149 bScreen *screen = CTX_wm_screen(C);
1150 sa = (ScrArea *)screen->areabase.first;
1152 sa = handler->op_area;
1154 if(event->val==EVT_FILESELECT_OPEN)
1155 ED_area_newspace(C, sa, SPACE_FILE);
1157 ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1159 /* settings for filebrowser, sfile is not operator owner but sends events */
1160 sa = CTX_wm_area(C);
1161 sfile= (SpaceFile*)sa->spacedata.first;
1162 sfile->op= handler->op;
1164 ED_fileselect_set_params(sfile);
1166 action= WM_HANDLER_BREAK;
1170 case EVT_FILESELECT_EXEC:
1171 case EVT_FILESELECT_CANCEL:
1173 /* XXX validate area and region? */
1174 bScreen *screen= CTX_wm_screen(C);
1175 char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
1177 if(screen != handler->filescreen)
1178 ED_screen_full_prevspace(C, CTX_wm_area(C));
1180 ED_area_prevspace(C, CTX_wm_area(C));
1182 /* remlink now, for load file case */
1183 BLI_remlink(handlers, handler);
1185 wm_handler_op_context(C, handler);
1187 /* needed for uiPupMenuReports */
1189 if(event->val==EVT_FILESELECT_EXEC) {
1190 /* a bit weak, might become arg for WM_event_fileselect? */
1191 /* XXX also extension code in image-save doesnt work for this yet */
1192 if (RNA_struct_find_property(handler->op->ptr, "check_existing") &&
1193 RNA_boolean_get(handler->op->ptr, "check_existing")) {
1194 /* this gives ownership to pupmenu */
1195 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1200 if(handler->op->type->flag & OPTYPE_UNDO)
1201 wm->op_undo_depth++;
1203 retval= handler->op->type->exec(C, handler->op);
1205 if(handler->op->type->flag & OPTYPE_UNDO)
1206 wm->op_undo_depth--;
1208 if (retval & OPERATOR_FINISHED)
1210 wm_operator_print(handler->op);
1212 if(wm->op_undo_depth == 0)
1213 if(handler->op->type->flag & OPTYPE_UNDO)
1214 ED_undo_push_op(C, handler->op);
1216 if(handler->op->reports->list.first) {
1218 /* FIXME, temp setting window, this is really bad!
1219 * only have because lib linking errors need to be seen by users :(
1220 * it can be removed without breaking anything but then no linking errors - campbell */
1221 wmWindow *win_prev= CTX_wm_window(C);
1223 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1225 handler->op->reports->printlevel = RPT_WARNING;
1226 uiPupMenuReports(C, handler->op->reports);
1228 CTX_wm_window_set(C, win_prev);
1231 WM_operator_free(handler->op);
1235 if(handler->op->type->cancel) {
1236 if(handler->op->type->flag & OPTYPE_UNDO)
1237 wm->op_undo_depth++;
1239 handler->op->type->cancel(C, handler->op);
1241 if(handler->op->type->flag & OPTYPE_UNDO)
1242 wm->op_undo_depth--;
1245 WM_operator_free(handler->op);
1248 CTX_wm_area_set(C, NULL);
1250 wm_event_free_handler(handler);
1254 action= WM_HANDLER_BREAK;
1262 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1264 if(handler->bbwin) {
1265 if(handler->bblocal) {
1266 rcti rect= *handler->bblocal;
1267 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1269 if(BLI_in_rcti(&rect, event->x, event->y))
1271 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1277 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1279 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1288 static int wm_action_not_handled(int action)
1290 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1293 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1295 wmWindowManager *wm= CTX_wm_manager(C);
1296 wmEventHandler *handler, *nexthandler;
1297 int action= WM_HANDLER_CONTINUE;
1300 if(handlers==NULL) return action;
1302 /* modal handlers can get removed in this loop, we keep the loop this way */
1303 for(handler= handlers->first; handler; handler= nexthandler) {
1304 nexthandler= handler->next;
1306 /* optional boundbox */
1307 if(handler_boundbox_test(handler, event)) {
1308 /* in advance to avoid access to freed event on window close */
1309 always_pass= wm_event_always_pass(event);
1311 /* modal+blocking handler */
1312 if(handler->flag & WM_HANDLER_BLOCKING)
1313 action |= WM_HANDLER_BREAK;
1315 if(handler->keymap) {
1316 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1319 if(!keymap->poll || keymap->poll(C)) {
1320 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1321 if(wm_eventmatch(event, kmi)) {
1323 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1325 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1326 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1332 else if(handler->ui_handle) {
1333 action |= wm_handler_ui_call(C, handler, event, always_pass);
1335 else if(handler->type==WM_HANDLER_FILESELECT) {
1336 /* screen context changes here */
1337 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1339 else if(handler->dropboxes) {
1340 if(event->type==EVT_DROP) {
1341 wmDropBox *drop= handler->dropboxes->first;
1342 for(; drop; drop= drop->next) {
1343 /* other drop custom types allowed */
1344 if(event->custom==EVT_DATA_LISTBASE) {
1345 ListBase *lb= (ListBase *)event->customdata;
1347 for(drag= lb->first; drag; drag= drag->next) {
1348 if(drop->poll(C, drag, event)) {
1349 drop->copy(drag, drop);
1351 wm_operator_invoke(C, drop->ot, event, drop->ptr, NULL);
1352 action |= WM_HANDLER_BREAK;
1360 /* modal, swallows all */
1361 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1364 if(action & WM_HANDLER_BREAK) {
1366 action &= ~WM_HANDLER_BREAK;
1373 if(CTX_wm_window(C)==NULL)
1377 /* test for CLICK event */
1378 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1379 wmWindow *win = CTX_wm_window(C);
1381 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1382 /* test for double click first */
1383 if ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time) {
1384 event->val = KM_DBL_CLICK;
1385 action |= wm_handlers_do(C, event, handlers);
1388 if (wm_action_not_handled(action)) {
1389 event->val = KM_CLICK;
1390 action |= wm_handlers_do(C, event, handlers);
1394 /* revert value if not handled */
1395 if (wm_action_not_handled(action)) {
1396 event->val = KM_RELEASE;
1404 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1406 if(wm_event_always_pass(event))
1408 if(BLI_in_rcti(rect, event->x, event->y))
1410 if(event->type==MOUSEMOVE) {
1411 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1419 static ScrArea *area_event_inside(bContext *C, int x, int y)
1421 bScreen *screen= CTX_wm_screen(C);
1425 for(sa= screen->areabase.first; sa; sa= sa->next)
1426 if(BLI_in_rcti(&sa->totrct, x, y))
1431 static ARegion *region_event_inside(bContext *C, int x, int y)
1433 bScreen *screen= CTX_wm_screen(C);
1434 ScrArea *area= CTX_wm_area(C);
1438 for(ar= area->regionbase.first; ar; ar= ar->next)
1439 if(BLI_in_rcti(&ar->winrct, x, y))
1444 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1447 for(; pc; pc= pc->next) {
1448 if(pc->poll == NULL || pc->poll(C)) {
1449 wmWindow *win= CTX_wm_window(C);
1450 win->screen->do_draw_paintcursor= 1;
1452 if(win->drawmethod != USER_DRAW_TRIPLE)
1453 ED_region_tag_redraw(ar);
1459 /* called on mousemove, check updates for paintcursors */
1460 /* context was set on active area and region */
1461 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1463 wmWindowManager *wm= CTX_wm_manager(C);
1465 if(wm->paintcursors.first) {
1466 ARegion *ar= CTX_wm_region(C);
1468 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1470 /* if previous position was not in current region, we have to set a temp new context */
1471 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1472 ScrArea *sa= CTX_wm_area(C);
1474 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1475 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1477 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1479 CTX_wm_area_set(C, sa);
1480 CTX_wm_region_set(C, ar);
1485 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1487 if(wm->drags.first==NULL) return;
1489 if(event->type==MOUSEMOVE)
1490 win->screen->do_draw_drag= 1;
1491 else if(event->type==ESCKEY) {
1492 BLI_freelistN(&wm->drags);
1493 win->screen->do_draw_drag= 1;
1495 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1496 event->type= EVT_DROP;
1498 /* create customdata, first free existing */
1499 if(event->customdata) {
1500 if(event->customdatafree)
1501 MEM_freeN(event->customdata);
1504 event->custom= EVT_DATA_LISTBASE;
1505 event->customdata= &wm->drags;
1506 event->customdatafree= 1;
1508 /* clear drop icon */
1509 win->screen->do_draw_drag= 1;
1511 /* restore cursor (disabled, see wm_dragdrop.c) */
1512 // WM_cursor_restore(win);
1515 /* overlap fails otherwise */
1516 if(win->screen->do_draw_drag)
1517 if(win->drawmethod == USER_DRAW_OVERLAP)
1518 win->screen->do_draw= 1;
1522 /* called in main loop */
1523 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1524 void wm_event_do_handlers(bContext *C)
1526 wmWindowManager *wm= CTX_wm_manager(C);
1529 for(win= wm->windows.first; win; win= win->next) {
1532 if( win->screen==NULL )
1533 wm_event_free_all(win);
1536 if(win->screen->scene)
1538 int playing = sound_scene_playing(win->screen->scene);
1541 if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer)))
1543 CTX_wm_window_set(C, win);
1544 CTX_wm_screen_set(C, win->screen);
1545 CTX_data_scene_set(C, win->screen->scene);
1547 ED_screen_animation_play(C, -1, 1);
1549 CTX_data_scene_set(C, NULL);
1550 CTX_wm_screen_set(C, NULL);
1551 CTX_wm_window_set(C, NULL);
1557 while( (event= win->queue.first) ) {
1558 int action = WM_HANDLER_CONTINUE;
1560 wm_eventemulation(event);
1562 CTX_wm_window_set(C, win);
1564 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1565 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1566 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1568 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1569 wm_window_make_drawable(C, win);
1571 /* first we do priority handlers, modal + some limited keymaps */
1572 action |= wm_handlers_do(C, event, &win->modalhandlers);
1575 if(CTX_wm_window(C)==NULL)
1578 /* check dragging, creates new event or frees, adds draw tag */
1579 wm_event_drag_test(wm, win, event);
1581 /* builtin tweak, if action is break it removes tweak */
1582 wm_tweakevent_test(C, event, action);
1584 if((action & WM_HANDLER_BREAK) == 0) {
1589 /* XXX to solve, here screen handlers? */
1590 if(event->type==MOUSEMOVE) {
1591 /* state variables in screen, cursors */
1592 ED_screen_set_subwinactive(win, event);
1593 /* for regions having custom cursors */
1594 wm_paintcursor_test(C, event);
1597 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1598 if(wm_event_inside_i(event, &sa->totrct)) {
1599 CTX_wm_area_set(C, sa);
1601 if((action & WM_HANDLER_BREAK) == 0) {
1602 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1603 if(wm_event_inside_i(event, &ar->winrct)) {
1604 CTX_wm_region_set(C, ar);
1606 /* does polls for drop regions and checks uibuts */
1607 /* need to be here to make sure region context is true */
1608 if(event->type==MOUSEMOVE) {
1609 wm_region_mouse_co(C, event);
1610 wm_drags_check_ops(C, event);
1613 action |= wm_handlers_do(C, event, &ar->handlers);
1615 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1617 if(action & WM_HANDLER_BREAK)
1623 CTX_wm_region_set(C, NULL);
1625 if((action & WM_HANDLER_BREAK) == 0)
1626 action |= wm_handlers_do(C, event, &sa->handlers);
1628 CTX_wm_area_set(C, NULL);
1630 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1634 if((action & WM_HANDLER_BREAK) == 0) {
1635 /* also some non-modal handlers need active area/region */
1636 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1637 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1639 action |= wm_handlers_do(C, event, &win->handlers);
1642 if(CTX_wm_window(C)==NULL)
1646 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1647 doing it on ghost queue gives errors when mousemoves go over area borders */
1648 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1649 win->eventstate->prevx= event->x;
1650 win->eventstate->prevy= event->y;
1654 /* store last event for this window */
1655 /* mousemove and timer events don't overwrite last type */
1656 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1657 if (wm_action_not_handled(action)) {
1658 if (win->eventstate->prevtype == event->type) {
1659 /* set click time on first click (press -> release) */
1660 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1661 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1664 /* reset click time if event type not the same */
1665 win->eventstate->prevclicktime = 0;
1668 win->eventstate->prevval = event->val;
1669 win->eventstate->prevtype = event->type;
1670 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1671 win->eventstate->prevtype = event->type;
1672 win->eventstate->prevval = event->val;
1673 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1674 } else { /* reset if not */
1675 win->eventstate->prevtype = -1;
1676 win->eventstate->prevval = 0;
1677 win->eventstate->prevclicktime = 0;
1681 /* unlink and free here, blender-quit then frees all */
1682 BLI_remlink(&win->queue, event);
1683 wm_event_free(event);
1687 /* only add mousemove when queue was read entirely */
1688 if(win->addmousemove && win->eventstate) {
1689 wmEvent event= *(win->eventstate);
1690 event.type= MOUSEMOVE;
1691 event.prevx= event.x;
1692 event.prevy= event.y;
1693 wm_event_add(win, &event);
1694 win->addmousemove= 0;
1697 CTX_wm_window_set(C, NULL);
1701 /* ********** filesector handling ************ */
1703 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1705 /* add to all windows! */
1708 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1709 wmEvent event= *win->eventstate;
1711 event.type= EVT_FILESELECT;
1712 event.val= eventval;
1713 event.customdata= ophandle; // only as void pointer type check
1715 wm_event_add(win, &event);
1719 /* operator is supposed to have a filled "path" property */
1720 /* optional property: filetype (XXX enum?) */
1722 /* Idea is to keep a handler alive on window queue, owning the operator.
1723 The filewindow can send event to make it execute, thus ensuring
1724 executing happens outside of lower level queues, with UI refreshed.
1725 Should also allow multiwin solutions */
1727 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1729 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1730 wmWindow *win= CTX_wm_window(C);
1731 int full= 1; // XXX preset?
1733 handler->type= WM_HANDLER_FILESELECT;
1735 handler->op_area= CTX_wm_area(C);
1736 handler->op_region= CTX_wm_region(C);
1737 handler->filescreen= CTX_wm_screen(C);
1739 BLI_addhead(&win->modalhandlers, handler);
1741 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1744 /* lets not expose struct outside wm? */
1745 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1747 handler->flag= flag;
1750 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1752 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1753 wmWindow *win= CTX_wm_window(C);
1755 /* operator was part of macro */
1757 /* give the mother macro to the handler */
1758 handler->op= op->opm;
1759 /* mother macro opm becomes the macro element */
1760 handler->op->opm= op;
1765 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
1766 handler->op_region= CTX_wm_region(C);
1768 BLI_addhead(&win->modalhandlers, handler);
1773 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1775 wmEventHandler *handler;
1778 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1782 /* only allow same keymap once */
1783 for(handler= handlers->first; handler; handler= handler->next)
1784 if(handler->keymap==keymap)
1787 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1788 BLI_addtail(handlers, handler);
1789 handler->keymap= keymap;
1794 /* priorities not implemented yet, for time being just insert in begin of list */
1795 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int priority)
1797 wmEventHandler *handler;
1799 WM_event_remove_keymap_handler(handlers, keymap);
1801 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1802 BLI_addhead(handlers, handler);
1803 handler->keymap= keymap;
1808 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1810 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1813 handler->bblocal= bblocal;
1814 handler->bbwin= bbwin;
1819 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1821 wmEventHandler *handler;
1823 for(handler= handlers->first; handler; handler= handler->next) {
1824 if(handler->keymap==keymap) {
1825 BLI_remlink(handlers, handler);
1826 wm_event_free_handler(handler);
1832 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1834 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1835 handler->ui_handle= func;
1836 handler->ui_remove= remove;
1837 handler->ui_userdata= userdata;
1838 handler->ui_area= (C)? CTX_wm_area(C): NULL;
1839 handler->ui_region= (C)? CTX_wm_region(C): NULL;
1840 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1842 BLI_addhead(handlers, handler);
1847 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1849 wmEventHandler *handler;
1851 for(handler= handlers->first; handler; handler= handler->next) {
1852 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1853 BLI_remlink(handlers, handler);
1854 wm_event_free_handler(handler);
1860 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
1862 wmEventHandler *handler;
1864 /* only allow same dropbox once */
1865 for(handler= handlers->first; handler; handler= handler->next)
1866 if(handler->dropboxes==dropboxes)
1869 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
1871 /* dropbox stored static, no free or copy */
1872 handler->dropboxes= dropboxes;
1873 BLI_addhead(handlers, handler);
1878 /* XXX solution works, still better check the real cause (ton) */
1879 void WM_event_remove_area_handler(ListBase *handlers, void *area)
1881 wmEventHandler *handler, *nexthandler;
1883 for(handler = handlers->first; handler; handler= nexthandler) {
1884 nexthandler = handler->next;
1885 if (handler->type != WM_HANDLER_FILESELECT) {
1886 if (handler->ui_area == area) {
1887 BLI_remlink(handlers, handler);
1888 wm_event_free_handler(handler);
1894 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
1896 BLI_remlink(handlers, handler);
1897 wm_event_free_handler(handler);
1900 void WM_event_add_mousemove(bContext *C)
1902 wmWindow *window= CTX_wm_window(C);
1904 window->addmousemove= 1;
1907 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
1908 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1910 /* user preset or keymap? dunno... */
1911 int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1913 switch(tweak_event) {
1917 if(evt->val==tweak_modal)
1920 /* this case is when modal callcback didnt get started with a tweak */
1927 /* ********************* ghost stuff *************** */
1929 static int convert_key(GHOST_TKey key)
1931 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1932 return (AKEY + ((int) key - GHOST_kKeyA));
1933 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1934 return (ZEROKEY + ((int) key - GHOST_kKey0));
1935 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1936 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1937 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1938 return (F1KEY + ((int) key - GHOST_kKeyF1));
1941 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
1942 case GHOST_kKeyTab: return TABKEY;
1943 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
1944 case GHOST_kKeyClear: return 0;
1945 case GHOST_kKeyEnter: return RETKEY;
1947 case GHOST_kKeyEsc: return ESCKEY;
1948 case GHOST_kKeySpace: return SPACEKEY;
1949 case GHOST_kKeyQuote: return QUOTEKEY;
1950 case GHOST_kKeyComma: return COMMAKEY;
1951 case GHOST_kKeyMinus: return MINUSKEY;
1952 case GHOST_kKeyPeriod: return PERIODKEY;
1953 case GHOST_kKeySlash: return SLASHKEY;
1955 case GHOST_kKeySemicolon: return SEMICOLONKEY;
1956 case GHOST_kKeyEqual: return EQUALKEY;
1958 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
1959 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
1960 case GHOST_kKeyBackslash: return BACKSLASHKEY;
1961 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
1963 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
1964 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
1965 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
1966 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
1967 case GHOST_kKeyCommand: return COMMANDKEY;
1968 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
1969 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
1971 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
1972 case GHOST_kKeyNumLock: return 0;
1973 case GHOST_kKeyScrollLock: return 0;
1975 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
1976 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
1977 case GHOST_kKeyUpArrow: return UPARROWKEY;
1978 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
1980 case GHOST_kKeyPrintScreen: return 0;
1981 case GHOST_kKeyPause: return PAUSEKEY;
1983 case GHOST_kKeyInsert: return INSERTKEY;
1984 case GHOST_kKeyDelete: return DELKEY;
1985 case GHOST_kKeyHome: return HOMEKEY;
1986 case GHOST_kKeyEnd: return ENDKEY;
1987 case GHOST_kKeyUpPage: return PAGEUPKEY;
1988 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
1990 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
1991 case GHOST_kKeyNumpadEnter: return PADENTER;
1992 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
1993 case GHOST_kKeyNumpadMinus: return PADMINUS;
1994 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
1995 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
1997 case GHOST_kKeyGrLess: return GRLESSKEY;
2000 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
2005 /* adds customdata to event */
2006 static void update_tablet_data(wmWindow *win, wmEvent *event)
2008 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2010 /* if there's tablet data from an active tablet device then add it */
2011 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2012 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2014 wmtab->Active = (int)td->Active;
2015 wmtab->Pressure = td->Pressure;
2016 wmtab->Xtilt = td->Xtilt;
2017 wmtab->Ytilt = td->Ytilt;
2019 event->custom= EVT_DATA_TABLET;
2020 event->customdata= wmtab;
2021 event->customdatafree= 1;
2025 /* imperfect but probably usable... draw/enable drags to other windows */
2026 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2028 short mx= evt->x, my= evt->y;
2030 if(wm->windows.first== wm->windows.last)
2033 /* top window bar... */
2034 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
2036 wmEventHandler *handler;
2038 /* let's skip windows having modal handlers now */
2039 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2040 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2041 if(handler->ui_handle || handler->op)
2044 /* to desktop space */
2048 /* check other windows to see if it has mouse inside */
2049 for(owin= wm->windows.first; owin; owin= owin->next) {
2052 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2053 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2054 evt->x= mx-owin->posx;
2055 evt->y= my-owin->posy;
2065 /* windows store own event queues, no bContext here */
2066 /* time is in 1000s of seconds, from ghost */
2067 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int time, void *customdata)
2070 wmEvent event, *evt= win->eventstate;
2072 /* initialize and copy state (only mouse x y and modifiers) */
2077 case GHOST_kEventCursorMove: {
2079 GHOST_TEventCursorData *cd= customdata;
2081 #if defined(__APPLE__) && defined(GHOST_COCOA)
2082 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2088 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2090 evt->y= (win->sizey-1) - cy;
2096 event.type= MOUSEMOVE;
2098 update_tablet_data(win, &event);
2099 wm_event_add(win, &event);
2101 /* also add to other window if event is there, this makes overdraws disappear nicely */
2102 /* it remaps mousecoord to other window in event */
2103 owin= wm_event_cursor_other_windows(wm, win, &event);
2105 wmEvent oevent= *(owin->eventstate);
2109 oevent.type= MOUSEMOVE;
2111 *(owin->eventstate)= oevent;
2112 update_tablet_data(owin, &oevent);
2113 wm_event_add(owin, &oevent);
2119 case GHOST_kEventTrackpad: {
2120 GHOST_TEventTrackpadData * pd = customdata;
2121 switch (pd->subtype) {
2122 case GHOST_kTrackpadEventMagnify:
2123 event.type = MOUSEZOOM;
2125 case GHOST_kTrackpadEventRotate:
2126 event.type = MOUSEROTATE;
2128 case GHOST_kTrackpadEventScroll:
2130 event.type= MOUSEPAN;
2133 #if defined(__APPLE__) && defined(GHOST_COCOA)
2134 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2135 event.x= evt->x = pd->x;
2136 event.y = evt->y = pd->y;
2140 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2141 event.x= evt->x= cx;
2142 event.y= evt->y= (win->sizey-1) - cy;
2145 // Use prevx/prevy so we can calculate the delta later
2146 event.prevx= event.x - pd->deltaX;
2147 event.prevy= event.y - pd->deltaY;
2149 update_tablet_data(win, &event);
2150 wm_event_add(win, &event);
2154 case GHOST_kEventButtonDown:
2155 case GHOST_kEventButtonUp: {
2156 GHOST_TEventButtonData *bd= customdata;
2157 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 */
2159 if (bd->button == GHOST_kButtonMaskLeft)
2160 event.type= LEFTMOUSE;
2161 else if (bd->button == GHOST_kButtonMaskRight)
2162 event.type= RIGHTMOUSE;
2163 else if (bd->button == GHOST_kButtonMaskButton4)
2164 event.type= BUTTON4MOUSE;
2165 else if (bd->button == GHOST_kButtonMaskButton5)
2166 event.type= BUTTON5MOUSE;
2168 event.type= MIDDLEMOUSE;
2170 /* add to other window if event is there (not to both!) */
2171 owin= wm_event_cursor_other_windows(wm, win, &event);
2173 wmEvent oevent= *(owin->eventstate);
2177 oevent.type= event.type;
2178 oevent.val= event.val;
2180 update_tablet_data(owin, &oevent);
2181 wm_event_add(owin, &oevent);
2184 update_tablet_data(win, &event);
2185 wm_event_add(win, &event);
2191 case GHOST_kEventKeyDown:
2192 case GHOST_kEventKeyUp: {
2193 GHOST_TEventKeyData *kd= customdata;
2194 event.type= convert_key(kd->key);
2195 event.ascii= kd->ascii;
2196 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2198 /* exclude arrow keys, esc, etc from text input */
2199 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2203 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2204 event.shift= evt->shift= (event.val==KM_PRESS);
2205 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2206 event.shift= evt->shift = 3; // define?
2208 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2209 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2210 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2211 event.ctrl= evt->ctrl = 3; // define?
2213 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2214 event.alt= evt->alt= (event.val==KM_PRESS);
2215 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2216 event.alt= evt->alt = 3; // define?
2218 else if (event.type==COMMANDKEY) {
2219 event.oskey= evt->oskey= (event.val==KM_PRESS);
2220 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2221 event.oskey= evt->oskey = 3; // define?
2224 if(event.val==KM_PRESS && event.keymodifier==0)
2225 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2226 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2227 event.keymodifier= evt->keymodifier= 0;
2230 /* this case happens on some systems that on holding a key pressed,
2231 generate press events without release, we still want to keep the
2232 modifier in win->eventstate, but for the press event of the same
2233 key we don't want the key modifier */
2234 if(event.keymodifier == event.type)
2235 event.keymodifier= 0;
2237 /* if test_break set, it catches this. XXX Keep global for now? */
2238 if(event.type==ESCKEY)
2241 wm_event_add(win, &event);
2246 case GHOST_kEventWheel: {
2247 GHOST_TEventWheelData* wheelData = customdata;
2249 if (wheelData->z > 0)
2250 event.type= WHEELUPMOUSE;
2252 event.type= WHEELDOWNMOUSE;
2254 event.val= KM_PRESS;
2255 wm_event_add(win, &event);
2259 case GHOST_kEventTimer: {
2261 event.custom= EVT_DATA_TIMER;
2262 event.customdata= customdata;
2263 wm_event_add(win, &event);
2268 case GHOST_kEventUnknown:
2269 case GHOST_kNumEventTypes:
2272 case GHOST_kEventWindowDeactivate: {
2273 event.type= WINDEACTIVATE;
2274 wm_event_add(win, &event);