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 && event->customdatafree)
85 MEM_freeN(event->customdata);
89 void wm_event_free_all(wmWindow *win)
93 while((event= win->queue.first)) {
94 BLI_remlink(&win->queue, event);
99 /* ********************* notifiers, listeners *************** */
101 /* XXX: in future, which notifiers to send to other windows? */
102 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
104 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
106 note->wm= CTX_wm_manager(C);
107 BLI_addtail(¬e->wm->queue, note);
109 note->window= CTX_wm_window(C);
112 note->swinid= CTX_wm_region(C)->swinid;
114 note->category= type & NOTE_CATEGORY;
115 note->data= type & NOTE_DATA;
116 note->subtype= type & NOTE_SUBTYPE;
117 note->action= type & NOTE_ACTION;
119 note->reference= reference;
122 void WM_main_add_notifier(unsigned int type, void *reference)
125 wmWindowManager *wm= bmain->wm.first;
128 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
131 BLI_addtail(¬e->wm->queue, note);
133 note->category= type & NOTE_CATEGORY;
134 note->data= type & NOTE_DATA;
135 note->subtype= type & NOTE_SUBTYPE;
136 note->action= type & NOTE_ACTION;
138 note->reference= reference;
142 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
144 wmNotifier *note= wm->queue.first;
146 if(note) BLI_remlink(&wm->queue, note);
150 /* called in mainloop */
151 void wm_event_do_notifiers(bContext *C)
153 wmWindowManager *wm= CTX_wm_manager(C);
154 wmNotifier *note, *next;
160 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
161 for(win= wm->windows.first; win; win= win->next) {
164 CTX_wm_window_set(C, win);
166 for(note= wm->queue.first; note; note= next) {
169 if(note->category==NC_WM) {
170 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
172 wm_window_title(wm, win);
174 else if(note->data==ND_DATACHANGED)
175 wm_window_title(wm, win);
177 if(note->window==win) {
178 if(note->category==NC_SCREEN) {
179 if(note->data==ND_SCREENBROWSE) {
180 ED_screen_set(C, note->reference); // XXX hrms, think this over!
182 printf("screen set %p\n", note->reference);
184 else if(note->data==ND_SCREENDELETE) {
185 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
187 printf("screen delete %p\n", note->reference);
192 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
193 if(note->category==NC_SCENE) {
194 if(note->data==ND_SCENEBROWSE) {
195 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
197 printf("scene set %p\n", note->reference);
199 else if(note->data==ND_FRAME)
202 if(note->action == NA_REMOVED) {
203 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
205 printf("scene delete %p\n", note->reference);
210 if(ELEM4(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE)) {
211 ED_info_stats_clear(CTX_data_scene(C));
212 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
216 /* depsgraph gets called, might send more notifiers */
217 ED_update_for_newframe(C, 1);
221 /* the notifiers are sent without context, to keep it clean */
222 while( (note=wm_notifier_next(wm)) ) {
225 for(win= wm->windows.first; win; win= win->next) {
227 /* filter out notifiers */
228 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
229 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
234 /* XXX context in notifiers? */
235 CTX_wm_window_set(C, win);
237 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
238 ED_screen_do_listen(win, note);
240 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
241 ED_region_do_listen(ar, note);
244 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
245 ED_area_do_listen(sa, note);
246 for(ar=sa->regionbase.first; ar; ar= ar->next) {
247 ED_region_do_listen(ar, note);
256 /* cached: editor refresh callbacks now, they get context */
257 for(win= wm->windows.first; win; win= win->next) {
260 CTX_wm_window_set(C, win);
261 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
263 CTX_wm_area_set(C, sa);
264 ED_area_do_refresh(C, sa);
268 /* XXX make lock in future, or separated derivedmesh users in scene */
270 /* depsgraph & animation: update tagged datablocks */
271 scene_update_tagged(win->screen->scene);
274 CTX_wm_window_set(C, NULL);
277 /* ********************* operators ******************* */
279 int WM_operator_poll(bContext *C, wmOperatorType *ot)
281 wmOperatorTypeMacro *otmacro;
283 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
284 wmOperatorType *ot= WM_operatortype_find(otmacro->idname, 0);
286 if(0==WM_operator_poll(C, ot))
290 /* python needs operator type, so we added exception for it */
292 return ot->pyop_poll(C, ot);
299 /* if repeat is true, it doesn't register again, nor does it free */
300 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
302 int retval= OPERATOR_CANCELLED;
304 if(op==NULL || op->type==NULL)
307 if(0==WM_operator_poll(C, op->type))
311 retval= op->type->exec(C, op);
313 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
314 if(op->reports->list.first)
315 uiPupMenuReports(C, op->reports);
317 if(retval & OPERATOR_FINISHED) {
318 op->customdata= NULL;
320 if(op->type->flag & OPTYPE_UNDO)
321 ED_undo_push_op(C, op);
325 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
326 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
330 if((op->type->flag & OPTYPE_REGISTER))
331 wm_operator_register(C, op);
333 WM_operator_free(op);
337 WM_operator_free(op);
343 /* for running operators with frozen context (modal handlers, menus) */
344 int WM_operator_call(bContext *C, wmOperator *op)
346 return wm_operator_exec(C, op, 0);
349 /* do this operator again, put here so it can share above code */
350 int WM_operator_repeat(bContext *C, wmOperator *op)
352 return wm_operator_exec(C, op, 1);
355 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
357 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
359 /* XXX adding new operator could be function, only happens here now */
361 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
363 /* initialize properties, either copy or create */
364 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
365 if(properties && properties->data) {
366 op->properties= IDP_CopyProperty(properties->data);
369 IDPropertyTemplate val = {0};
370 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
372 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
374 /* initialize error reports */
376 op->reports= reports; /* must be initialized already */
379 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
380 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
383 /* recursive filling of operator macro list */
384 if(ot->macro.first) {
385 static wmOperator *motherop= NULL;
386 wmOperatorTypeMacro *otmacro;
389 /* ensure all ops are in execution order in 1 list */
396 /* if properties exist, it will contain everything needed */
398 otmacro= ot->macro.first;
400 RNA_STRUCT_BEGIN(properties, prop) {
405 /* skip invalid properties */
406 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
408 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
409 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
410 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
412 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
414 BLI_addtail(&motherop->macro, opm);
415 opm->opm= motherop; /* pointer to mom, for modal() */
417 otmacro= otmacro->next;
422 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
423 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
424 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
426 BLI_addtail(&motherop->macro, opm);
427 opm->opm= motherop; /* pointer to mom, for modal() */
438 static void wm_operator_print(wmOperator *op)
440 char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
445 static void wm_region_mouse_co(bContext *C, wmEvent *event)
447 ARegion *ar= CTX_wm_region(C);
449 /* compatibility convention */
450 event->mval[0]= event->x - ar->winrct.xmin;
451 event->mval[1]= event->y - ar->winrct.ymin;
455 static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
457 wmWindowManager *wm= CTX_wm_manager(C);
458 int retval= OPERATOR_PASS_THROUGH;
460 if(WM_operator_poll(C, ot)) {
461 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
463 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
464 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
466 if(op->type->invoke && event) {
467 wm_region_mouse_co(C, event);
468 retval= op->type->invoke(C, op, event);
470 else if(op->type->exec)
471 retval= op->type->exec(C, op);
473 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
475 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
476 * currently python only uses this */
477 if((retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED)) && reports==NULL)
478 if(op->reports->list.first) /* only show the report if the report list was not given in the function */
479 uiPupMenuReports(C, op->reports);
481 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 */
483 wm_operator_print(op);
486 if(retval & OPERATOR_FINISHED) {
487 op->customdata= NULL;
489 if(ot->flag & OPTYPE_UNDO)
490 ED_undo_push_op(C, op);
493 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
494 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
498 if((ot->flag & OPTYPE_REGISTER))
499 wm_operator_register(C, op);
501 WM_operator_free(op);
503 else if(retval & OPERATOR_RUNNING_MODAL) {
504 /* grab cursor during blocking modal ops (X11)
505 * Also check for macro
507 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
508 int bounds[4] = {-1,-1,-1,-1};
512 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
514 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
518 ARegion *ar= CTX_wm_region(C);
520 bounds[0]= ar->winrct.xmin;
521 bounds[1]= ar->winrct.ymax;
522 bounds[2]= ar->winrct.xmax;
523 bounds[3]= ar->winrct.ymin;
527 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
531 WM_operator_free(op);
537 /* WM_operator_name_call is the main accessor function
538 * this is for python to access since its done the operator lookup
540 * invokes operator in context */
541 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
543 wmWindow *window= CTX_wm_window(C);
551 case WM_OP_INVOKE_DEFAULT:
552 case WM_OP_INVOKE_REGION_WIN:
553 case WM_OP_INVOKE_AREA:
554 case WM_OP_INVOKE_SCREEN:
555 /* window is needed for invoke, cancel operator */
559 event= window->eventstate;
567 case WM_OP_EXEC_REGION_WIN:
568 case WM_OP_INVOKE_REGION_WIN:
570 /* forces operator to go to the region window, for header menus */
571 ARegion *ar= CTX_wm_region(C);
572 ScrArea *area= CTX_wm_area(C);
575 ARegion *ar1= area->regionbase.first;
576 for(; ar1; ar1= ar1->next)
577 if(ar1->regiontype==RGN_TYPE_WINDOW)
580 CTX_wm_region_set(C, ar1);
583 retval= wm_operator_invoke(C, ot, event, properties, reports);
585 /* set region back */
586 CTX_wm_region_set(C, ar);
590 case WM_OP_EXEC_AREA:
591 case WM_OP_INVOKE_AREA:
593 /* remove region from context */
594 ARegion *ar= CTX_wm_region(C);
596 CTX_wm_region_set(C, NULL);
597 retval= wm_operator_invoke(C, ot, event, properties, reports);
598 CTX_wm_region_set(C, ar);
602 case WM_OP_EXEC_SCREEN:
603 case WM_OP_INVOKE_SCREEN:
605 /* remove region + area from context */
606 ARegion *ar= CTX_wm_region(C);
607 ScrArea *area= CTX_wm_area(C);
609 CTX_wm_region_set(C, NULL);
610 CTX_wm_area_set(C, NULL);
611 retval= wm_operator_invoke(C, ot, event, properties, reports);
612 CTX_wm_region_set(C, ar);
613 CTX_wm_area_set(C, area);
617 case WM_OP_EXEC_DEFAULT:
618 case WM_OP_INVOKE_DEFAULT:
619 return wm_operator_invoke(C, ot, event, properties, reports);
627 /* invokes operator in context */
628 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
630 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
632 return wm_operator_call_internal(C, ot, context, properties, NULL);
637 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
638 - wmOperatorType is used instead of operator name since python alredy has the operator type
639 - poll() must be called by python before this runs.
640 - reports can be passed to this function (so python can report them as exceptions)
642 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
644 int retval= OPERATOR_CANCELLED;
648 wmWindowManager *wm= CTX_wm_manager(C);
649 op= wm_operator_create(wm, ot, properties, reports);
652 retval= op->type->exec(C, op);
654 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
657 retval= wm_operator_call_internal(C, ot, context, properties, reports);
659 /* keep the reports around if needed later */
660 if (retval & OPERATOR_RUNNING_MODAL || ot->flag & OPTYPE_REGISTER)
662 reports->flag |= RPT_FREE;
669 /* ********************* handlers *************** */
671 /* future extra customadata free? */
672 void wm_event_free_handler(wmEventHandler *handler)
677 /* only set context when area/region is part of screen */
678 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
680 bScreen *screen= CTX_wm_screen(C);
682 if(screen && handler->op) {
683 if(handler->op_area==NULL)
684 CTX_wm_area_set(C, NULL);
688 for(sa= screen->areabase.first; sa; sa= sa->next)
689 if(sa==handler->op_area)
692 /* when changing screen layouts with running modal handlers (like render display), this
693 is not an error to print */
694 if(handler->op==NULL)
695 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
699 CTX_wm_area_set(C, sa);
700 for(ar= sa->regionbase.first; ar; ar= ar->next)
701 if(ar==handler->op_region)
703 /* XXX no warning print here, after full-area and back regions are remade */
705 CTX_wm_region_set(C, ar);
711 /* called on exit or remove area, only here call cancel callback */
712 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
714 wmEventHandler *handler;
716 /* C is zero on freeing database, modal handlers then already were freed */
717 while((handler=handlers->first)) {
718 BLI_remlink(handlers, handler);
721 if(handler->op->type->cancel) {
722 ScrArea *area= CTX_wm_area(C);
723 ARegion *region= CTX_wm_region(C);
725 wm_handler_op_context(C, handler);
727 handler->op->type->cancel(C, handler->op);
729 CTX_wm_area_set(C, area);
730 CTX_wm_region_set(C, region);
733 WM_cursor_ungrab(CTX_wm_window(C));
734 WM_operator_free(handler->op);
736 else if(handler->ui_remove) {
737 ScrArea *area= CTX_wm_area(C);
738 ARegion *region= CTX_wm_region(C);
739 ARegion *menu= CTX_wm_menu(C);
741 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
742 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
743 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
745 handler->ui_remove(C, handler->ui_userdata);
747 CTX_wm_area_set(C, area);
748 CTX_wm_region_set(C, region);
749 CTX_wm_menu_set(C, menu);
752 wm_event_free_handler(handler);
756 /* do userdef mappings */
757 int WM_userdef_event_map(int kmitype)
761 if(U.flag & USER_LMOUSESELECT)
767 if(U.flag & USER_LMOUSESELECT)
773 if(U.uiflag & USER_WHEELZOOMDIR)
776 return WHEELDOWNMOUSE;
779 if(U.uiflag & USER_WHEELZOOMDIR)
780 return WHEELDOWNMOUSE;
785 if(U.flag & USER_LMOUSESELECT)
791 if(U.flag & USER_LMOUSESELECT)
800 static void wm_eventemulation(wmEvent *event)
802 /* middlemouse emulation */
803 if(U.flag & USER_TWOBUTTONMOUSE) {
804 if(event->type == LEFTMOUSE && event->alt) {
805 event->type = MIDDLEMOUSE;
811 /* rightmouse emulation */
812 if(U.flag & USER_TWOBUTTONMOUSE) {
813 if(event->type == LEFTMOUSE && event->oskey) {
814 event->type = RIGHTMOUSE;
820 /* numpad emulation */
821 if(U.flag & USER_NONUMPAD) {
822 switch(event->type) {
823 case ZEROKEY: event->type = PAD0; break;
824 case ONEKEY: event->type = PAD1; break;
825 case TWOKEY: event->type = PAD2; break;
826 case THREEKEY: event->type = PAD3; break;
827 case FOURKEY: event->type = PAD4; break;
828 case FIVEKEY: event->type = PAD5; break;
829 case SIXKEY: event->type = PAD6; break;
830 case SEVENKEY: event->type = PAD7; break;
831 case EIGHTKEY: event->type = PAD8; break;
832 case NINEKEY: event->type = PAD9; break;
833 case MINUSKEY: event->type = PADMINUS; break;
834 case EQUALKEY: event->type = PADPLUSKEY; break;
835 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
840 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
842 int kmitype= WM_userdef_event_map(kmi->type);
844 if(kmi->flag & KMI_INACTIVE) return 0;
846 /* the matching rules */
847 if(kmitype==KM_TEXTINPUT)
848 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
850 if(winevent->type!=kmitype) return 0;
853 if(winevent->val!=kmi->val) return 0;
855 /* modifiers also check bits, so it allows modifier order */
856 if(kmi->shift!=KM_ANY)
857 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
858 if(kmi->ctrl!=KM_ANY)
859 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
861 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
862 if(kmi->oskey!=KM_ANY)
863 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
866 if(winevent->keymodifier!=kmi->keymodifier) return 0;
868 /* key modifiers always check when event has it */
869 /* otherwise regular keypresses with keymodifier still work */
870 if(winevent->keymodifier)
871 if(ISTEXTINPUT(winevent->type))
872 if(winevent->keymodifier!=kmi->keymodifier) return 0;
877 static int wm_event_always_pass(wmEvent *event)
879 /* some events we always pass on, to ensure proper communication */
880 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
883 /* operator exists */
884 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
886 /* support for modal keymap in macros */
890 if(op->type->modalkeymap) {
891 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
894 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
895 if(wm_eventmatch(event, kmi)) {
897 event->type= EVT_MODAL_MAP;
898 event->val= kmi->propvalue;
904 /* Warning: this function removes a modal handler, when finished */
905 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
907 int retval= OPERATOR_PASS_THROUGH;
909 /* derived, modal or blocking operator */
911 wmOperator *op= handler->op;
912 wmOperatorType *ot= op->type;
915 /* we set context to where modal handler came from */
916 ScrArea *area= CTX_wm_area(C);
917 ARegion *region= CTX_wm_region(C);
919 wm_handler_op_context(C, handler);
920 wm_region_mouse_co(C, event);
921 wm_event_modalkeymap(C, op, event);
923 retval= ot->modal(C, op, event);
925 /* putting back screen context, reval can pass trough after modal failures! */
926 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
927 CTX_wm_area_set(C, area);
928 CTX_wm_region_set(C, region);
931 /* this special cases is for areas and regions that get removed */
932 CTX_wm_area_set(C, NULL);
933 CTX_wm_region_set(C, NULL);
936 if(retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
937 if(op->reports->list.first)
938 uiPupMenuReports(C, op->reports);
940 if (retval & OPERATOR_FINISHED) {
942 wm_operator_print(op); /* todo - this print may double up, might want to check more flags then the FINISHED */
945 if(retval & OPERATOR_FINISHED) {
946 op->customdata= NULL;
948 if(ot->flag & OPTYPE_UNDO)
949 ED_undo_push_op(C, op);
952 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
953 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
957 if((ot->flag & OPTYPE_REGISTER))
958 wm_operator_register(C, op);
960 WM_operator_free(op);
963 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
964 WM_operator_free(op);
968 /* remove modal handler, operator itself should have been cancelled and freed */
969 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
970 WM_cursor_ungrab(CTX_wm_window(C));
972 BLI_remlink(handlers, handler);
973 wm_event_free_handler(handler);
975 /* prevent silly errors from operator users */
976 //retval &= ~OPERATOR_PASS_THROUGH;
981 printf("wm_handler_operator_call error\n");
984 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
987 retval= wm_operator_invoke(C, ot, event, properties, NULL);
990 /* Finished and pass through flag as handled */
991 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
992 return WM_HANDLER_HANDLED;
994 /* Modal unhandled, break */
995 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
996 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
998 if(retval & OPERATOR_PASS_THROUGH)
999 return WM_HANDLER_CONTINUE;
1001 return WM_HANDLER_BREAK;
1004 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event)
1006 ScrArea *area= CTX_wm_area(C);
1007 ARegion *region= CTX_wm_region(C);
1008 ARegion *menu= CTX_wm_menu(C);
1009 int retval, always_pass;
1011 /* we set context to where ui handler came from */
1012 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
1013 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
1014 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
1016 /* in advance to avoid access to freed event on window close */
1017 always_pass= wm_event_always_pass(event);
1019 retval= handler->ui_handle(C, event, handler->ui_userdata);
1021 /* putting back screen context */
1022 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
1023 CTX_wm_area_set(C, area);
1024 CTX_wm_region_set(C, region);
1025 CTX_wm_menu_set(C, menu);
1028 /* this special cases is for areas and regions that get removed */
1029 CTX_wm_area_set(C, NULL);
1030 CTX_wm_region_set(C, NULL);
1031 CTX_wm_menu_set(C, NULL);
1034 if(retval == WM_UI_HANDLER_BREAK)
1035 return WM_HANDLER_BREAK;
1037 return WM_HANDLER_CONTINUE;
1040 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1041 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1044 int action= WM_HANDLER_CONTINUE;
1046 if(event->type != EVT_FILESELECT)
1048 if(handler->op != (wmOperator *)event->customdata)
1051 switch(event->val) {
1052 case EVT_FILESELECT_OPEN:
1053 case EVT_FILESELECT_FULL_OPEN:
1057 /* sa can be null when window A is active, but mouse is over window B */
1058 /* in this case, open file select in original window A */
1059 if (handler->op_area == NULL) {
1060 bScreen *screen = CTX_wm_screen(C);
1061 sa = (ScrArea *)screen->areabase.first;
1063 sa = handler->op_area;
1065 if(event->val==EVT_FILESELECT_OPEN)
1066 ED_area_newspace(C, sa, SPACE_FILE);
1068 ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1070 /* settings for filebrowser, sfile is not operator owner but sends events */
1071 sa = CTX_wm_area(C);
1072 sfile= (SpaceFile*)sa->spacedata.first;
1073 sfile->op= handler->op;
1075 ED_fileselect_set_params(sfile);
1077 action= WM_HANDLER_BREAK;
1081 case EVT_FILESELECT_EXEC:
1082 case EVT_FILESELECT_CANCEL:
1084 /* XXX validate area and region? */
1085 bScreen *screen= CTX_wm_screen(C);
1086 char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
1088 if(screen != handler->filescreen)
1089 ED_screen_full_prevspace(C, CTX_wm_area(C));
1091 ED_area_prevspace(C, CTX_wm_area(C));
1093 /* remlink now, for load file case */
1094 BLI_remlink(handlers, handler);
1096 wm_handler_op_context(C, handler);
1098 /* needed for uiPupMenuReports */
1100 if(event->val==EVT_FILESELECT_EXEC) {
1101 /* a bit weak, might become arg for WM_event_fileselect? */
1102 /* XXX also extension code in image-save doesnt work for this yet */
1103 if(strncmp(handler->op->type->name, "Save", 4)==0) {
1104 /* this gives ownership to pupmenu */
1105 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1108 int retval= handler->op->type->exec(C, handler->op);
1110 if (retval & OPERATOR_FINISHED)
1112 wm_operator_print(handler->op);
1114 if(handler->op->reports->list.first) {
1116 /* FIXME, temp setting window, this is really bad!
1117 * only have because lib linking errors need to be seen by users :(
1118 * it can be removed without breaking anything but then no linking errors - campbell */
1119 wmWindow *win_prev= CTX_wm_window(C);
1121 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1123 handler->op->reports->printlevel = RPT_WARNING;
1124 uiPupMenuReports(C, handler->op->reports);
1126 CTX_wm_window_set(C, win_prev);
1129 WM_operator_free(handler->op);
1133 if(handler->op->type->cancel)
1134 handler->op->type->cancel(C, handler->op);
1136 WM_operator_free(handler->op);
1139 CTX_wm_area_set(C, NULL);
1141 wm_event_free_handler(handler);
1145 action= WM_HANDLER_BREAK;
1153 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1155 if(handler->bbwin) {
1156 if(handler->bblocal) {
1157 rcti rect= *handler->bblocal;
1158 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1160 if(BLI_in_rcti(&rect, event->x, event->y))
1162 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1168 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1170 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1179 static int wm_action_not_handled(int action)
1181 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1184 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1186 wmWindowManager *wm= CTX_wm_manager(C);
1187 wmEventHandler *handler, *nexthandler;
1188 int action= WM_HANDLER_CONTINUE;
1191 if(handlers==NULL) return action;
1193 /* modal handlers can get removed in this loop, we keep the loop this way */
1194 for(handler= handlers->first; handler; handler= nexthandler) {
1195 nexthandler= handler->next;
1197 /* optional boundbox */
1198 if(handler_boundbox_test(handler, event)) {
1199 /* in advance to avoid access to freed event on window close */
1200 always_pass= wm_event_always_pass(event);
1202 /* modal+blocking handler */
1203 if(handler->flag & WM_HANDLER_BLOCKING)
1204 action |= WM_HANDLER_BREAK;
1206 if(handler->keymap) {
1207 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1210 if(!keymap->poll || keymap->poll(C)) {
1211 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1212 if(wm_eventmatch(event, kmi)) {
1214 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1216 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1217 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1223 else if(handler->ui_handle) {
1224 action |= wm_handler_ui_call(C, handler, event);
1226 else if(handler->type==WM_HANDLER_FILESELECT) {
1227 /* screen context changes here */
1228 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1231 /* modal, swallows all */
1232 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1235 if(action & WM_HANDLER_BREAK) {
1237 action &= ~WM_HANDLER_BREAK;
1244 if(CTX_wm_window(C)==NULL)
1248 /* test for CLICK event */
1249 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1250 wmWindow *win = CTX_wm_window(C);
1252 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1253 /* test for double click first */
1254 if ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time) {
1255 event->val = KM_DBL_CLICK;
1256 action |= wm_handlers_do(C, event, handlers);
1259 if (wm_action_not_handled(action)) {
1260 event->val = KM_CLICK;
1261 action |= wm_handlers_do(C, event, handlers);
1265 /* revert value if not handled */
1266 if (wm_action_not_handled(action)) {
1267 event->val = KM_RELEASE;
1275 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1277 if(wm_event_always_pass(event))
1279 if(BLI_in_rcti(rect, event->x, event->y))
1281 if(event->type==MOUSEMOVE) {
1282 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1290 static ScrArea *area_event_inside(bContext *C, int x, int y)
1292 bScreen *screen= CTX_wm_screen(C);
1296 for(sa= screen->areabase.first; sa; sa= sa->next)
1297 if(BLI_in_rcti(&sa->totrct, x, y))
1302 static ARegion *region_event_inside(bContext *C, int x, int y)
1304 bScreen *screen= CTX_wm_screen(C);
1305 ScrArea *area= CTX_wm_area(C);
1309 for(ar= area->regionbase.first; ar; ar= ar->next)
1310 if(BLI_in_rcti(&ar->winrct, x, y))
1315 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1318 for(; pc; pc= pc->next) {
1320 wmWindow *win= CTX_wm_window(C);
1321 win->screen->do_draw_paintcursor= 1;
1323 if(win->drawmethod != USER_DRAW_TRIPLE)
1324 ED_region_tag_redraw(ar);
1330 /* called on mousemove, check updates for paintcursors */
1331 /* context was set on active area and region */
1332 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1334 wmWindowManager *wm= CTX_wm_manager(C);
1336 if(wm->paintcursors.first) {
1337 ARegion *ar= CTX_wm_region(C);
1339 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1341 /* if previous position was not in current region, we have to set a temp new context */
1342 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1343 ScrArea *sa= CTX_wm_area(C);
1345 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1346 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1348 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1350 CTX_wm_area_set(C, sa);
1351 CTX_wm_region_set(C, ar);
1356 /* called in main loop */
1357 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1358 void wm_event_do_handlers(bContext *C)
1362 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1365 if( win->screen==NULL )
1366 wm_event_free_all(win);
1368 while( (event= win->queue.first) ) {
1369 int action = WM_HANDLER_CONTINUE;
1371 wm_eventemulation(event);
1373 CTX_wm_window_set(C, win);
1375 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1376 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1377 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1379 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1380 wm_window_make_drawable(C, win);
1382 /* first we do priority handlers, modal + some limited keymaps */
1383 action |= wm_handlers_do(C, event, &win->modalhandlers);
1386 if(CTX_wm_window(C)==NULL)
1389 /* builtin tweak, if action is break it removes tweak */
1390 wm_tweakevent_test(C, event, action);
1392 if((action & WM_HANDLER_BREAK) == 0) {
1397 /* XXX to solve, here screen handlers? */
1398 if(event->type==MOUSEMOVE) {
1399 /* state variables in screen, cursors */
1400 ED_screen_set_subwinactive(win, event);
1401 /* for regions having custom cursors */
1402 wm_paintcursor_test(C, event);
1405 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1406 if(wm_event_inside_i(event, &sa->totrct)) {
1407 CTX_wm_area_set(C, sa);
1409 if((action & WM_HANDLER_BREAK) == 0) {
1410 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1411 if(wm_event_inside_i(event, &ar->winrct)) {
1412 CTX_wm_region_set(C, ar);
1413 action |= wm_handlers_do(C, event, &ar->handlers);
1415 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1417 if(action & WM_HANDLER_BREAK)
1423 CTX_wm_region_set(C, NULL);
1425 if((action & WM_HANDLER_BREAK) == 0)
1426 action |= wm_handlers_do(C, event, &sa->handlers);
1428 CTX_wm_area_set(C, NULL);
1430 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1434 if((action & WM_HANDLER_BREAK) == 0) {
1435 /* also some non-modal handlers need active area/region */
1436 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1437 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1439 action |= wm_handlers_do(C, event, &win->handlers);
1442 if(CTX_wm_window(C)==NULL)
1446 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1447 doing it on ghost queue gives errors when mousemoves go over area borders */
1448 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1449 win->eventstate->prevx= event->x;
1450 win->eventstate->prevy= event->y;
1454 /* store last event for this window */
1455 /* mousemove and timer events don't overwrite last type */
1456 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1457 if (wm_action_not_handled(action)) {
1458 if (win->eventstate->prevtype == event->type) {
1459 /* set click time on first click (press -> release) */
1460 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1461 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1464 /* reset click time if event type not the same */
1465 win->eventstate->prevclicktime = 0;
1468 win->eventstate->prevval = event->val;
1469 win->eventstate->prevtype = event->type;
1470 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1471 win->eventstate->prevtype = event->type;
1472 win->eventstate->prevval = event->val;
1473 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1474 } else { /* reset if not */
1475 win->eventstate->prevtype = -1;
1476 win->eventstate->prevval = 0;
1477 win->eventstate->prevclicktime = 0;
1481 /* unlink and free here, blender-quit then frees all */
1482 BLI_remlink(&win->queue, event);
1483 wm_event_free(event);
1487 /* only add mousemove when queue was read entirely */
1488 if(win->addmousemove && win->eventstate) {
1489 wmEvent event= *(win->eventstate);
1490 event.type= MOUSEMOVE;
1491 event.prevx= event.x;
1492 event.prevy= event.y;
1493 wm_event_add(win, &event);
1494 win->addmousemove= 0;
1497 CTX_wm_window_set(C, NULL);
1501 /* ********** filesector handling ************ */
1503 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1505 /* add to all windows! */
1508 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1509 wmEvent event= *win->eventstate;
1511 event.type= EVT_FILESELECT;
1512 event.val= eventval;
1513 event.customdata= ophandle; // only as void pointer type check
1515 wm_event_add(win, &event);
1519 /* operator is supposed to have a filled "path" property */
1520 /* optional property: filetype (XXX enum?) */
1522 /* Idea is to keep a handler alive on window queue, owning the operator.
1523 The filewindow can send event to make it execute, thus ensuring
1524 executing happens outside of lower level queues, with UI refreshed.
1525 Should also allow multiwin solutions */
1527 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1529 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1530 wmWindow *win= CTX_wm_window(C);
1531 int full= 1; // XXX preset?
1533 handler->type= WM_HANDLER_FILESELECT;
1535 handler->op_area= CTX_wm_area(C);
1536 handler->op_region= CTX_wm_region(C);
1537 handler->filescreen= CTX_wm_screen(C);
1539 BLI_addhead(&win->modalhandlers, handler);
1541 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1544 /* lets not expose struct outside wm? */
1545 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1547 handler->flag= flag;
1550 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1552 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1553 wmWindow *win= CTX_wm_window(C);
1555 /* operator was part of macro */
1557 /* give the mother macro to the handler */
1558 handler->op= op->opm;
1559 /* mother macro opm becomes the macro element */
1560 handler->op->opm= op;
1565 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
1566 handler->op_region= CTX_wm_region(C);
1568 BLI_addhead(&win->modalhandlers, handler);
1573 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1575 wmEventHandler *handler;
1578 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1582 /* only allow same keymap once */
1583 for(handler= handlers->first; handler; handler= handler->next)
1584 if(handler->keymap==keymap)
1587 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1588 BLI_addtail(handlers, handler);
1589 handler->keymap= keymap;
1594 /* priorities not implemented yet, for time being just insert in begin of list */
1595 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int priority)
1597 wmEventHandler *handler;
1599 WM_event_remove_keymap_handler(handlers, keymap);
1601 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1602 BLI_addhead(handlers, handler);
1603 handler->keymap= keymap;
1608 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1610 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1613 handler->bblocal= bblocal;
1614 handler->bbwin= bbwin;
1619 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1621 wmEventHandler *handler;
1623 for(handler= handlers->first; handler; handler= handler->next) {
1624 if(handler->keymap==keymap) {
1625 BLI_remlink(handlers, handler);
1626 wm_event_free_handler(handler);
1632 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1634 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1635 handler->ui_handle= func;
1636 handler->ui_remove= remove;
1637 handler->ui_userdata= userdata;
1638 handler->ui_area= (C)? CTX_wm_area(C): NULL;
1639 handler->ui_region= (C)? CTX_wm_region(C): NULL;
1640 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1642 BLI_addhead(handlers, handler);
1647 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1649 wmEventHandler *handler;
1651 for(handler= handlers->first; handler; handler= handler->next) {
1652 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1653 BLI_remlink(handlers, handler);
1654 wm_event_free_handler(handler);
1660 void WM_event_remove_area_handler(ListBase *handlers, void *area)
1662 wmEventHandler *handler, *nexthandler;
1664 for(handler = handlers->first; handler; handler= nexthandler) {
1665 nexthandler = handler->next;
1666 if (handler->type != WM_HANDLER_FILESELECT) {
1667 if (handler->ui_area == area) {
1668 BLI_remlink(handlers, handler);
1669 wm_event_free_handler(handler);
1675 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
1677 BLI_remlink(handlers, handler);
1678 wm_event_free_handler(handler);
1681 void WM_event_add_mousemove(bContext *C)
1683 wmWindow *window= CTX_wm_window(C);
1685 window->addmousemove= 1;
1688 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
1689 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
1691 /* user preset or keymap? dunno... */
1692 int tweak_modal= (U.flag & USER_DRAGIMMEDIATE)==0;
1694 switch(tweak_event) {
1698 if(evt->val==tweak_modal)
1701 /* this case is when modal callcback didnt get started with a tweak */
1709 /* ********************* ghost stuff *************** */
1711 static int convert_key(GHOST_TKey key)
1713 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
1714 return (AKEY + ((int) key - GHOST_kKeyA));
1715 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
1716 return (ZEROKEY + ((int) key - GHOST_kKey0));
1717 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
1718 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
1719 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF12) {
1720 return (F1KEY + ((int) key - GHOST_kKeyF1));
1723 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
1724 case GHOST_kKeyTab: return TABKEY;
1725 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
1726 case GHOST_kKeyClear: return 0;
1727 case GHOST_kKeyEnter: return RETKEY;
1729 case GHOST_kKeyEsc: return ESCKEY;
1730 case GHOST_kKeySpace: return SPACEKEY;
1731 case GHOST_kKeyQuote: return QUOTEKEY;
1732 case GHOST_kKeyComma: return COMMAKEY;
1733 case GHOST_kKeyMinus: return MINUSKEY;
1734 case GHOST_kKeyPeriod: return PERIODKEY;
1735 case GHOST_kKeySlash: return SLASHKEY;
1737 case GHOST_kKeySemicolon: return SEMICOLONKEY;
1738 case GHOST_kKeyEqual: return EQUALKEY;
1740 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
1741 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
1742 case GHOST_kKeyBackslash: return BACKSLASHKEY;
1743 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
1745 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
1746 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
1747 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
1748 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
1749 case GHOST_kKeyCommand: return COMMANDKEY;
1750 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
1751 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
1753 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
1754 case GHOST_kKeyNumLock: return 0;
1755 case GHOST_kKeyScrollLock: return 0;
1757 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
1758 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
1759 case GHOST_kKeyUpArrow: return UPARROWKEY;
1760 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
1762 case GHOST_kKeyPrintScreen: return 0;
1763 case GHOST_kKeyPause: return PAUSEKEY;
1765 case GHOST_kKeyInsert: return INSERTKEY;
1766 case GHOST_kKeyDelete: return DELKEY;
1767 case GHOST_kKeyHome: return HOMEKEY;
1768 case GHOST_kKeyEnd: return ENDKEY;
1769 case GHOST_kKeyUpPage: return PAGEUPKEY;
1770 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
1772 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
1773 case GHOST_kKeyNumpadEnter: return PADENTER;
1774 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
1775 case GHOST_kKeyNumpadMinus: return PADMINUS;
1776 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
1777 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
1779 case GHOST_kKeyGrLess: return GRLESSKEY;
1782 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
1787 /* adds customdata to event */
1788 static void update_tablet_data(wmWindow *win, wmEvent *event)
1790 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
1792 /* if there's tablet data from an active tablet device then add it */
1793 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
1794 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
1796 wmtab->Active = (int)td->Active;
1797 wmtab->Pressure = td->Pressure;
1798 wmtab->Xtilt = td->Xtilt;
1799 wmtab->Ytilt = td->Ytilt;
1801 event->custom= EVT_DATA_TABLET;
1802 event->customdata= wmtab;
1803 event->customdatafree= 1;
1808 /* windows store own event queues, no bContext here */
1809 void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
1811 wmEvent event, *evt= win->eventstate;
1813 /* initialize and copy state (only mouse x y and modifiers) */
1818 case GHOST_kEventCursorMove: {
1820 GHOST_TEventCursorData *cd= customdata;
1821 #if defined(__APPLE__) && defined(GHOST_COCOA)
1822 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
1823 event.type= MOUSEMOVE;
1824 event.x= evt->x = cd->x;
1825 event.y = evt->y = cd->y;
1828 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
1829 event.type= MOUSEMOVE;
1830 event.x= evt->x= cx;
1831 event.y= evt->y= (win->sizey-1) - cy;
1833 update_tablet_data(win, &event);
1834 wm_event_add(win, &event);
1838 case GHOST_kEventTrackpad: {
1840 GHOST_TEventTrackpadData * pd = customdata;
1841 switch (pd->subtype) {
1842 case GHOST_kTrackpadEventMagnify:
1843 event.type = MOUSEZOOM;
1845 case GHOST_kTrackpadEventRotate:
1846 event.type = MOUSEROTATE;
1848 case GHOST_kTrackpadEventScroll:
1850 event.type= MOUSEPAN;
1853 #if defined(__APPLE__) && defined(GHOST_COCOA)
1854 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
1855 event.x= evt->x = pd->x;
1856 event.y = evt->y = pd->y;
1860 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
1861 event.x= evt->x= cx;
1862 event.y= evt->y= (win->sizey-1) - cy;
1865 // Use prevx/prevy so we can calculate the delta later
1866 event.prevx= event.x - pd->deltaX;
1867 event.prevy= event.y - pd->deltaY;
1869 update_tablet_data(win, &event);
1870 wm_event_add(win, &event);
1875 case GHOST_kEventButtonDown:
1876 case GHOST_kEventButtonUp: {
1877 GHOST_TEventButtonData *bd= customdata;
1878 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 */
1880 if (bd->button == GHOST_kButtonMaskLeft)
1881 event.type= LEFTMOUSE;
1882 else if (bd->button == GHOST_kButtonMaskRight)
1883 event.type= RIGHTMOUSE;
1884 else if (bd->button == GHOST_kButtonMaskButton4)
1885 event.type= BUTTON4MOUSE;
1886 else if (bd->button == GHOST_kButtonMaskButton5)
1887 event.type= BUTTON5MOUSE;
1889 event.type= MIDDLEMOUSE;
1891 update_tablet_data(win, &event);
1892 wm_event_add(win, &event);
1897 case GHOST_kEventKeyDown:
1898 case GHOST_kEventKeyUp: {
1899 GHOST_TEventKeyData *kd= customdata;
1900 event.type= convert_key(kd->key);
1901 event.ascii= kd->ascii;
1902 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
1904 /* exclude arrow keys, esc, etc from text input */
1905 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
1909 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
1910 event.shift= evt->shift= (event.val==KM_PRESS);
1911 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
1912 event.shift= evt->shift = 3; // define?
1914 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
1915 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
1916 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
1917 event.ctrl= evt->ctrl = 3; // define?
1919 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
1920 event.alt= evt->alt= (event.val==KM_PRESS);
1921 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
1922 event.alt= evt->alt = 3; // define?
1924 else if (event.type==COMMANDKEY) {
1925 event.oskey= evt->oskey= (event.val==KM_PRESS);
1926 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
1927 event.oskey= evt->oskey = 3; // define?
1930 if(event.val==KM_PRESS && event.keymodifier==0)
1931 evt->keymodifier= event.type; /* only set in eventstate, for next event */
1932 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
1933 event.keymodifier= evt->keymodifier= 0;
1936 /* this case happens on some systems that on holding a key pressed,
1937 generate press events without release, we still want to keep the
1938 modifier in win->eventstate, but for the press event of the same
1939 key we don't want the key modifier */
1940 if(event.keymodifier == event.type)
1941 event.keymodifier= 0;
1943 /* if test_break set, it catches this. XXX Keep global for now? */
1944 if(event.type==ESCKEY)
1947 wm_event_add(win, &event);
1952 case GHOST_kEventWheel: {
1953 GHOST_TEventWheelData* wheelData = customdata;
1955 if (wheelData->z > 0)
1956 event.type= WHEELUPMOUSE;
1958 event.type= WHEELDOWNMOUSE;
1960 event.val= KM_PRESS;
1961 wm_event_add(win, &event);
1965 case GHOST_kEventTimer: {
1967 event.custom= EVT_DATA_TIMER;
1968 event.customdata= customdata;
1969 wm_event_add(win, &event);
1974 case GHOST_kEventUnknown:
1975 case GHOST_kNumEventTypes:
1978 case GHOST_kEventWindowDeactivate: {
1979 event.type= WINDEACTIVATE;
1980 wm_event_add(win, &event);