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 *****
33 #include "DNA_listBase.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_scene_types.h"
36 #include "DNA_windowmanager_types.h"
37 #include "DNA_userdef_types.h"
39 #include "MEM_guardedalloc.h"
41 #include "GHOST_C-api.h"
43 #include "BLI_blenlib.h"
45 #include "BKE_blender.h"
46 #include "BKE_context.h"
47 #include "BKE_idprop.h"
48 #include "BKE_global.h"
50 #include "BKE_report.h"
51 #include "BKE_scene.h"
52 #include "BKE_screen.h"
53 #include "BKE_utildefines.h"
54 #include "BKE_sound.h"
56 #include "ED_fileselect.h"
58 #include "ED_screen.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"
75 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only);
77 /* ************ event management ************** */
79 void wm_event_add(wmWindow *win, wmEvent *event_to_add)
81 wmEvent *event= MEM_callocN(sizeof(wmEvent), "event");
83 *event= *event_to_add;
84 BLI_addtail(&win->queue, event);
87 void wm_event_free(wmEvent *event)
89 if(event->customdata) {
90 if(event->customdatafree) {
91 /* note: pointer to listbase struct elsewhere */
92 if(event->custom==EVT_DATA_LISTBASE)
93 BLI_freelistN(event->customdata);
95 MEM_freeN(event->customdata);
101 void wm_event_free_all(wmWindow *win)
105 while((event= win->queue.first)) {
106 BLI_remlink(&win->queue, event);
107 wm_event_free(event);
111 /* ********************* notifiers, listeners *************** */
113 static int wm_test_duplicate_notifier(wmWindowManager *wm, unsigned int type, void *reference)
117 for(note=wm->queue.first; note; note=note->next)
118 if((note->category|note->data|note->subtype|note->action) == type && note->reference == reference)
124 /* XXX: in future, which notifiers to send to other windows? */
125 void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
127 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
129 note->wm= CTX_wm_manager(C);
130 BLI_addtail(¬e->wm->queue, note);
132 note->window= CTX_wm_window(C);
135 note->swinid= CTX_wm_region(C)->swinid;
137 note->category= type & NOTE_CATEGORY;
138 note->data= type & NOTE_DATA;
139 note->subtype= type & NOTE_SUBTYPE;
140 note->action= type & NOTE_ACTION;
142 note->reference= reference;
145 void WM_main_add_notifier(unsigned int type, void *reference)
148 wmWindowManager *wm= bmain->wm.first;
150 if(wm && !wm_test_duplicate_notifier(wm, type, reference)) {
151 wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
154 BLI_addtail(¬e->wm->queue, note);
156 note->category= type & NOTE_CATEGORY;
157 note->data= type & NOTE_DATA;
158 note->subtype= type & NOTE_SUBTYPE;
159 note->action= type & NOTE_ACTION;
161 note->reference= reference;
165 static wmNotifier *wm_notifier_next(wmWindowManager *wm)
167 wmNotifier *note= wm->queue.first;
169 if(note) BLI_remlink(&wm->queue, note);
173 /* called in mainloop */
174 void wm_event_do_notifiers(bContext *C)
176 wmWindowManager *wm= CTX_wm_manager(C);
177 wmNotifier *note, *next;
183 /* cache & catch WM level notifiers, such as frame change, scene/screen set */
184 for(win= wm->windows.first; win; win= win->next) {
187 CTX_wm_window_set(C, win);
189 for(note= wm->queue.first; note; note= next) {
192 if(note->category==NC_WM) {
193 if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
195 wm_window_title(wm, win);
197 else if(note->data==ND_DATACHANGED)
198 wm_window_title(wm, win);
200 if(note->window==win) {
201 if(note->category==NC_SCREEN) {
202 if(note->data==ND_SCREENBROWSE) {
203 ED_screen_set(C, note->reference); // XXX hrms, think this over!
205 printf("screen set %p\n", note->reference);
207 else if(note->data==ND_SCREENDELETE) {
208 ED_screen_delete(C, note->reference); // XXX hrms, think this over!
210 printf("screen delete %p\n", note->reference);
215 if(note->window==win || (note->window == NULL && (note->reference == NULL || note->reference == CTX_data_scene(C)))) {
216 if(note->category==NC_SCENE) {
217 if(note->data==ND_SCENEBROWSE) {
218 ED_screen_set_scene(C, note->reference); // XXX hrms, think this over!
220 printf("scene set %p\n", note->reference);
222 else if(note->data==ND_FRAME)
225 if(note->action == NA_REMOVED) {
226 ED_screen_delete_scene(C, note->reference); // XXX hrms, think this over!
228 printf("scene delete %p\n", note->reference);
233 if(ELEM5(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE, NC_WM)) {
234 ED_info_stats_clear(CTX_data_scene(C));
235 WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
240 /* XXX, quick frame changes can cause a crash if framechange and rendering
241 * collide (happens on slow scenes), scene_update_for_newframe can be called
242 * twice which can depgraph update the same object at once */
245 /* depsgraph gets called, might send more notifiers */
246 ED_update_for_newframe(CTX_data_main(C), win->screen->scene, win->screen, 1);
251 /* the notifiers are sent without context, to keep it clean */
252 while( (note=wm_notifier_next(wm)) ) {
253 for(win= wm->windows.first; win; win= win->next) {
255 /* filter out notifiers */
256 if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
257 else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
262 /* XXX context in notifiers? */
263 CTX_wm_window_set(C, win);
265 /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
266 ED_screen_do_listen(win, note);
268 for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
269 ED_region_do_listen(ar, note);
272 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
273 ED_area_do_listen(sa, note);
274 for(ar=sa->regionbase.first; ar; ar= ar->next) {
275 ED_region_do_listen(ar, note);
284 /* cached: editor refresh callbacks now, they get context */
285 for(win= wm->windows.first; win; win= win->next) {
288 CTX_wm_window_set(C, win);
289 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
291 CTX_wm_area_set(C, sa);
292 ED_area_do_refresh(C, sa);
296 /* XXX make lock in future, or separated derivedmesh users in scene */
298 /* depsgraph & animation: update tagged datablocks */
299 scene_update_tagged(CTX_data_main(C), win->screen->scene);
302 CTX_wm_window_set(C, NULL);
305 /* ********************* ui handler ******************* */
307 static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *event, int always_pass)
309 ScrArea *area= CTX_wm_area(C);
310 ARegion *region= CTX_wm_region(C);
311 ARegion *menu= CTX_wm_menu(C);
314 /* we set context to where ui handler came from */
315 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
316 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
317 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
319 retval= handler->ui_handle(C, event, handler->ui_userdata);
321 /* putting back screen context */
322 if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
323 CTX_wm_area_set(C, area);
324 CTX_wm_region_set(C, region);
325 CTX_wm_menu_set(C, menu);
328 /* this special cases is for areas and regions that get removed */
329 CTX_wm_area_set(C, NULL);
330 CTX_wm_region_set(C, NULL);
331 CTX_wm_menu_set(C, NULL);
334 if(retval == WM_UI_HANDLER_BREAK)
335 return WM_HANDLER_BREAK;
337 return WM_HANDLER_CONTINUE;
340 static void wm_handler_ui_cancel(bContext *C)
342 wmWindow *win= CTX_wm_window(C);
343 ARegion *ar= CTX_wm_region(C);
344 wmEventHandler *handler, *nexthandler;
349 for(handler= ar->handlers.first; handler; handler= nexthandler) {
350 nexthandler= handler->next;
352 if(handler->ui_handle) {
353 wmEvent event= *(win->eventstate);
354 event.type= EVT_BUT_CANCEL;
355 handler->ui_handle(C, &event, handler->ui_userdata);
360 /* ********************* operators ******************* */
362 int WM_operator_poll(bContext *C, wmOperatorType *ot)
364 wmOperatorTypeMacro *otmacro;
366 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
367 wmOperatorType *ot_macro= WM_operatortype_find(otmacro->idname, 0);
369 if(0==WM_operator_poll(C, ot_macro))
373 /* python needs operator type, so we added exception for it */
375 return ot->pyop_poll(C, ot);
382 /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */
383 int WM_operator_poll_context(bContext *C, wmOperatorType *ot, int context)
385 return wm_operator_call_internal(C, ot, NULL, NULL, context, TRUE);
388 static void wm_operator_print(bContext *C, wmOperator *op)
390 /* context is needed for enum function */
391 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
396 static void wm_operator_reports(bContext *C, wmOperator *op, int retval, int popup)
398 wmWindowManager *wm = CTX_wm_manager(C);
399 ReportList *reports = CTX_wm_reports(C);
403 if(op->reports->list.first)
404 uiPupMenuReports(C, op->reports);
406 if(retval & OPERATOR_FINISHED) {
408 wm_operator_print(C, op); /* todo - this print may double up, might want to check more flags then the FINISHED */
410 if (op->type->flag & OPTYPE_REGISTER) {
411 /* Report the python string representation of the operator */
412 buf = WM_operator_pystring(C, op->type, op->ptr, 1);
413 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
418 if (op->reports->list.first) {
419 ReportTimerInfo *rti;
421 /* add reports to the global list, otherwise they are not seen */
422 addlisttolist(&CTX_wm_reports(C)->list, &op->reports->list);
424 /* After adding reports to the global list, reset the report timer. */
425 WM_event_remove_timer(wm, NULL, reports->reporttimer);
427 /* Records time since last report was added */
428 reports->reporttimer= WM_event_add_timer(wm, CTX_wm_window(C), TIMER, 0.05);
430 rti = MEM_callocN(sizeof(ReportTimerInfo), "ReportTimerInfo");
431 reports->reporttimer->customdata = rti;
435 /* this function is mainly to check that the rules for freeing
436 * an operator are kept in sync.
438 static int wm_operator_register_check(wmWindowManager *wm, wmOperatorType *ot)
440 return wm && (wm->op_undo_depth == 0) && (ot->flag & OPTYPE_REGISTER);
443 static void wm_operator_finished(bContext *C, wmOperator *op, int repeat)
445 wmWindowManager *wm= CTX_wm_manager(C);
447 op->customdata= NULL;
449 /* we don't want to do undo pushes for operators that are being
450 called from operators that already do an undo push. usually
451 this will happen for python operators that call C operators */
452 if(wm->op_undo_depth == 0)
453 if(op->type->flag & OPTYPE_UNDO)
454 ED_undo_push_op(C, op);
458 char *buf = WM_operator_pystring(C, op->type, op->ptr, 1);
459 BKE_report(CTX_wm_reports(C), RPT_OPERATOR, buf);
463 if(wm_operator_register_check(wm, op->type))
464 wm_operator_register(C, op);
466 WM_operator_free(op);
470 /* if repeat is true, it doesn't register again, nor does it free */
471 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
473 wmWindowManager *wm= CTX_wm_manager(C);
474 int retval= OPERATOR_CANCELLED;
476 CTX_wm_operator_poll_msg_set(C, NULL);
478 if(op==NULL || op->type==NULL)
481 if(0==WM_operator_poll(C, op->type))
485 if(op->type->flag & OPTYPE_UNDO)
488 retval= op->type->exec(C, op);
490 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
494 if (retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED) && repeat == 0)
495 wm_operator_reports(C, op, retval, 0);
497 if(retval & OPERATOR_FINISHED)
498 wm_operator_finished(C, op, repeat);
500 WM_operator_free(op);
502 return retval | OPERATOR_HANDLED;
506 /* for running operators with frozen context (modal handlers, menus) */
507 int WM_operator_call(bContext *C, wmOperator *op)
509 return wm_operator_exec(C, op, 0);
512 /* do this operator again, put here so it can share above code */
513 int WM_operator_repeat(bContext *C, wmOperator *op)
515 return wm_operator_exec(C, op, 1);
517 /* TRUE if WM_operator_repeat can run
518 * simple check for now but may become more involved.
519 * To be sure the operator can run call WM_operator_poll(C, op->type) also, since this call
520 * checks if WM_operator_repeat() can run at all, not that it WILL run at any time. */
521 int WM_operator_repeat_check(const bContext *UNUSED(C), wmOperator *op)
523 return op->type->exec != NULL;
526 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
528 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
530 /* XXX adding new operator could be function, only happens here now */
532 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
534 /* initialize properties, either copy or create */
535 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
536 if(properties && properties->data) {
537 op->properties= IDP_CopyProperty(properties->data);
540 IDPropertyTemplate val = {0};
541 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
543 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
545 /* initialize error reports */
547 op->reports= reports; /* must be initialized already */
550 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
551 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
554 /* recursive filling of operator macro list */
555 if(ot->macro.first) {
556 static wmOperator *motherop= NULL;
557 wmOperatorTypeMacro *otmacro;
560 /* ensure all ops are in execution order in 1 list */
567 /* if properties exist, it will contain everything needed */
569 otmacro= ot->macro.first;
571 RNA_STRUCT_BEGIN(properties, prop) {
576 /* skip invalid properties */
577 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
579 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
580 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
581 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
583 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
585 BLI_addtail(&motherop->macro, opm);
586 opm->opm= motherop; /* pointer to mom, for modal() */
588 otmacro= otmacro->next;
593 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
594 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
595 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
597 BLI_addtail(&motherop->macro, opm);
598 opm->opm= motherop; /* pointer to mom, for modal() */
606 WM_operator_properties_sanitize(op->ptr, 0);
611 static void wm_region_mouse_co(bContext *C, wmEvent *event)
613 ARegion *ar= CTX_wm_region(C);
615 /* compatibility convention */
616 event->mval[0]= event->x - ar->winrct.xmin;
617 event->mval[1]= event->y - ar->winrct.ymin;
621 int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports, short poll_only)
623 wmWindowManager *wm= CTX_wm_manager(C);
624 int retval= OPERATOR_PASS_THROUGH;
626 /* this is done because complicated setup is done to call this function that is better not duplicated */
628 return WM_operator_poll(C, ot);
630 if(WM_operator_poll(C, ot)) {
631 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
633 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
634 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
636 if(op->type->invoke && event) {
637 wm_region_mouse_co(C, event);
639 if(op->type->flag & OPTYPE_UNDO)
642 retval= op->type->invoke(C, op, event);
644 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
647 else if(op->type->exec) {
648 if(op->type->flag & OPTYPE_UNDO)
651 retval= op->type->exec(C, op);
653 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
657 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
659 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
660 * currently python only uses this */
661 if (!(retval & OPERATOR_HANDLED) && retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
662 /* only show the report if the report list was not given in the function */
663 wm_operator_reports(C, op, retval, (reports==NULL));
666 if(retval & OPERATOR_HANDLED)
667 ; /* do nothing, wm_operator_exec() has been called somewhere */
668 else if(retval & OPERATOR_FINISHED) {
669 wm_operator_finished(C, op, 0);
671 else if(retval & OPERATOR_RUNNING_MODAL) {
672 /* grab cursor during blocking modal ops (X11)
673 * Also check for macro
675 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
676 int bounds[4] = {-1,-1,-1,-1};
680 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
682 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
686 ARegion *ar= CTX_wm_region(C);
688 bounds[0]= ar->winrct.xmin;
689 bounds[1]= ar->winrct.ymax;
690 bounds[2]= ar->winrct.xmax;
691 bounds[3]= ar->winrct.ymin;
695 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
698 /* cancel UI handlers, typically tooltips that can hang around
699 while dragging the view or worse, that stay there permanently
700 after the modal operator has swallowed all events and passed
701 none to the UI handler */
702 wm_handler_ui_cancel(C);
705 WM_operator_free(op);
711 /* WM_operator_name_call is the main accessor function
712 * this is for python to access since its done the operator lookup
714 * invokes operator in context */
715 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only)
717 wmWindow *window= CTX_wm_window(C);
722 CTX_wm_operator_poll_msg_set(C, NULL);
727 case WM_OP_INVOKE_DEFAULT:
728 case WM_OP_INVOKE_REGION_WIN:
729 case WM_OP_INVOKE_AREA:
730 case WM_OP_INVOKE_SCREEN:
731 /* window is needed for invoke, cancel operator */
735 event= window->eventstate;
743 case WM_OP_EXEC_REGION_WIN:
744 case WM_OP_INVOKE_REGION_WIN:
745 case WM_OP_EXEC_REGION_CHANNELS:
746 case WM_OP_INVOKE_REGION_CHANNELS:
747 case WM_OP_EXEC_REGION_PREVIEW:
748 case WM_OP_INVOKE_REGION_PREVIEW:
750 /* forces operator to go to the region window/channels/preview, for header menus
751 * but we stay in the same region if we are already in one
753 ARegion *ar= CTX_wm_region(C);
754 ScrArea *area= CTX_wm_area(C);
755 int type = RGN_TYPE_WINDOW;
758 case WM_OP_EXEC_REGION_CHANNELS:
759 case WM_OP_INVOKE_REGION_CHANNELS:
760 type = RGN_TYPE_CHANNELS;
763 case WM_OP_EXEC_REGION_PREVIEW:
764 case WM_OP_INVOKE_REGION_PREVIEW:
765 type = RGN_TYPE_PREVIEW;
768 case WM_OP_EXEC_REGION_WIN:
769 case WM_OP_INVOKE_REGION_WIN:
771 type = RGN_TYPE_WINDOW;
775 if(!(ar && ar->regiontype == type) && area) {
776 ARegion *ar1= BKE_area_find_region_type(area, type);
778 CTX_wm_region_set(C, ar1);
781 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
783 /* set region back */
784 CTX_wm_region_set(C, ar);
788 case WM_OP_EXEC_AREA:
789 case WM_OP_INVOKE_AREA:
791 /* remove region from context */
792 ARegion *ar= CTX_wm_region(C);
794 CTX_wm_region_set(C, NULL);
795 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
796 CTX_wm_region_set(C, ar);
800 case WM_OP_EXEC_SCREEN:
801 case WM_OP_INVOKE_SCREEN:
803 /* remove region + area from context */
804 ARegion *ar= CTX_wm_region(C);
805 ScrArea *area= CTX_wm_area(C);
807 CTX_wm_region_set(C, NULL);
808 CTX_wm_area_set(C, NULL);
809 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
810 CTX_wm_region_set(C, ar);
811 CTX_wm_area_set(C, area);
815 case WM_OP_EXEC_DEFAULT:
816 case WM_OP_INVOKE_DEFAULT:
817 return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
825 /* invokes operator in context */
826 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
828 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
830 return wm_operator_call_internal(C, ot, properties, NULL, context, FALSE);
835 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
836 - wmOperatorType is used instead of operator name since python already has the operator type
837 - poll() must be called by python before this runs.
838 - reports can be passed to this function (so python can report them as exceptions)
840 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
842 int retval= OPERATOR_CANCELLED;
846 op= wm_operator_create(wm, ot, properties, reports);
848 if (op->type->exec) {
849 if(op->type->flag & OPTYPE_UNDO)
852 retval= op->type->exec(C, op);
854 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
858 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
861 retval= wm_operator_call_internal(C, ot, properties, reports, context, FALSE);
863 /* keep the reports around if needed later */
864 if ( (retval & OPERATOR_RUNNING_MODAL) ||
865 ((retval & OPERATOR_FINISHED) && wm_operator_register_check(CTX_wm_manager(C), ot))
867 reports->flag |= RPT_FREE; /* let blender manage freeing */
874 /* ********************* handlers *************** */
876 /* future extra customadata free? */
877 void wm_event_free_handler(wmEventHandler *handler)
882 /* only set context when area/region is part of screen */
883 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
885 bScreen *screen= CTX_wm_screen(C);
887 if(screen && handler->op) {
888 if(handler->op_area==NULL)
889 CTX_wm_area_set(C, NULL);
893 for(sa= screen->areabase.first; sa; sa= sa->next)
894 if(sa==handler->op_area)
897 /* when changing screen layouts with running modal handlers (like render display), this
898 is not an error to print */
899 if(handler->op==NULL)
900 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
904 CTX_wm_area_set(C, sa);
905 for(ar= sa->regionbase.first; ar; ar= ar->next)
906 if(ar==handler->op_region)
908 /* XXX no warning print here, after full-area and back regions are remade */
910 CTX_wm_region_set(C, ar);
916 /* called on exit or remove area, only here call cancel callback */
917 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
919 wmEventHandler *handler;
920 wmWindowManager *wm= CTX_wm_manager(C);
922 /* C is zero on freeing database, modal handlers then already were freed */
923 while((handler=handlers->first)) {
924 BLI_remlink(handlers, handler);
927 if(handler->op->type->cancel) {
928 ScrArea *area= CTX_wm_area(C);
929 ARegion *region= CTX_wm_region(C);
931 wm_handler_op_context(C, handler);
933 if(handler->op->type->flag & OPTYPE_UNDO)
936 handler->op->type->cancel(C, handler->op);
938 if(handler->op->type->flag & OPTYPE_UNDO)
941 CTX_wm_area_set(C, area);
942 CTX_wm_region_set(C, region);
945 WM_cursor_ungrab(CTX_wm_window(C));
946 WM_operator_free(handler->op);
948 else if(handler->ui_remove) {
949 ScrArea *area= CTX_wm_area(C);
950 ARegion *region= CTX_wm_region(C);
951 ARegion *menu= CTX_wm_menu(C);
953 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
954 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
955 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
957 handler->ui_remove(C, handler->ui_userdata);
959 CTX_wm_area_set(C, area);
960 CTX_wm_region_set(C, region);
961 CTX_wm_menu_set(C, menu);
964 wm_event_free_handler(handler);
968 /* do userdef mappings */
969 int WM_userdef_event_map(int kmitype)
973 if(U.flag & USER_LMOUSESELECT)
979 if(U.flag & USER_LMOUSESELECT)
985 if(U.uiflag & USER_WHEELZOOMDIR)
988 return WHEELDOWNMOUSE;
991 if(U.uiflag & USER_WHEELZOOMDIR)
992 return WHEELDOWNMOUSE;
997 if(U.flag & USER_LMOUSESELECT)
1003 if(U.flag & USER_LMOUSESELECT)
1012 static void wm_eventemulation(wmEvent *event)
1014 static int mmb_emulated = 0; /* this should be in a data structure somwhere */
1016 /* middlemouse emulation */
1017 if(U.flag & USER_TWOBUTTONMOUSE) {
1018 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
1019 event->type = MIDDLEMOUSE;
1021 mmb_emulated = event->val;
1026 /* rightmouse emulation */
1027 if(U.flag & USER_TWOBUTTONMOUSE) {
1028 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
1029 event->type = RIGHTMOUSE;
1031 mmb_emulated = event->val;
1036 /* numpad emulation */
1037 if(U.flag & USER_NONUMPAD) {
1038 switch(event->type) {
1039 case ZEROKEY: event->type = PAD0; break;
1040 case ONEKEY: event->type = PAD1; break;
1041 case TWOKEY: event->type = PAD2; break;
1042 case THREEKEY: event->type = PAD3; break;
1043 case FOURKEY: event->type = PAD4; break;
1044 case FIVEKEY: event->type = PAD5; break;
1045 case SIXKEY: event->type = PAD6; break;
1046 case SEVENKEY: event->type = PAD7; break;
1047 case EIGHTKEY: event->type = PAD8; break;
1048 case NINEKEY: event->type = PAD9; break;
1049 case MINUSKEY: event->type = PADMINUS; break;
1050 case EQUALKEY: event->type = PADPLUSKEY; break;
1051 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
1056 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
1058 int kmitype= WM_userdef_event_map(kmi->type);
1060 if(kmi->flag & KMI_INACTIVE) return 0;
1062 /* the matching rules */
1063 if(kmitype==KM_TEXTINPUT)
1064 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
1066 if(winevent->type!=kmitype) return 0;
1068 if(kmi->val!=KM_ANY)
1069 if(winevent->val!=kmi->val) return 0;
1071 /* modifiers also check bits, so it allows modifier order */
1072 if(kmi->shift!=KM_ANY)
1073 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
1074 if(kmi->ctrl!=KM_ANY)
1075 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
1076 if(kmi->alt!=KM_ANY)
1077 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
1078 if(kmi->oskey!=KM_ANY)
1079 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
1081 if(kmi->keymodifier)
1082 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1084 /* key modifiers always check when event has it */
1085 /* otherwise regular keypresses with keymodifier still work */
1086 if(winevent->keymodifier)
1087 if(ISTEXTINPUT(winevent->type))
1088 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1093 static int wm_event_always_pass(wmEvent *event)
1095 /* some events we always pass on, to ensure proper communication */
1096 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
1099 /* operator exists */
1100 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1102 /* support for modal keymap in macros */
1106 if(op->type->modalkeymap) {
1107 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1110 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1111 if(wm_eventmatch(event, kmi)) {
1113 event->type= EVT_MODAL_MAP;
1114 event->val= kmi->propvalue;
1120 /* Warning: this function removes a modal handler, when finished */
1121 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1123 int retval= OPERATOR_PASS_THROUGH;
1125 /* derived, modal or blocking operator */
1127 wmOperator *op= handler->op;
1128 wmOperatorType *ot= op->type;
1131 /* we set context to where modal handler came from */
1132 wmWindowManager *wm= CTX_wm_manager(C);
1133 ScrArea *area= CTX_wm_area(C);
1134 ARegion *region= CTX_wm_region(C);
1136 wm_handler_op_context(C, handler);
1137 wm_region_mouse_co(C, event);
1138 wm_event_modalkeymap(C, op, event);
1140 if(ot->flag & OPTYPE_UNDO)
1141 wm->op_undo_depth++;
1143 retval= ot->modal(C, op, event);
1145 if(ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1146 wm->op_undo_depth--;
1148 /* putting back screen context, reval can pass trough after modal failures! */
1149 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1150 CTX_wm_area_set(C, area);
1151 CTX_wm_region_set(C, region);
1154 /* this special cases is for areas and regions that get removed */
1155 CTX_wm_area_set(C, NULL);
1156 CTX_wm_region_set(C, NULL);
1159 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED))
1160 wm_operator_reports(C, op, retval, 0);
1162 if(retval & OPERATOR_FINISHED) {
1163 wm_operator_finished(C, op, 0);
1166 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1167 WM_operator_free(op);
1171 /* remove modal handler, operator itself should have been cancelled and freed */
1172 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1173 WM_cursor_ungrab(CTX_wm_window(C));
1175 BLI_remlink(handlers, handler);
1176 wm_event_free_handler(handler);
1178 /* prevent silly errors from operator users */
1179 //retval &= ~OPERATOR_PASS_THROUGH;
1184 printf("wm_handler_operator_call error\n");
1187 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1190 retval= wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
1193 /* Finished and pass through flag as handled */
1194 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1195 return WM_HANDLER_HANDLED;
1197 /* Modal unhandled, break */
1198 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1199 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1201 if(retval & OPERATOR_PASS_THROUGH)
1202 return WM_HANDLER_CONTINUE;
1204 return WM_HANDLER_BREAK;
1207 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1208 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1210 wmWindowManager *wm= CTX_wm_manager(C);
1212 int action= WM_HANDLER_CONTINUE;
1214 if(event->type != EVT_FILESELECT)
1216 if(handler->op != (wmOperator *)event->customdata)
1219 switch(event->val) {
1220 case EVT_FILESELECT_OPEN:
1221 case EVT_FILESELECT_FULL_OPEN:
1225 /* sa can be null when window A is active, but mouse is over window B */
1226 /* in this case, open file select in original window A */
1227 if (handler->op_area == NULL) {
1228 bScreen *screen = CTX_wm_screen(C);
1229 sa = (ScrArea *)screen->areabase.first;
1231 sa = handler->op_area;
1233 if(event->val==EVT_FILESELECT_OPEN)
1234 ED_area_newspace(C, sa, SPACE_FILE);
1236 ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1238 /* settings for filebrowser, sfile is not operator owner but sends events */
1239 sa = CTX_wm_area(C);
1240 sfile= (SpaceFile*)sa->spacedata.first;
1241 sfile->op= handler->op;
1243 ED_fileselect_set_params(sfile);
1245 action= WM_HANDLER_BREAK;
1249 case EVT_FILESELECT_EXEC:
1250 case EVT_FILESELECT_CANCEL:
1251 case EVT_FILESELECT_EXTERNAL_CANCEL:
1253 /* XXX validate area and region? */
1254 bScreen *screen= CTX_wm_screen(C);
1256 /* remlink now, for load file case before removing*/
1257 BLI_remlink(handlers, handler);
1259 if(event->val!=EVT_FILESELECT_EXTERNAL_CANCEL) {
1260 if(screen != handler->filescreen) {
1261 ED_screen_full_prevspace(C, CTX_wm_area(C));
1264 ED_area_prevspace(C, CTX_wm_area(C));
1268 wm_handler_op_context(C, handler);
1270 /* needed for uiPupMenuReports */
1272 if(event->val==EVT_FILESELECT_EXEC) {
1273 #if 0 // use REDALERT now
1275 /* a bit weak, might become arg for WM_event_fileselect? */
1276 /* XXX also extension code in image-save doesnt work for this yet */
1277 if (RNA_struct_find_property(handler->op->ptr, "check_existing") &&
1278 RNA_boolean_get(handler->op->ptr, "check_existing")) {
1279 char *path= RNA_string_get_alloc(handler->op->ptr, "filepath", NULL, 0);
1280 /* this gives ownership to pupmenu */
1281 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1290 if(handler->op->type->flag & OPTYPE_UNDO)
1291 wm->op_undo_depth++;
1293 retval= handler->op->type->exec(C, handler->op);
1295 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1296 if(handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1297 wm->op_undo_depth--;
1299 if (retval & OPERATOR_FINISHED)
1301 wm_operator_print(C, handler->op);
1303 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1304 if(CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
1305 if(handler->op->type->flag & OPTYPE_UNDO)
1306 ED_undo_push_op(C, handler->op);
1308 if(handler->op->reports->list.first) {
1310 /* FIXME, temp setting window, this is really bad!
1311 * only have because lib linking errors need to be seen by users :(
1312 * it can be removed without breaking anything but then no linking errors - campbell */
1313 wmWindow *win_prev= CTX_wm_window(C);
1315 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1317 handler->op->reports->printlevel = RPT_WARNING;
1318 uiPupMenuReports(C, handler->op->reports);
1320 /* XXX - copied from 'wm_operator_finished()' */
1321 /* add reports to the global list, otherwise they are not seen */
1322 addlisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
1324 CTX_wm_window_set(C, win_prev);
1327 WM_operator_free(handler->op);
1331 if(handler->op->type->cancel) {
1332 if(handler->op->type->flag & OPTYPE_UNDO)
1333 wm->op_undo_depth++;
1335 handler->op->type->cancel(C, handler->op);
1337 if(handler->op->type->flag & OPTYPE_UNDO)
1338 wm->op_undo_depth--;
1341 WM_operator_free(handler->op);
1344 CTX_wm_area_set(C, NULL);
1346 wm_event_free_handler(handler);
1348 action= WM_HANDLER_BREAK;
1356 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1358 if(handler->bbwin) {
1359 if(handler->bblocal) {
1360 rcti rect= *handler->bblocal;
1361 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1363 if(BLI_in_rcti(&rect, event->x, event->y))
1365 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1371 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1373 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1382 static int wm_action_not_handled(int action)
1384 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1387 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1389 wmWindowManager *wm= CTX_wm_manager(C);
1390 wmEventHandler *handler, *nexthandler;
1391 int action= WM_HANDLER_CONTINUE;
1394 if(handlers==NULL) return action;
1396 /* modal handlers can get removed in this loop, we keep the loop this way */
1397 for(handler= handlers->first; handler; handler= nexthandler) {
1398 nexthandler= handler->next;
1400 /* optional boundbox */
1401 if(handler_boundbox_test(handler, event)) {
1402 /* in advance to avoid access to freed event on window close */
1403 always_pass= wm_event_always_pass(event);
1405 /* modal+blocking handler */
1406 if(handler->flag & WM_HANDLER_BLOCKING)
1407 action |= WM_HANDLER_BREAK;
1409 if(handler->keymap) {
1410 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1413 if(!keymap->poll || keymap->poll(C)) {
1414 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1415 if(wm_eventmatch(event, kmi)) {
1417 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1419 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1420 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1426 else if(handler->ui_handle) {
1427 action |= wm_handler_ui_call(C, handler, event, always_pass);
1429 else if(handler->type==WM_HANDLER_FILESELECT) {
1430 /* screen context changes here */
1431 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1433 else if(handler->dropboxes) {
1434 if(event->type==EVT_DROP) {
1435 wmDropBox *drop= handler->dropboxes->first;
1436 for(; drop; drop= drop->next) {
1437 /* other drop custom types allowed */
1438 if(event->custom==EVT_DATA_LISTBASE) {
1439 ListBase *lb= (ListBase *)event->customdata;
1441 for(drag= lb->first; drag; drag= drag->next) {
1442 if(drop->poll(C, drag, event)) {
1443 drop->copy(drag, drop);
1445 wm_operator_invoke(C, drop->ot, event, drop->ptr, NULL, FALSE);
1446 action |= WM_HANDLER_BREAK;
1454 /* modal, swallows all */
1455 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1458 if(action & WM_HANDLER_BREAK) {
1460 action &= ~WM_HANDLER_BREAK;
1467 if(CTX_wm_window(C)==NULL)
1471 /* test for CLICK event */
1472 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1473 wmWindow *win = CTX_wm_window(C);
1475 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1476 /* test for double click first,
1477 * note1: this can be problematic because single click operators can get the
1478 * double click event but then with old mouse coords which is highly confusing,
1479 * so check for mouse moves too.
1480 * note2: the first click event will be handled but still used to create a
1481 * double click event if clicking again quickly.
1482 * If no double click events are found itwill fallback to a single click.
1483 * So a double click event can result in 2 successive single click calls
1484 * if its not handled by the keymap - campbell */
1485 if ( (ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
1486 (ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
1487 ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time)
1489 event->val = KM_DBL_CLICK;
1490 /* removed this because in cases where we're this is used as a single click
1491 * event, this will give old coords, since the distance is checked above, using new coords should be ok. */
1492 // event->x = win->eventstate->prevclickx;
1493 // event->y = win->eventstate->prevclicky;
1494 action |= wm_handlers_do(C, event, handlers);
1497 if (wm_action_not_handled(action)) {
1498 event->val = KM_CLICK;
1499 action |= wm_handlers_do(C, event, handlers);
1503 /* revert value if not handled */
1504 if (wm_action_not_handled(action)) {
1505 event->val = KM_RELEASE;
1513 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1515 if(wm_event_always_pass(event))
1517 if(BLI_in_rcti(rect, event->x, event->y))
1519 if(event->type==MOUSEMOVE) {
1520 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1528 static ScrArea *area_event_inside(bContext *C, int x, int y)
1530 bScreen *screen= CTX_wm_screen(C);
1534 for(sa= screen->areabase.first; sa; sa= sa->next)
1535 if(BLI_in_rcti(&sa->totrct, x, y))
1540 static ARegion *region_event_inside(bContext *C, int x, int y)
1542 bScreen *screen= CTX_wm_screen(C);
1543 ScrArea *area= CTX_wm_area(C);
1547 for(ar= area->regionbase.first; ar; ar= ar->next)
1548 if(BLI_in_rcti(&ar->winrct, x, y))
1553 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1556 for(; pc; pc= pc->next) {
1557 if(pc->poll == NULL || pc->poll(C)) {
1558 wmWindow *win= CTX_wm_window(C);
1559 win->screen->do_draw_paintcursor= 1;
1560 wm_tag_redraw_overlay(win, ar);
1566 /* called on mousemove, check updates for paintcursors */
1567 /* context was set on active area and region */
1568 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1570 wmWindowManager *wm= CTX_wm_manager(C);
1572 if(wm->paintcursors.first) {
1573 ARegion *ar= CTX_wm_region(C);
1575 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1577 /* if previous position was not in current region, we have to set a temp new context */
1578 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1579 ScrArea *sa= CTX_wm_area(C);
1581 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1582 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1584 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1586 CTX_wm_area_set(C, sa);
1587 CTX_wm_region_set(C, ar);
1592 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1594 if(wm->drags.first==NULL) return;
1596 if(event->type==MOUSEMOVE)
1597 win->screen->do_draw_drag= 1;
1598 else if(event->type==ESCKEY) {
1599 BLI_freelistN(&wm->drags);
1600 win->screen->do_draw_drag= 1;
1602 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1603 event->type= EVT_DROP;
1605 /* create customdata, first free existing */
1606 if(event->customdata) {
1607 if(event->customdatafree)
1608 MEM_freeN(event->customdata);
1611 event->custom= EVT_DATA_LISTBASE;
1612 event->customdata= &wm->drags;
1613 event->customdatafree= 1;
1615 /* clear drop icon */
1616 win->screen->do_draw_drag= 1;
1618 /* restore cursor (disabled, see wm_dragdrop.c) */
1619 // WM_cursor_restore(win);
1622 /* overlap fails otherwise */
1623 if(win->screen->do_draw_drag)
1624 if(win->drawmethod == USER_DRAW_OVERLAP)
1625 win->screen->do_draw= 1;
1629 /* called in main loop */
1630 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1631 void wm_event_do_handlers(bContext *C)
1633 wmWindowManager *wm= CTX_wm_manager(C);
1636 for(win= wm->windows.first; win; win= win->next) {
1639 if( win->screen==NULL )
1640 wm_event_free_all(win);
1642 Scene* scene = win->screen->scene;
1645 int playing = sound_scene_playing(win->screen->scene);
1648 CTX_wm_window_set(C, win);
1649 CTX_wm_screen_set(C, win->screen);
1650 CTX_data_scene_set(C, scene);
1652 if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer))){
1653 ED_screen_animation_play(C, -1, 1);
1657 int ncfra = sound_sync_scene(scene) * FPS + 0.5;
1658 if(ncfra != scene->r.cfra) {
1659 scene->r.cfra = ncfra;
1660 ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
1661 WM_event_add_notifier(C, NC_WINDOW, NULL);
1665 CTX_data_scene_set(C, NULL);
1666 CTX_wm_screen_set(C, NULL);
1667 CTX_wm_window_set(C, NULL);
1672 while( (event= win->queue.first) ) {
1673 int action = WM_HANDLER_CONTINUE;
1675 if((G.f & G_DEBUG) && event && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1676 printf("pass on evt %d val %d\n", event->type, event->val);
1678 wm_eventemulation(event);
1680 CTX_wm_window_set(C, win);
1682 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1683 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1684 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1686 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1687 wm_window_make_drawable(C, win);
1689 /* first we do priority handlers, modal + some limited keymaps */
1690 action |= wm_handlers_do(C, event, &win->modalhandlers);
1693 if(CTX_wm_window(C)==NULL)
1696 /* check dragging, creates new event or frees, adds draw tag */
1697 wm_event_drag_test(wm, win, event);
1699 /* builtin tweak, if action is break it removes tweak */
1700 wm_tweakevent_test(C, event, action);
1702 if((action & WM_HANDLER_BREAK) == 0) {
1707 /* XXX to solve, here screen handlers? */
1708 if(event->type==MOUSEMOVE) {
1709 /* state variables in screen, cursors */
1710 ED_screen_set_subwinactive(win, event);
1711 /* for regions having custom cursors */
1712 wm_paintcursor_test(C, event);
1715 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1716 if(wm_event_inside_i(event, &sa->totrct)) {
1717 CTX_wm_area_set(C, sa);
1719 if((action & WM_HANDLER_BREAK) == 0) {
1720 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1721 if(wm_event_inside_i(event, &ar->winrct)) {
1722 CTX_wm_region_set(C, ar);
1724 /* does polls for drop regions and checks uibuts */
1725 /* need to be here to make sure region context is true */
1726 if(ELEM(event->type, MOUSEMOVE, EVT_DROP)) {
1727 wm_region_mouse_co(C, event);
1728 wm_drags_check_ops(C, event);
1731 action |= wm_handlers_do(C, event, &ar->handlers);
1733 /* fileread case (python), [#29489] */
1734 if(CTX_wm_window(C)==NULL)
1737 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1739 if(action & WM_HANDLER_BREAK)
1745 CTX_wm_region_set(C, NULL);
1747 if((action & WM_HANDLER_BREAK) == 0)
1748 action |= wm_handlers_do(C, event, &sa->handlers);
1750 CTX_wm_area_set(C, NULL);
1752 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1756 if((action & WM_HANDLER_BREAK) == 0) {
1757 /* also some non-modal handlers need active area/region */
1758 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1759 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1761 action |= wm_handlers_do(C, event, &win->handlers);
1764 if(CTX_wm_window(C)==NULL)
1768 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1769 doing it on ghost queue gives errors when mousemoves go over area borders */
1770 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1771 win->eventstate->prevx= event->x;
1772 win->eventstate->prevy= event->y;
1776 /* store last event for this window */
1777 /* mousemove and timer events don't overwrite last type */
1778 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1779 if (wm_action_not_handled(action)) {
1780 if (win->eventstate->prevtype == event->type) {
1781 /* set click time on first click (press -> release) */
1782 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1783 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1784 win->eventstate->prevclickx = event->x;
1785 win->eventstate->prevclicky = event->y;
1788 /* reset click time if event type not the same */
1789 win->eventstate->prevclicktime = 0;
1792 win->eventstate->prevval = event->val;
1793 win->eventstate->prevtype = event->type;
1794 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1795 win->eventstate->prevtype = event->type;
1796 win->eventstate->prevval = event->val;
1797 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1798 win->eventstate->prevclickx = event->x;
1799 win->eventstate->prevclicky = event->y;
1800 } else { /* reset if not */
1801 win->eventstate->prevtype = -1;
1802 win->eventstate->prevval = 0;
1803 win->eventstate->prevclicktime = 0;
1807 /* unlink and free here, blender-quit then frees all */
1808 BLI_remlink(&win->queue, event);
1809 wm_event_free(event);
1813 /* only add mousemove when queue was read entirely */
1814 if(win->addmousemove && win->eventstate) {
1815 wmEvent tevent= *(win->eventstate);
1816 tevent.type= MOUSEMOVE;
1817 tevent.prevx= tevent.x;
1818 tevent.prevy= tevent.y;
1819 wm_event_add(win, &tevent);
1820 win->addmousemove= 0;
1823 CTX_wm_window_set(C, NULL);
1827 /* ********** filesector handling ************ */
1829 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1831 /* add to all windows! */
1834 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1835 wmEvent event= *win->eventstate;
1837 event.type= EVT_FILESELECT;
1838 event.val= eventval;
1839 event.customdata= ophandle; // only as void pointer type check
1841 wm_event_add(win, &event);
1845 /* operator is supposed to have a filled "path" property */
1846 /* optional property: filetype (XXX enum?) */
1848 /* Idea is to keep a handler alive on window queue, owning the operator.
1849 The filewindow can send event to make it execute, thus ensuring
1850 executing happens outside of lower level queues, with UI refreshed.
1851 Should also allow multiwin solutions */
1853 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1855 wmEventHandler *handler;
1856 wmWindow *win= CTX_wm_window(C);
1857 int full= 1; // XXX preset?
1859 /* only allow file selector open per window bug [#23553] */
1860 for(handler= win->modalhandlers.first; handler; handler=handler->next) {
1861 if(handler->type == WM_HANDLER_FILESELECT)
1865 handler = MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1867 handler->type= WM_HANDLER_FILESELECT;
1869 handler->op_area= CTX_wm_area(C);
1870 handler->op_region= CTX_wm_region(C);
1871 handler->filescreen= CTX_wm_screen(C);
1873 BLI_addhead(&win->modalhandlers, handler);
1875 /* check props once before invoking if check is available
1876 * ensures initial properties are valid */
1877 if(op->type->check) {
1878 op->type->check(C, op); /* ignore return value */
1881 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1884 /* lets not expose struct outside wm? */
1885 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1887 handler->flag= flag;
1890 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1892 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1893 wmWindow *win= CTX_wm_window(C);
1895 /* operator was part of macro */
1897 /* give the mother macro to the handler */
1898 handler->op= op->opm;
1899 /* mother macro opm becomes the macro element */
1900 handler->op->opm= op;
1905 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
1906 handler->op_region= CTX_wm_region(C);
1908 BLI_addhead(&win->modalhandlers, handler);
1913 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1915 wmEventHandler *handler;
1918 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1922 /* only allow same keymap once */
1923 for(handler= handlers->first; handler; handler= handler->next)
1924 if(handler->keymap==keymap)
1927 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1928 BLI_addtail(handlers, handler);
1929 handler->keymap= keymap;
1934 /* priorities not implemented yet, for time being just insert in begin of list */
1935 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
1937 wmEventHandler *handler;
1939 WM_event_remove_keymap_handler(handlers, keymap);
1941 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1942 BLI_addhead(handlers, handler);
1943 handler->keymap= keymap;
1948 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1950 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1953 handler->bblocal= bblocal;
1954 handler->bbwin= bbwin;
1959 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1961 wmEventHandler *handler;
1963 for(handler= handlers->first; handler; handler= handler->next) {
1964 if(handler->keymap==keymap) {
1965 BLI_remlink(handlers, handler);
1966 wm_event_free_handler(handler);
1972 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1974 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1975 handler->ui_handle= func;
1976 handler->ui_remove= remove;
1977 handler->ui_userdata= userdata;
1978 handler->ui_area= (C)? CTX_wm_area(C): NULL;
1979 handler->ui_region= (C)? CTX_wm_region(C): NULL;
1980 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1982 BLI_addhead(handlers, handler);
1987 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1989 wmEventHandler *handler;
1991 for(handler= handlers->first; handler; handler= handler->next) {
1992 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1993 BLI_remlink(handlers, handler);
1994 wm_event_free_handler(handler);
2000 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
2002 wmEventHandler *handler;
2004 /* only allow same dropbox once */
2005 for(handler= handlers->first; handler; handler= handler->next)
2006 if(handler->dropboxes==dropboxes)
2009 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
2011 /* dropbox stored static, no free or copy */
2012 handler->dropboxes= dropboxes;
2013 BLI_addhead(handlers, handler);
2018 /* XXX solution works, still better check the real cause (ton) */
2019 void WM_event_remove_area_handler(ListBase *handlers, void *area)
2021 wmEventHandler *handler, *nexthandler;
2023 for(handler = handlers->first; handler; handler= nexthandler) {
2024 nexthandler = handler->next;
2025 if (handler->type != WM_HANDLER_FILESELECT) {
2026 if (handler->ui_area == area) {
2027 BLI_remlink(handlers, handler);
2028 wm_event_free_handler(handler);
2034 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
2036 BLI_remlink(handlers, handler);
2037 wm_event_free_handler(handler);
2040 void WM_event_add_mousemove(bContext *C)
2042 wmWindow *window= CTX_wm_window(C);
2044 window->addmousemove= 1;
2047 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
2048 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
2050 /* user preset or keymap? dunno... */
2052 int tweak_modal= (U.flag & USER_RELEASECONFIRM)==0;
2054 switch(tweak_event) {
2058 if(evt->val==tweak_modal)
2061 /* this case is when modal callcback didnt get started with a tweak */
2068 /* ********************* ghost stuff *************** */
2070 static int convert_key(GHOST_TKey key)
2072 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
2073 return (AKEY + ((int) key - GHOST_kKeyA));
2074 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
2075 return (ZEROKEY + ((int) key - GHOST_kKey0));
2076 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
2077 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
2078 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF19) {
2079 return (F1KEY + ((int) key - GHOST_kKeyF1));
2082 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
2083 case GHOST_kKeyTab: return TABKEY;
2084 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
2085 case GHOST_kKeyClear: return 0;
2086 case GHOST_kKeyEnter: return RETKEY;
2088 case GHOST_kKeyEsc: return ESCKEY;
2089 case GHOST_kKeySpace: return SPACEKEY;
2090 case GHOST_kKeyQuote: return QUOTEKEY;
2091 case GHOST_kKeyComma: return COMMAKEY;
2092 case GHOST_kKeyMinus: return MINUSKEY;
2093 case GHOST_kKeyPeriod: return PERIODKEY;
2094 case GHOST_kKeySlash: return SLASHKEY;
2096 case GHOST_kKeySemicolon: return SEMICOLONKEY;
2097 case GHOST_kKeyEqual: return EQUALKEY;
2099 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
2100 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
2101 case GHOST_kKeyBackslash: return BACKSLASHKEY;
2102 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
2104 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
2105 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
2106 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
2107 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
2108 case GHOST_kKeyOS: return OSKEY;
2109 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
2110 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
2112 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
2113 case GHOST_kKeyNumLock: return 0;
2114 case GHOST_kKeyScrollLock: return 0;
2116 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
2117 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
2118 case GHOST_kKeyUpArrow: return UPARROWKEY;
2119 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
2121 case GHOST_kKeyPrintScreen: return 0;
2122 case GHOST_kKeyPause: return PAUSEKEY;
2124 case GHOST_kKeyInsert: return INSERTKEY;
2125 case GHOST_kKeyDelete: return DELKEY;
2126 case GHOST_kKeyHome: return HOMEKEY;
2127 case GHOST_kKeyEnd: return ENDKEY;
2128 case GHOST_kKeyUpPage: return PAGEUPKEY;
2129 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
2131 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
2132 case GHOST_kKeyNumpadEnter: return PADENTER;
2133 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
2134 case GHOST_kKeyNumpadMinus: return PADMINUS;
2135 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
2136 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
2138 case GHOST_kKeyGrLess: return GRLESSKEY;
2141 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
2146 /* adds customdata to event */
2147 static void update_tablet_data(wmWindow *win, wmEvent *event)
2149 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2151 /* if there's tablet data from an active tablet device then add it */
2152 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2153 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2155 wmtab->Active = (int)td->Active;
2156 wmtab->Pressure = td->Pressure;
2157 wmtab->Xtilt = td->Xtilt;
2158 wmtab->Ytilt = td->Ytilt;
2160 event->custom= EVT_DATA_TABLET;
2161 event->customdata= wmtab;
2162 event->customdatafree= 1;
2166 /* imperfect but probably usable... draw/enable drags to other windows */
2167 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2169 short mx= evt->x, my= evt->y;
2171 if(wm->windows.first== wm->windows.last)
2174 /* top window bar... */
2175 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
2177 wmEventHandler *handler;
2179 /* let's skip windows having modal handlers now */
2180 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2181 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2182 if(handler->ui_handle || handler->op)
2185 /* to desktop space */
2189 /* check other windows to see if it has mouse inside */
2190 for(owin= wm->windows.first; owin; owin= owin->next) {
2193 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2194 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2195 evt->x= mx-owin->posx;
2196 evt->y= my-owin->posy;
2206 /* windows store own event queues, no bContext here */
2207 /* time is in 1000s of seconds, from ghost */
2208 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int UNUSED(time), void *customdata)
2211 wmEvent event, *evt= win->eventstate;
2213 /* initialize and copy state (only mouse x y and modifiers) */
2218 case GHOST_kEventCursorMove: {
2220 GHOST_TEventCursorData *cd= customdata;
2221 wmEvent *lastevent= win->queue.last;
2223 #if defined(__APPLE__) && defined(GHOST_COCOA)
2224 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2230 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2232 evt->y= (win->sizey-1) - cy;
2238 event.type= MOUSEMOVE;
2240 /* some painting operators want accurate mouse events, they can
2241 handle inbetween mouse move moves, others can happily ignore
2242 them for better performance */
2243 if(lastevent && lastevent->type == MOUSEMOVE)
2244 lastevent->type = INBETWEEN_MOUSEMOVE;
2246 update_tablet_data(win, &event);
2247 wm_event_add(win, &event);
2249 /* also add to other window if event is there, this makes overdraws disappear nicely */
2250 /* it remaps mousecoord to other window in event */
2251 owin= wm_event_cursor_other_windows(wm, win, &event);
2253 wmEvent oevent= *(owin->eventstate);
2255 oevent.x=owin->eventstate->x= event.x;
2256 oevent.y=owin->eventstate->y= event.y;
2257 oevent.type= MOUSEMOVE;
2259 update_tablet_data(owin, &oevent);
2260 wm_event_add(owin, &oevent);
2266 case GHOST_kEventTrackpad: {
2267 GHOST_TEventTrackpadData * pd = customdata;
2268 switch (pd->subtype) {
2269 case GHOST_kTrackpadEventMagnify:
2270 event.type = MOUSEZOOM;
2272 case GHOST_kTrackpadEventRotate:
2273 event.type = MOUSEROTATE;
2275 case GHOST_kTrackpadEventScroll:
2277 event.type= MOUSEPAN;
2280 #if defined(__APPLE__) && defined(GHOST_COCOA)
2281 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2282 event.x= evt->x = pd->x;
2283 event.y = evt->y = pd->y;
2287 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2288 event.x= evt->x= cx;
2289 event.y= evt->y= (win->sizey-1) - cy;
2292 // Use prevx/prevy so we can calculate the delta later
2293 event.prevx= event.x - pd->deltaX;
2294 event.prevy= event.y - pd->deltaY;
2296 update_tablet_data(win, &event);
2297 wm_event_add(win, &event);
2301 case GHOST_kEventButtonDown:
2302 case GHOST_kEventButtonUp: {
2303 GHOST_TEventButtonData *bd= customdata;
2304 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 */
2306 if (bd->button == GHOST_kButtonMaskLeft)
2307 event.type= LEFTMOUSE;
2308 else if (bd->button == GHOST_kButtonMaskRight)
2309 event.type= RIGHTMOUSE;
2310 else if (bd->button == GHOST_kButtonMaskButton4)
2311 event.type= BUTTON4MOUSE;
2312 else if (bd->button == GHOST_kButtonMaskButton5)
2313 event.type= BUTTON5MOUSE;
2315 event.type= MIDDLEMOUSE;
2317 /* add to other window if event is there (not to both!) */
2318 owin= wm_event_cursor_other_windows(wm, win, &event);
2320 wmEvent oevent= *(owin->eventstate);
2324 oevent.type= event.type;
2325 oevent.val= event.val;
2327 update_tablet_data(owin, &oevent);
2328 wm_event_add(owin, &oevent);
2331 update_tablet_data(win, &event);
2332 wm_event_add(win, &event);
2338 case GHOST_kEventKeyDown:
2339 case GHOST_kEventKeyUp: {
2340 GHOST_TEventKeyData *kd= customdata;
2341 event.type= convert_key(kd->key);
2342 event.ascii= kd->ascii;
2343 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2345 /* exclude arrow keys, esc, etc from text input */
2346 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2350 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2351 event.shift= evt->shift= (event.val==KM_PRESS);
2352 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2353 event.shift= evt->shift = 3; // define?
2355 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2356 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2357 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2358 event.ctrl= evt->ctrl = 3; // define?
2360 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2361 event.alt= evt->alt= (event.val==KM_PRESS);
2362 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2363 event.alt= evt->alt = 3; // define?
2365 else if (event.type==OSKEY) {
2366 event.oskey= evt->oskey= (event.val==KM_PRESS);
2367 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2368 event.oskey= evt->oskey = 3; // define?
2371 if(event.val==KM_PRESS && event.keymodifier==0)
2372 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2373 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2374 event.keymodifier= evt->keymodifier= 0;
2377 /* this case happens on some systems that on holding a key pressed,
2378 generate press events without release, we still want to keep the
2379 modifier in win->eventstate, but for the press event of the same
2380 key we don't want the key modifier */
2381 if(event.keymodifier == event.type)
2382 event.keymodifier= 0;
2384 /* if test_break set, it catches this. XXX Keep global for now? */
2385 if(event.type==ESCKEY)
2388 wm_event_add(win, &event);
2393 case GHOST_kEventWheel: {
2394 GHOST_TEventWheelData* wheelData = customdata;
2396 if (wheelData->z > 0)
2397 event.type= WHEELUPMOUSE;
2399 event.type= WHEELDOWNMOUSE;
2401 event.val= KM_PRESS;
2402 wm_event_add(win, &event);
2406 case GHOST_kEventTimer: {
2408 event.custom= EVT_DATA_TIMER;
2409 event.customdata= customdata;
2410 wm_event_add(win, &event);
2415 case GHOST_kEventUnknown:
2416 case GHOST_kNumEventTypes:
2419 case GHOST_kEventWindowDeactivate: {
2420 event.type= WINDEACTIVATE;
2421 wm_event_add(win, &event);