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);
518 static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
520 wmOperator *op= MEM_callocN(sizeof(wmOperator), ot->idname); /* XXX operatortype names are static still. for debug */
522 /* XXX adding new operator could be function, only happens here now */
524 BLI_strncpy(op->idname, ot->idname, OP_MAX_TYPENAME);
526 /* initialize properties, either copy or create */
527 op->ptr= MEM_callocN(sizeof(PointerRNA), "wmOperatorPtrRNA");
528 if(properties && properties->data) {
529 op->properties= IDP_CopyProperty(properties->data);
532 IDPropertyTemplate val = {0};
533 op->properties= IDP_New(IDP_GROUP, val, "wmOperatorProperties");
535 RNA_pointer_create(&wm->id, ot->srna, op->properties, op->ptr);
537 /* initialize error reports */
539 op->reports= reports; /* must be initialized already */
542 op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
543 BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
546 /* recursive filling of operator macro list */
547 if(ot->macro.first) {
548 static wmOperator *motherop= NULL;
549 wmOperatorTypeMacro *otmacro;
552 /* ensure all ops are in execution order in 1 list */
559 /* if properties exist, it will contain everything needed */
561 otmacro= ot->macro.first;
563 RNA_STRUCT_BEGIN(properties, prop) {
568 /* skip invalid properties */
569 if (strcmp(RNA_property_identifier(prop), otmacro->idname) == 0)
571 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
572 PointerRNA someptr = RNA_property_pointer_get(properties, prop);
573 wmOperator *opm= wm_operator_create(wm, otm, &someptr, NULL);
575 IDP_ReplaceGroupInGroup(opm->properties, otmacro->properties);
577 BLI_addtail(&motherop->macro, opm);
578 opm->opm= motherop; /* pointer to mom, for modal() */
580 otmacro= otmacro->next;
585 for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
586 wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
587 wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
589 BLI_addtail(&motherop->macro, opm);
590 opm->opm= motherop; /* pointer to mom, for modal() */
598 WM_operator_properties_sanitize(op->ptr, 0);
603 static void wm_region_mouse_co(bContext *C, wmEvent *event)
605 ARegion *ar= CTX_wm_region(C);
607 /* compatibility convention */
608 event->mval[0]= event->x - ar->winrct.xmin;
609 event->mval[1]= event->y - ar->winrct.ymin;
613 int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports, short poll_only)
615 wmWindowManager *wm= CTX_wm_manager(C);
616 int retval= OPERATOR_PASS_THROUGH;
618 /* this is done because complicated setup is done to call this function that is better not duplicated */
620 return WM_operator_poll(C, ot);
622 if(WM_operator_poll(C, ot)) {
623 wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
625 if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
626 printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname);
628 if(op->type->invoke && event) {
629 wm_region_mouse_co(C, event);
631 if(op->type->flag & OPTYPE_UNDO)
634 retval= op->type->invoke(C, op, event);
636 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
639 else if(op->type->exec) {
640 if(op->type->flag & OPTYPE_UNDO)
643 retval= op->type->exec(C, op);
645 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
649 printf("invalid operator call %s\n", ot->idname); /* debug, important to leave a while, should never happen */
651 /* Note, if the report is given as an argument then assume the caller will deal with displaying them
652 * currently python only uses this */
653 if (!(retval & OPERATOR_HANDLED) && retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED))
654 /* only show the report if the report list was not given in the function */
655 wm_operator_reports(C, op, retval, (reports==NULL));
658 if(retval & OPERATOR_HANDLED)
659 ; /* do nothing, wm_operator_exec() has been called somewhere */
660 else if(retval & OPERATOR_FINISHED) {
661 wm_operator_finished(C, op, 0);
663 else if(retval & OPERATOR_RUNNING_MODAL) {
664 /* grab cursor during blocking modal ops (X11)
665 * Also check for macro
667 if(ot->flag & OPTYPE_BLOCKING || (op->opm && op->opm->type->flag & OPTYPE_BLOCKING)) {
668 int bounds[4] = {-1,-1,-1,-1};
672 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
674 wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->flag & OP_GRAB_POINTER) || (ot->flag & OPTYPE_GRAB_POINTER));
678 ARegion *ar= CTX_wm_region(C);
680 bounds[0]= ar->winrct.xmin;
681 bounds[1]= ar->winrct.ymax;
682 bounds[2]= ar->winrct.xmax;
683 bounds[3]= ar->winrct.ymin;
687 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
690 /* cancel UI handlers, typically tooltips that can hang around
691 while dragging the view or worse, that stay there permanently
692 after the modal operator has swallowed all events and passed
693 none to the UI handler */
694 wm_handler_ui_cancel(C);
697 WM_operator_free(op);
703 /* WM_operator_name_call is the main accessor function
704 * this is for python to access since its done the operator lookup
706 * invokes operator in context */
707 static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports, short context, short poll_only)
709 wmWindow *window= CTX_wm_window(C);
714 CTX_wm_operator_poll_msg_set(C, NULL);
719 case WM_OP_INVOKE_DEFAULT:
720 case WM_OP_INVOKE_REGION_WIN:
721 case WM_OP_INVOKE_AREA:
722 case WM_OP_INVOKE_SCREEN:
723 /* window is needed for invoke, cancel operator */
727 event= window->eventstate;
735 case WM_OP_EXEC_REGION_WIN:
736 case WM_OP_INVOKE_REGION_WIN:
737 case WM_OP_EXEC_REGION_CHANNELS:
738 case WM_OP_INVOKE_REGION_CHANNELS:
739 case WM_OP_EXEC_REGION_PREVIEW:
740 case WM_OP_INVOKE_REGION_PREVIEW:
742 /* forces operator to go to the region window/channels/preview, for header menus
743 * but we stay in the same region if we are already in one
745 ARegion *ar= CTX_wm_region(C);
746 ScrArea *area= CTX_wm_area(C);
747 int type = RGN_TYPE_WINDOW;
750 case WM_OP_EXEC_REGION_CHANNELS:
751 case WM_OP_INVOKE_REGION_CHANNELS:
752 type = RGN_TYPE_CHANNELS;
755 case WM_OP_EXEC_REGION_PREVIEW:
756 case WM_OP_INVOKE_REGION_PREVIEW:
757 type = RGN_TYPE_PREVIEW;
760 case WM_OP_EXEC_REGION_WIN:
761 case WM_OP_INVOKE_REGION_WIN:
763 type = RGN_TYPE_WINDOW;
767 if(!(ar && ar->regiontype == type) && area) {
768 ARegion *ar1= BKE_area_find_region_type(area, type);
770 CTX_wm_region_set(C, ar1);
773 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
775 /* set region back */
776 CTX_wm_region_set(C, ar);
780 case WM_OP_EXEC_AREA:
781 case WM_OP_INVOKE_AREA:
783 /* remove region from context */
784 ARegion *ar= CTX_wm_region(C);
786 CTX_wm_region_set(C, NULL);
787 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
788 CTX_wm_region_set(C, ar);
792 case WM_OP_EXEC_SCREEN:
793 case WM_OP_INVOKE_SCREEN:
795 /* remove region + area from context */
796 ARegion *ar= CTX_wm_region(C);
797 ScrArea *area= CTX_wm_area(C);
799 CTX_wm_region_set(C, NULL);
800 CTX_wm_area_set(C, NULL);
801 retval= wm_operator_invoke(C, ot, event, properties, reports, poll_only);
802 CTX_wm_region_set(C, ar);
803 CTX_wm_area_set(C, area);
807 case WM_OP_EXEC_DEFAULT:
808 case WM_OP_INVOKE_DEFAULT:
809 return wm_operator_invoke(C, ot, event, properties, reports, poll_only);
817 /* invokes operator in context */
818 int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
820 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
822 return wm_operator_call_internal(C, ot, properties, NULL, context, FALSE);
827 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
828 - wmOperatorType is used instead of operator name since python already has the operator type
829 - poll() must be called by python before this runs.
830 - reports can be passed to this function (so python can report them as exceptions)
832 int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
834 int retval= OPERATOR_CANCELLED;
838 op= wm_operator_create(wm, ot, properties, reports);
840 if (op->type->exec) {
841 if(op->type->flag & OPTYPE_UNDO)
844 retval= op->type->exec(C, op);
846 if(op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
850 printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
853 retval= wm_operator_call_internal(C, ot, properties, reports, context, FALSE);
855 /* keep the reports around if needed later */
856 if ( (retval & OPERATOR_RUNNING_MODAL) ||
857 ((retval & OPERATOR_FINISHED) && wm_operator_register_check(CTX_wm_manager(C), ot))
859 reports->flag |= RPT_FREE; /* let blender manage freeing */
866 /* ********************* handlers *************** */
868 /* future extra customadata free? */
869 void wm_event_free_handler(wmEventHandler *handler)
874 /* only set context when area/region is part of screen */
875 static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
877 bScreen *screen= CTX_wm_screen(C);
879 if(screen && handler->op) {
880 if(handler->op_area==NULL)
881 CTX_wm_area_set(C, NULL);
885 for(sa= screen->areabase.first; sa; sa= sa->next)
886 if(sa==handler->op_area)
889 /* when changing screen layouts with running modal handlers (like render display), this
890 is not an error to print */
891 if(handler->op==NULL)
892 printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
896 CTX_wm_area_set(C, sa);
897 for(ar= sa->regionbase.first; ar; ar= ar->next)
898 if(ar==handler->op_region)
900 /* XXX no warning print here, after full-area and back regions are remade */
902 CTX_wm_region_set(C, ar);
908 /* called on exit or remove area, only here call cancel callback */
909 void WM_event_remove_handlers(bContext *C, ListBase *handlers)
911 wmEventHandler *handler;
912 wmWindowManager *wm= CTX_wm_manager(C);
914 /* C is zero on freeing database, modal handlers then already were freed */
915 while((handler=handlers->first)) {
916 BLI_remlink(handlers, handler);
919 if(handler->op->type->cancel) {
920 ScrArea *area= CTX_wm_area(C);
921 ARegion *region= CTX_wm_region(C);
923 wm_handler_op_context(C, handler);
925 if(handler->op->type->flag & OPTYPE_UNDO)
928 handler->op->type->cancel(C, handler->op);
930 if(handler->op->type->flag & OPTYPE_UNDO)
933 CTX_wm_area_set(C, area);
934 CTX_wm_region_set(C, region);
937 WM_cursor_ungrab(CTX_wm_window(C));
938 WM_operator_free(handler->op);
940 else if(handler->ui_remove) {
941 ScrArea *area= CTX_wm_area(C);
942 ARegion *region= CTX_wm_region(C);
943 ARegion *menu= CTX_wm_menu(C);
945 if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
946 if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
947 if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
949 handler->ui_remove(C, handler->ui_userdata);
951 CTX_wm_area_set(C, area);
952 CTX_wm_region_set(C, region);
953 CTX_wm_menu_set(C, menu);
956 wm_event_free_handler(handler);
960 /* do userdef mappings */
961 int WM_userdef_event_map(int kmitype)
965 if(U.flag & USER_LMOUSESELECT)
971 if(U.flag & USER_LMOUSESELECT)
977 if(U.uiflag & USER_WHEELZOOMDIR)
980 return WHEELDOWNMOUSE;
983 if(U.uiflag & USER_WHEELZOOMDIR)
984 return WHEELDOWNMOUSE;
989 if(U.flag & USER_LMOUSESELECT)
995 if(U.flag & USER_LMOUSESELECT)
1004 static void wm_eventemulation(wmEvent *event)
1006 static int mmb_emulated = 0; /* this should be in a data structure somwhere */
1008 /* middlemouse emulation */
1009 if(U.flag & USER_TWOBUTTONMOUSE) {
1010 if(event->type == LEFTMOUSE && (event->alt || mmb_emulated == KM_PRESS)) {
1011 event->type = MIDDLEMOUSE;
1013 mmb_emulated = event->val;
1018 /* rightmouse emulation */
1019 if(U.flag & USER_TWOBUTTONMOUSE) {
1020 if(event->type == LEFTMOUSE && (event->oskey || mmb_emulated == KM_PRESS)) {
1021 event->type = RIGHTMOUSE;
1023 mmb_emulated = event->val;
1028 /* numpad emulation */
1029 if(U.flag & USER_NONUMPAD) {
1030 switch(event->type) {
1031 case ZEROKEY: event->type = PAD0; break;
1032 case ONEKEY: event->type = PAD1; break;
1033 case TWOKEY: event->type = PAD2; break;
1034 case THREEKEY: event->type = PAD3; break;
1035 case FOURKEY: event->type = PAD4; break;
1036 case FIVEKEY: event->type = PAD5; break;
1037 case SIXKEY: event->type = PAD6; break;
1038 case SEVENKEY: event->type = PAD7; break;
1039 case EIGHTKEY: event->type = PAD8; break;
1040 case NINEKEY: event->type = PAD9; break;
1041 case MINUSKEY: event->type = PADMINUS; break;
1042 case EQUALKEY: event->type = PADPLUSKEY; break;
1043 case BACKSLASHKEY: event->type = PADSLASHKEY; break;
1048 static int wm_eventmatch(wmEvent *winevent, wmKeyMapItem *kmi)
1050 int kmitype= WM_userdef_event_map(kmi->type);
1052 if(kmi->flag & KMI_INACTIVE) return 0;
1054 /* the matching rules */
1055 if(kmitype==KM_TEXTINPUT)
1056 if(ISTEXTINPUT(winevent->type) && winevent->ascii) return 1;
1058 if(winevent->type!=kmitype) return 0;
1060 if(kmi->val!=KM_ANY)
1061 if(winevent->val!=kmi->val) return 0;
1063 /* modifiers also check bits, so it allows modifier order */
1064 if(kmi->shift!=KM_ANY)
1065 if(winevent->shift != kmi->shift && !(winevent->shift & kmi->shift)) return 0;
1066 if(kmi->ctrl!=KM_ANY)
1067 if(winevent->ctrl != kmi->ctrl && !(winevent->ctrl & kmi->ctrl)) return 0;
1068 if(kmi->alt!=KM_ANY)
1069 if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
1070 if(kmi->oskey!=KM_ANY)
1071 if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
1073 if(kmi->keymodifier)
1074 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1076 /* key modifiers always check when event has it */
1077 /* otherwise regular keypresses with keymodifier still work */
1078 if(winevent->keymodifier)
1079 if(ISTEXTINPUT(winevent->type))
1080 if(winevent->keymodifier!=kmi->keymodifier) return 0;
1085 static int wm_event_always_pass(wmEvent *event)
1087 /* some events we always pass on, to ensure proper communication */
1088 return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
1091 /* operator exists */
1092 static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *event)
1094 /* support for modal keymap in macros */
1098 if(op->type->modalkeymap) {
1099 wmKeyMap *keymap= WM_keymap_active(CTX_wm_manager(C), op->type->modalkeymap);
1102 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1103 if(wm_eventmatch(event, kmi)) {
1105 event->type= EVT_MODAL_MAP;
1106 event->val= kmi->propvalue;
1112 /* Warning: this function removes a modal handler, when finished */
1113 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
1115 int retval= OPERATOR_PASS_THROUGH;
1117 /* derived, modal or blocking operator */
1119 wmOperator *op= handler->op;
1120 wmOperatorType *ot= op->type;
1123 /* we set context to where modal handler came from */
1124 wmWindowManager *wm= CTX_wm_manager(C);
1125 ScrArea *area= CTX_wm_area(C);
1126 ARegion *region= CTX_wm_region(C);
1128 wm_handler_op_context(C, handler);
1129 wm_region_mouse_co(C, event);
1130 wm_event_modalkeymap(C, op, event);
1132 if(ot->flag & OPTYPE_UNDO)
1133 wm->op_undo_depth++;
1135 retval= ot->modal(C, op, event);
1137 if(ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1138 wm->op_undo_depth--;
1140 /* putting back screen context, reval can pass trough after modal failures! */
1141 if((retval & OPERATOR_PASS_THROUGH) || wm_event_always_pass(event)) {
1142 CTX_wm_area_set(C, area);
1143 CTX_wm_region_set(C, region);
1146 /* this special cases is for areas and regions that get removed */
1147 CTX_wm_area_set(C, NULL);
1148 CTX_wm_region_set(C, NULL);
1151 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED))
1152 wm_operator_reports(C, op, retval, 0);
1154 if(retval & OPERATOR_FINISHED) {
1155 wm_operator_finished(C, op, 0);
1158 else if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1159 WM_operator_free(op);
1163 /* remove modal handler, operator itself should have been cancelled and freed */
1164 if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
1165 WM_cursor_ungrab(CTX_wm_window(C));
1167 BLI_remlink(handlers, handler);
1168 wm_event_free_handler(handler);
1170 /* prevent silly errors from operator users */
1171 //retval &= ~OPERATOR_PASS_THROUGH;
1176 printf("wm_handler_operator_call error\n");
1179 wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
1182 retval= wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
1185 /* Finished and pass through flag as handled */
1186 if(retval == (OPERATOR_FINISHED|OPERATOR_PASS_THROUGH))
1187 return WM_HANDLER_HANDLED;
1189 /* Modal unhandled, break */
1190 if(retval == (OPERATOR_PASS_THROUGH|OPERATOR_RUNNING_MODAL))
1191 return (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1193 if(retval & OPERATOR_PASS_THROUGH)
1194 return WM_HANDLER_CONTINUE;
1196 return WM_HANDLER_BREAK;
1199 /* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
1200 static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
1202 wmWindowManager *wm= CTX_wm_manager(C);
1204 int action= WM_HANDLER_CONTINUE;
1206 if(event->type != EVT_FILESELECT)
1208 if(handler->op != (wmOperator *)event->customdata)
1211 switch(event->val) {
1212 case EVT_FILESELECT_OPEN:
1213 case EVT_FILESELECT_FULL_OPEN:
1217 /* sa can be null when window A is active, but mouse is over window B */
1218 /* in this case, open file select in original window A */
1219 if (handler->op_area == NULL) {
1220 bScreen *screen = CTX_wm_screen(C);
1221 sa = (ScrArea *)screen->areabase.first;
1223 sa = handler->op_area;
1225 if(event->val==EVT_FILESELECT_OPEN)
1226 ED_area_newspace(C, sa, SPACE_FILE);
1228 ED_screen_full_newspace(C, sa, SPACE_FILE); /* sets context */
1230 /* settings for filebrowser, sfile is not operator owner but sends events */
1231 sa = CTX_wm_area(C);
1232 sfile= (SpaceFile*)sa->spacedata.first;
1233 sfile->op= handler->op;
1235 ED_fileselect_set_params(sfile);
1237 action= WM_HANDLER_BREAK;
1241 case EVT_FILESELECT_EXEC:
1242 case EVT_FILESELECT_CANCEL:
1243 case EVT_FILESELECT_EXTERNAL_CANCEL:
1245 /* XXX validate area and region? */
1246 bScreen *screen= CTX_wm_screen(C);
1248 /* remlink now, for load file case before removing*/
1249 BLI_remlink(handlers, handler);
1251 if(event->val!=EVT_FILESELECT_EXTERNAL_CANCEL) {
1252 if(screen != handler->filescreen) {
1253 ED_screen_full_prevspace(C, CTX_wm_area(C));
1256 ED_area_prevspace(C, CTX_wm_area(C));
1260 wm_handler_op_context(C, handler);
1262 /* needed for uiPupMenuReports */
1264 if(event->val==EVT_FILESELECT_EXEC) {
1265 #if 0 // use REDALERT now
1267 /* a bit weak, might become arg for WM_event_fileselect? */
1268 /* XXX also extension code in image-save doesnt work for this yet */
1269 if (RNA_struct_find_property(handler->op->ptr, "check_existing") &&
1270 RNA_boolean_get(handler->op->ptr, "check_existing")) {
1271 char *path= RNA_string_get_alloc(handler->op->ptr, "filepath", NULL, 0);
1272 /* this gives ownership to pupmenu */
1273 uiPupMenuSaveOver(C, handler->op, (path)? path: "");
1282 if(handler->op->type->flag & OPTYPE_UNDO)
1283 wm->op_undo_depth++;
1285 retval= handler->op->type->exec(C, handler->op);
1287 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1288 if(handler->op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
1289 wm->op_undo_depth--;
1291 if (retval & OPERATOR_FINISHED)
1293 wm_operator_print(C, handler->op);
1295 /* XXX check this carefully, CTX_wm_manager(C) == wm is a bit hackish */
1296 if(CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
1297 if(handler->op->type->flag & OPTYPE_UNDO)
1298 ED_undo_push_op(C, handler->op);
1300 if(handler->op->reports->list.first) {
1302 /* FIXME, temp setting window, this is really bad!
1303 * only have because lib linking errors need to be seen by users :(
1304 * it can be removed without breaking anything but then no linking errors - campbell */
1305 wmWindow *win_prev= CTX_wm_window(C);
1307 CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first);
1309 handler->op->reports->printlevel = RPT_WARNING;
1310 uiPupMenuReports(C, handler->op->reports);
1312 /* XXX - copied from 'wm_operator_finished()' */
1313 /* add reports to the global list, otherwise they are not seen */
1314 addlisttolist(&CTX_wm_reports(C)->list, &handler->op->reports->list);
1316 CTX_wm_window_set(C, win_prev);
1319 WM_operator_free(handler->op);
1323 if(handler->op->type->cancel) {
1324 if(handler->op->type->flag & OPTYPE_UNDO)
1325 wm->op_undo_depth++;
1327 handler->op->type->cancel(C, handler->op);
1329 if(handler->op->type->flag & OPTYPE_UNDO)
1330 wm->op_undo_depth--;
1333 WM_operator_free(handler->op);
1336 CTX_wm_area_set(C, NULL);
1338 wm_event_free_handler(handler);
1340 action= WM_HANDLER_BREAK;
1348 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
1350 if(handler->bbwin) {
1351 if(handler->bblocal) {
1352 rcti rect= *handler->bblocal;
1353 BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
1355 if(BLI_in_rcti(&rect, event->x, event->y))
1357 else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
1363 if(BLI_in_rcti(handler->bbwin, event->x, event->y))
1365 else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
1374 static int wm_action_not_handled(int action)
1376 return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK|WM_HANDLER_MODAL);
1379 static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
1381 wmWindowManager *wm= CTX_wm_manager(C);
1382 wmEventHandler *handler, *nexthandler;
1383 int action= WM_HANDLER_CONTINUE;
1386 if(handlers==NULL) return action;
1388 /* modal handlers can get removed in this loop, we keep the loop this way */
1389 for(handler= handlers->first; handler; handler= nexthandler) {
1390 nexthandler= handler->next;
1392 /* optional boundbox */
1393 if(handler_boundbox_test(handler, event)) {
1394 /* in advance to avoid access to freed event on window close */
1395 always_pass= wm_event_always_pass(event);
1397 /* modal+blocking handler */
1398 if(handler->flag & WM_HANDLER_BLOCKING)
1399 action |= WM_HANDLER_BREAK;
1401 if(handler->keymap) {
1402 wmKeyMap *keymap= WM_keymap_active(wm, handler->keymap);
1405 if(!keymap->poll || keymap->poll(C)) {
1406 for(kmi= keymap->items.first; kmi; kmi= kmi->next) {
1407 if(wm_eventmatch(event, kmi)) {
1409 event->keymap_idname= kmi->idname; /* weak, but allows interactive callback to not use rawkey */
1411 action |= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
1412 if(action & WM_HANDLER_BREAK) /* not always_pass here, it denotes removed handler */
1418 else if(handler->ui_handle) {
1419 action |= wm_handler_ui_call(C, handler, event, always_pass);
1421 else if(handler->type==WM_HANDLER_FILESELECT) {
1422 /* screen context changes here */
1423 action |= wm_handler_fileselect_call(C, handlers, handler, event);
1425 else if(handler->dropboxes) {
1426 if(event->type==EVT_DROP) {
1427 wmDropBox *drop= handler->dropboxes->first;
1428 for(; drop; drop= drop->next) {
1429 /* other drop custom types allowed */
1430 if(event->custom==EVT_DATA_LISTBASE) {
1431 ListBase *lb= (ListBase *)event->customdata;
1433 for(drag= lb->first; drag; drag= drag->next) {
1434 if(drop->poll(C, drag, event)) {
1435 drop->copy(drag, drop);
1437 wm_operator_invoke(C, drop->ot, event, drop->ptr, NULL, FALSE);
1438 action |= WM_HANDLER_BREAK;
1446 /* modal, swallows all */
1447 action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
1450 if(action & WM_HANDLER_BREAK) {
1452 action &= ~WM_HANDLER_BREAK;
1459 if(CTX_wm_window(C)==NULL)
1463 /* test for CLICK event */
1464 if (wm_action_not_handled(action) && event->val == KM_RELEASE) {
1465 wmWindow *win = CTX_wm_window(C);
1467 if (win && win->eventstate->prevtype == event->type && win->eventstate->prevval == KM_PRESS) {
1468 /* test for double click first,
1469 * note1: this can be problematic because single click operators can get the
1470 * double click event but then with old mouse coords which is highly confusing,
1471 * so check for mouse moves too.
1472 * note2: the first click event will be handled but still used to create a
1473 * double click event if clicking again quickly.
1474 * If no double click events are found itwill fallback to a single click.
1475 * So a double click event can result in 2 successive single click calls
1476 * if its not handled by the keymap - campbell */
1477 if ( (ABS(event->x - win->eventstate->prevclickx)) <= 2 &&
1478 (ABS(event->y - win->eventstate->prevclicky)) <= 2 &&
1479 ((PIL_check_seconds_timer() - win->eventstate->prevclicktime) * 1000 < U.dbl_click_time)
1481 event->val = KM_DBL_CLICK;
1482 /* removed this because in cases where we're this is used as a single click
1483 * event, this will give old coords, since the distance is checked above, using new coords should be ok. */
1484 // event->x = win->eventstate->prevclickx;
1485 // event->y = win->eventstate->prevclicky;
1486 action |= wm_handlers_do(C, event, handlers);
1489 if (wm_action_not_handled(action)) {
1490 event->val = KM_CLICK;
1491 action |= wm_handlers_do(C, event, handlers);
1495 /* revert value if not handled */
1496 if (wm_action_not_handled(action)) {
1497 event->val = KM_RELEASE;
1505 static int wm_event_inside_i(wmEvent *event, rcti *rect)
1507 if(wm_event_always_pass(event))
1509 if(BLI_in_rcti(rect, event->x, event->y))
1511 if(event->type==MOUSEMOVE) {
1512 if( BLI_in_rcti(rect, event->prevx, event->prevy)) {
1520 static ScrArea *area_event_inside(bContext *C, int x, int y)
1522 bScreen *screen= CTX_wm_screen(C);
1526 for(sa= screen->areabase.first; sa; sa= sa->next)
1527 if(BLI_in_rcti(&sa->totrct, x, y))
1532 static ARegion *region_event_inside(bContext *C, int x, int y)
1534 bScreen *screen= CTX_wm_screen(C);
1535 ScrArea *area= CTX_wm_area(C);
1539 for(ar= area->regionbase.first; ar; ar= ar->next)
1540 if(BLI_in_rcti(&ar->winrct, x, y))
1545 static void wm_paintcursor_tag(bContext *C, wmPaintCursor *pc, ARegion *ar)
1548 for(; pc; pc= pc->next) {
1549 if(pc->poll == NULL || pc->poll(C)) {
1550 wmWindow *win= CTX_wm_window(C);
1551 win->screen->do_draw_paintcursor= 1;
1552 wm_tag_redraw_overlay(win, ar);
1558 /* called on mousemove, check updates for paintcursors */
1559 /* context was set on active area and region */
1560 static void wm_paintcursor_test(bContext *C, wmEvent *event)
1562 wmWindowManager *wm= CTX_wm_manager(C);
1564 if(wm->paintcursors.first) {
1565 ARegion *ar= CTX_wm_region(C);
1567 wm_paintcursor_tag(C, wm->paintcursors.first, ar);
1569 /* if previous position was not in current region, we have to set a temp new context */
1570 if(ar==NULL || !BLI_in_rcti(&ar->winrct, event->prevx, event->prevy)) {
1571 ScrArea *sa= CTX_wm_area(C);
1573 CTX_wm_area_set(C, area_event_inside(C, event->prevx, event->prevy));
1574 CTX_wm_region_set(C, region_event_inside(C, event->prevx, event->prevy));
1576 wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C));
1578 CTX_wm_area_set(C, sa);
1579 CTX_wm_region_set(C, ar);
1584 static void wm_event_drag_test(wmWindowManager *wm, wmWindow *win, wmEvent *event)
1586 if(wm->drags.first==NULL) return;
1588 if(event->type==MOUSEMOVE)
1589 win->screen->do_draw_drag= 1;
1590 else if(event->type==ESCKEY) {
1591 BLI_freelistN(&wm->drags);
1592 win->screen->do_draw_drag= 1;
1594 else if(event->type==LEFTMOUSE && event->val==KM_RELEASE) {
1595 event->type= EVT_DROP;
1597 /* create customdata, first free existing */
1598 if(event->customdata) {
1599 if(event->customdatafree)
1600 MEM_freeN(event->customdata);
1603 event->custom= EVT_DATA_LISTBASE;
1604 event->customdata= &wm->drags;
1605 event->customdatafree= 1;
1607 /* clear drop icon */
1608 win->screen->do_draw_drag= 1;
1610 /* restore cursor (disabled, see wm_dragdrop.c) */
1611 // WM_cursor_restore(win);
1614 /* overlap fails otherwise */
1615 if(win->screen->do_draw_drag)
1616 if(win->drawmethod == USER_DRAW_OVERLAP)
1617 win->screen->do_draw= 1;
1621 /* called in main loop */
1622 /* goes over entire hierarchy: events -> window -> screen -> area -> region */
1623 void wm_event_do_handlers(bContext *C)
1625 wmWindowManager *wm= CTX_wm_manager(C);
1628 for(win= wm->windows.first; win; win= win->next) {
1631 if( win->screen==NULL )
1632 wm_event_free_all(win);
1634 Scene* scene = win->screen->scene;
1637 int playing = sound_scene_playing(win->screen->scene);
1640 CTX_wm_window_set(C, win);
1641 CTX_wm_screen_set(C, win->screen);
1642 CTX_data_scene_set(C, scene);
1644 if(((playing == 1) && (!win->screen->animtimer)) || ((playing == 0) && (win->screen->animtimer))){
1645 ED_screen_animation_play(C, -1, 1);
1649 int ncfra = sound_sync_scene(scene) * FPS + 0.5;
1650 if(ncfra != scene->r.cfra) {
1651 scene->r.cfra = ncfra;
1652 ED_update_for_newframe(CTX_data_main(C), scene, win->screen, 1);
1653 WM_event_add_notifier(C, NC_WINDOW, NULL);
1657 CTX_data_scene_set(C, NULL);
1658 CTX_wm_screen_set(C, NULL);
1659 CTX_wm_window_set(C, NULL);
1664 while( (event= win->queue.first) ) {
1665 int action = WM_HANDLER_CONTINUE;
1667 if((G.f & G_DEBUG) && event && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))
1668 printf("pass on evt %d val %d\n", event->type, event->val);
1670 wm_eventemulation(event);
1672 CTX_wm_window_set(C, win);
1674 /* we let modal handlers get active area/region, also wm_paintcursor_test needs it */
1675 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1676 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1678 /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
1679 wm_window_make_drawable(C, win);
1681 /* first we do priority handlers, modal + some limited keymaps */
1682 action |= wm_handlers_do(C, event, &win->modalhandlers);
1685 if(CTX_wm_window(C)==NULL)
1688 /* check dragging, creates new event or frees, adds draw tag */
1689 wm_event_drag_test(wm, win, event);
1691 /* builtin tweak, if action is break it removes tweak */
1692 wm_tweakevent_test(C, event, action);
1694 if((action & WM_HANDLER_BREAK) == 0) {
1699 /* XXX to solve, here screen handlers? */
1700 if(event->type==MOUSEMOVE) {
1701 /* state variables in screen, cursors */
1702 ED_screen_set_subwinactive(win, event);
1703 /* for regions having custom cursors */
1704 wm_paintcursor_test(C, event);
1707 for(sa= win->screen->areabase.first; sa; sa= sa->next) {
1708 if(wm_event_inside_i(event, &sa->totrct)) {
1709 CTX_wm_area_set(C, sa);
1711 if((action & WM_HANDLER_BREAK) == 0) {
1712 for(ar=sa->regionbase.first; ar; ar= ar->next) {
1713 if(wm_event_inside_i(event, &ar->winrct)) {
1714 CTX_wm_region_set(C, ar);
1716 /* does polls for drop regions and checks uibuts */
1717 /* need to be here to make sure region context is true */
1718 if(ELEM(event->type, MOUSEMOVE, EVT_DROP)) {
1719 wm_region_mouse_co(C, event);
1720 wm_drags_check_ops(C, event);
1723 action |= wm_handlers_do(C, event, &ar->handlers);
1725 /* fileread case (python), [#29489] */
1726 if(CTX_wm_window(C)==NULL)
1729 doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
1731 if(action & WM_HANDLER_BREAK)
1737 CTX_wm_region_set(C, NULL);
1739 if((action & WM_HANDLER_BREAK) == 0)
1740 action |= wm_handlers_do(C, event, &sa->handlers);
1742 CTX_wm_area_set(C, NULL);
1744 /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
1748 if((action & WM_HANDLER_BREAK) == 0) {
1749 /* also some non-modal handlers need active area/region */
1750 CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
1751 CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
1753 action |= wm_handlers_do(C, event, &win->handlers);
1756 if(CTX_wm_window(C)==NULL)
1760 /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad?
1761 doing it on ghost queue gives errors when mousemoves go over area borders */
1762 if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
1763 win->eventstate->prevx= event->x;
1764 win->eventstate->prevy= event->y;
1768 /* store last event for this window */
1769 /* mousemove and timer events don't overwrite last type */
1770 if (event->type != MOUSEMOVE && !ISTIMER(event->type)) {
1771 if (wm_action_not_handled(action)) {
1772 if (win->eventstate->prevtype == event->type) {
1773 /* set click time on first click (press -> release) */
1774 if (win->eventstate->prevval == KM_PRESS && event->val == KM_RELEASE) {
1775 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1776 win->eventstate->prevclickx = event->x;
1777 win->eventstate->prevclicky = event->y;
1780 /* reset click time if event type not the same */
1781 win->eventstate->prevclicktime = 0;
1784 win->eventstate->prevval = event->val;
1785 win->eventstate->prevtype = event->type;
1786 } else if (event->val == KM_CLICK) { /* keep click for double click later */
1787 win->eventstate->prevtype = event->type;
1788 win->eventstate->prevval = event->val;
1789 win->eventstate->prevclicktime = PIL_check_seconds_timer();
1790 win->eventstate->prevclickx = event->x;
1791 win->eventstate->prevclicky = event->y;
1792 } else { /* reset if not */
1793 win->eventstate->prevtype = -1;
1794 win->eventstate->prevval = 0;
1795 win->eventstate->prevclicktime = 0;
1799 /* unlink and free here, blender-quit then frees all */
1800 BLI_remlink(&win->queue, event);
1801 wm_event_free(event);
1805 /* only add mousemove when queue was read entirely */
1806 if(win->addmousemove && win->eventstate) {
1807 wmEvent tevent= *(win->eventstate);
1808 tevent.type= MOUSEMOVE;
1809 tevent.prevx= tevent.x;
1810 tevent.prevy= tevent.y;
1811 wm_event_add(win, &tevent);
1812 win->addmousemove= 0;
1815 CTX_wm_window_set(C, NULL);
1819 /* ********** filesector handling ************ */
1821 void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
1823 /* add to all windows! */
1826 for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
1827 wmEvent event= *win->eventstate;
1829 event.type= EVT_FILESELECT;
1830 event.val= eventval;
1831 event.customdata= ophandle; // only as void pointer type check
1833 wm_event_add(win, &event);
1837 /* operator is supposed to have a filled "path" property */
1838 /* optional property: filetype (XXX enum?) */
1840 /* Idea is to keep a handler alive on window queue, owning the operator.
1841 The filewindow can send event to make it execute, thus ensuring
1842 executing happens outside of lower level queues, with UI refreshed.
1843 Should also allow multiwin solutions */
1845 void WM_event_add_fileselect(bContext *C, wmOperator *op)
1847 wmEventHandler *handler;
1848 wmWindow *win= CTX_wm_window(C);
1849 int full= 1; // XXX preset?
1851 /* only allow file selector open per window bug [#23553] */
1852 for(handler= win->modalhandlers.first; handler; handler=handler->next) {
1853 if(handler->type == WM_HANDLER_FILESELECT)
1857 handler = MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
1859 handler->type= WM_HANDLER_FILESELECT;
1861 handler->op_area= CTX_wm_area(C);
1862 handler->op_region= CTX_wm_region(C);
1863 handler->filescreen= CTX_wm_screen(C);
1865 BLI_addhead(&win->modalhandlers, handler);
1867 /* check props once before invoking if check is available
1868 * ensures initial properties are valid */
1869 if(op->type->check) {
1870 op->type->check(C, op); /* ignore return value */
1873 WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
1876 /* lets not expose struct outside wm? */
1877 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
1879 handler->flag= flag;
1882 wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
1884 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
1885 wmWindow *win= CTX_wm_window(C);
1887 /* operator was part of macro */
1889 /* give the mother macro to the handler */
1890 handler->op= op->opm;
1891 /* mother macro opm becomes the macro element */
1892 handler->op->opm= op;
1897 handler->op_area= CTX_wm_area(C); /* means frozen screen context for modal handlers! */
1898 handler->op_region= CTX_wm_region(C);
1900 BLI_addhead(&win->modalhandlers, handler);
1905 wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1907 wmEventHandler *handler;
1910 printf("WM_event_add_keymap_handler called with NULL keymap\n");
1914 /* only allow same keymap once */
1915 for(handler= handlers->first; handler; handler= handler->next)
1916 if(handler->keymap==keymap)
1919 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1920 BLI_addtail(handlers, handler);
1921 handler->keymap= keymap;
1926 /* priorities not implemented yet, for time being just insert in begin of list */
1927 wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int UNUSED(priority))
1929 wmEventHandler *handler;
1931 WM_event_remove_keymap_handler(handlers, keymap);
1933 handler= MEM_callocN(sizeof(wmEventHandler), "event keymap handler");
1934 BLI_addhead(handlers, handler);
1935 handler->keymap= keymap;
1940 wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
1942 wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
1945 handler->bblocal= bblocal;
1946 handler->bbwin= bbwin;
1951 void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
1953 wmEventHandler *handler;
1955 for(handler= handlers->first; handler; handler= handler->next) {
1956 if(handler->keymap==keymap) {
1957 BLI_remlink(handlers, handler);
1958 wm_event_free_handler(handler);
1964 wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1966 wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
1967 handler->ui_handle= func;
1968 handler->ui_remove= remove;
1969 handler->ui_userdata= userdata;
1970 handler->ui_area= (C)? CTX_wm_area(C): NULL;
1971 handler->ui_region= (C)? CTX_wm_region(C): NULL;
1972 handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
1974 BLI_addhead(handlers, handler);
1979 void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
1981 wmEventHandler *handler;
1983 for(handler= handlers->first; handler; handler= handler->next) {
1984 if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
1985 BLI_remlink(handlers, handler);
1986 wm_event_free_handler(handler);
1992 wmEventHandler *WM_event_add_dropbox_handler(ListBase *handlers, ListBase *dropboxes)
1994 wmEventHandler *handler;
1996 /* only allow same dropbox once */
1997 for(handler= handlers->first; handler; handler= handler->next)
1998 if(handler->dropboxes==dropboxes)
2001 handler= MEM_callocN(sizeof(wmEventHandler), "dropbox handler");
2003 /* dropbox stored static, no free or copy */
2004 handler->dropboxes= dropboxes;
2005 BLI_addhead(handlers, handler);
2010 /* XXX solution works, still better check the real cause (ton) */
2011 void WM_event_remove_area_handler(ListBase *handlers, void *area)
2013 wmEventHandler *handler, *nexthandler;
2015 for(handler = handlers->first; handler; handler= nexthandler) {
2016 nexthandler = handler->next;
2017 if (handler->type != WM_HANDLER_FILESELECT) {
2018 if (handler->ui_area == area) {
2019 BLI_remlink(handlers, handler);
2020 wm_event_free_handler(handler);
2026 void WM_event_remove_handler(ListBase *handlers, wmEventHandler *handler)
2028 BLI_remlink(handlers, handler);
2029 wm_event_free_handler(handler);
2032 void WM_event_add_mousemove(bContext *C)
2034 wmWindow *window= CTX_wm_window(C);
2036 window->addmousemove= 1;
2039 /* for modal callbacks, check configuration for how to interpret exit with tweaks */
2040 int WM_modal_tweak_exit(wmEvent *evt, int tweak_event)
2042 /* user preset or keymap? dunno... */
2044 int tweak_modal= (U.flag & USER_RELEASECONFIRM)==0;
2046 switch(tweak_event) {
2050 if(evt->val==tweak_modal)
2053 /* this case is when modal callcback didnt get started with a tweak */
2060 /* ********************* ghost stuff *************** */
2062 static int convert_key(GHOST_TKey key)
2064 if (key>=GHOST_kKeyA && key<=GHOST_kKeyZ) {
2065 return (AKEY + ((int) key - GHOST_kKeyA));
2066 } else if (key>=GHOST_kKey0 && key<=GHOST_kKey9) {
2067 return (ZEROKEY + ((int) key - GHOST_kKey0));
2068 } else if (key>=GHOST_kKeyNumpad0 && key<=GHOST_kKeyNumpad9) {
2069 return (PAD0 + ((int) key - GHOST_kKeyNumpad0));
2070 } else if (key>=GHOST_kKeyF1 && key<=GHOST_kKeyF19) {
2071 return (F1KEY + ((int) key - GHOST_kKeyF1));
2074 case GHOST_kKeyBackSpace: return BACKSPACEKEY;
2075 case GHOST_kKeyTab: return TABKEY;
2076 case GHOST_kKeyLinefeed: return LINEFEEDKEY;
2077 case GHOST_kKeyClear: return 0;
2078 case GHOST_kKeyEnter: return RETKEY;
2080 case GHOST_kKeyEsc: return ESCKEY;
2081 case GHOST_kKeySpace: return SPACEKEY;
2082 case GHOST_kKeyQuote: return QUOTEKEY;
2083 case GHOST_kKeyComma: return COMMAKEY;
2084 case GHOST_kKeyMinus: return MINUSKEY;
2085 case GHOST_kKeyPeriod: return PERIODKEY;
2086 case GHOST_kKeySlash: return SLASHKEY;
2088 case GHOST_kKeySemicolon: return SEMICOLONKEY;
2089 case GHOST_kKeyEqual: return EQUALKEY;
2091 case GHOST_kKeyLeftBracket: return LEFTBRACKETKEY;
2092 case GHOST_kKeyRightBracket: return RIGHTBRACKETKEY;
2093 case GHOST_kKeyBackslash: return BACKSLASHKEY;
2094 case GHOST_kKeyAccentGrave: return ACCENTGRAVEKEY;
2096 case GHOST_kKeyLeftShift: return LEFTSHIFTKEY;
2097 case GHOST_kKeyRightShift: return RIGHTSHIFTKEY;
2098 case GHOST_kKeyLeftControl: return LEFTCTRLKEY;
2099 case GHOST_kKeyRightControl: return RIGHTCTRLKEY;
2100 case GHOST_kKeyOS: return OSKEY;
2101 case GHOST_kKeyLeftAlt: return LEFTALTKEY;
2102 case GHOST_kKeyRightAlt: return RIGHTALTKEY;
2104 case GHOST_kKeyCapsLock: return CAPSLOCKKEY;
2105 case GHOST_kKeyNumLock: return 0;
2106 case GHOST_kKeyScrollLock: return 0;
2108 case GHOST_kKeyLeftArrow: return LEFTARROWKEY;
2109 case GHOST_kKeyRightArrow: return RIGHTARROWKEY;
2110 case GHOST_kKeyUpArrow: return UPARROWKEY;
2111 case GHOST_kKeyDownArrow: return DOWNARROWKEY;
2113 case GHOST_kKeyPrintScreen: return 0;
2114 case GHOST_kKeyPause: return PAUSEKEY;
2116 case GHOST_kKeyInsert: return INSERTKEY;
2117 case GHOST_kKeyDelete: return DELKEY;
2118 case GHOST_kKeyHome: return HOMEKEY;
2119 case GHOST_kKeyEnd: return ENDKEY;
2120 case GHOST_kKeyUpPage: return PAGEUPKEY;
2121 case GHOST_kKeyDownPage: return PAGEDOWNKEY;
2123 case GHOST_kKeyNumpadPeriod: return PADPERIOD;
2124 case GHOST_kKeyNumpadEnter: return PADENTER;
2125 case GHOST_kKeyNumpadPlus: return PADPLUSKEY;
2126 case GHOST_kKeyNumpadMinus: return PADMINUS;
2127 case GHOST_kKeyNumpadAsterisk: return PADASTERKEY;
2128 case GHOST_kKeyNumpadSlash: return PADSLASHKEY;
2130 case GHOST_kKeyGrLess: return GRLESSKEY;
2133 return UNKNOWNKEY; /* GHOST_kKeyUnknown */
2138 /* adds customdata to event */
2139 static void update_tablet_data(wmWindow *win, wmEvent *event)
2141 const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
2143 /* if there's tablet data from an active tablet device then add it */
2144 if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
2145 struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
2147 wmtab->Active = (int)td->Active;
2148 wmtab->Pressure = td->Pressure;
2149 wmtab->Xtilt = td->Xtilt;
2150 wmtab->Ytilt = td->Ytilt;
2152 event->custom= EVT_DATA_TABLET;
2153 event->customdata= wmtab;
2154 event->customdatafree= 1;
2158 /* imperfect but probably usable... draw/enable drags to other windows */
2159 static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
2161 short mx= evt->x, my= evt->y;
2163 if(wm->windows.first== wm->windows.last)
2166 /* top window bar... */
2167 if(mx<0 || my<0 || mx>win->sizex || my>win->sizey+30) {
2169 wmEventHandler *handler;
2171 /* let's skip windows having modal handlers now */
2172 /* potential XXX ugly... I wouldn't have added a modalhandlers list (introduced in rev 23331, ton) */
2173 for(handler= win->modalhandlers.first; handler; handler= handler->next)
2174 if(handler->ui_handle || handler->op)
2177 /* to desktop space */
2181 /* check other windows to see if it has mouse inside */
2182 for(owin= wm->windows.first; owin; owin= owin->next) {
2185 if(mx-owin->posx >= 0 && my-owin->posy >= 0 &&
2186 mx-owin->posx <= owin->sizex && my-owin->posy <= owin->sizey) {
2187 evt->x= mx-owin->posx;
2188 evt->y= my-owin->posy;
2198 /* windows store own event queues, no bContext here */
2199 /* time is in 1000s of seconds, from ghost */
2200 void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int UNUSED(time), void *customdata)
2203 wmEvent event, *evt= win->eventstate;
2205 /* initialize and copy state (only mouse x y and modifiers) */
2210 case GHOST_kEventCursorMove: {
2212 GHOST_TEventCursorData *cd= customdata;
2213 wmEvent *lastevent= win->queue.last;
2215 #if defined(__APPLE__) && defined(GHOST_COCOA)
2216 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2222 GHOST_ScreenToClient(win->ghostwin, cd->x, cd->y, &cx, &cy);
2224 evt->y= (win->sizey-1) - cy;
2230 event.type= MOUSEMOVE;
2232 /* some painting operators want accurate mouse events, they can
2233 handle inbetween mouse move moves, others can happily ignore
2234 them for better performance */
2235 if(lastevent && lastevent->type == MOUSEMOVE)
2236 lastevent->type = INBETWEEN_MOUSEMOVE;
2238 update_tablet_data(win, &event);
2239 wm_event_add(win, &event);
2241 /* also add to other window if event is there, this makes overdraws disappear nicely */
2242 /* it remaps mousecoord to other window in event */
2243 owin= wm_event_cursor_other_windows(wm, win, &event);
2245 wmEvent oevent= *(owin->eventstate);
2247 oevent.x=owin->eventstate->x= event.x;
2248 oevent.y=owin->eventstate->y= event.y;
2249 oevent.type= MOUSEMOVE;
2251 update_tablet_data(owin, &oevent);
2252 wm_event_add(owin, &oevent);
2258 case GHOST_kEventTrackpad: {
2259 GHOST_TEventTrackpadData * pd = customdata;
2260 switch (pd->subtype) {
2261 case GHOST_kTrackpadEventMagnify:
2262 event.type = MOUSEZOOM;
2264 case GHOST_kTrackpadEventRotate:
2265 event.type = MOUSEROTATE;
2267 case GHOST_kTrackpadEventScroll:
2269 event.type= MOUSEPAN;
2272 #if defined(__APPLE__) && defined(GHOST_COCOA)
2273 //Cocoa already uses coordinates with y=0 at bottom, and returns inwindow coordinates on mouse moved event
2274 event.x= evt->x = pd->x;
2275 event.y = evt->y = pd->y;
2279 GHOST_ScreenToClient(win->ghostwin, pd->x, pd->y, &cx, &cy);
2280 event.x= evt->x= cx;
2281 event.y= evt->y= (win->sizey-1) - cy;
2284 // Use prevx/prevy so we can calculate the delta later
2285 event.prevx= event.x - pd->deltaX;
2286 event.prevy= event.y - pd->deltaY;
2288 update_tablet_data(win, &event);
2289 wm_event_add(win, &event);
2293 case GHOST_kEventButtonDown:
2294 case GHOST_kEventButtonUp: {
2295 GHOST_TEventButtonData *bd= customdata;
2296 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 */
2298 if (bd->button == GHOST_kButtonMaskLeft)
2299 event.type= LEFTMOUSE;
2300 else if (bd->button == GHOST_kButtonMaskRight)
2301 event.type= RIGHTMOUSE;
2302 else if (bd->button == GHOST_kButtonMaskButton4)
2303 event.type= BUTTON4MOUSE;
2304 else if (bd->button == GHOST_kButtonMaskButton5)
2305 event.type= BUTTON5MOUSE;
2307 event.type= MIDDLEMOUSE;
2309 /* add to other window if event is there (not to both!) */
2310 owin= wm_event_cursor_other_windows(wm, win, &event);
2312 wmEvent oevent= *(owin->eventstate);
2316 oevent.type= event.type;
2317 oevent.val= event.val;
2319 update_tablet_data(owin, &oevent);
2320 wm_event_add(owin, &oevent);
2323 update_tablet_data(win, &event);
2324 wm_event_add(win, &event);
2330 case GHOST_kEventKeyDown:
2331 case GHOST_kEventKeyUp: {
2332 GHOST_TEventKeyData *kd= customdata;
2333 event.type= convert_key(kd->key);
2334 event.ascii= kd->ascii;
2335 event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
2337 /* exclude arrow keys, esc, etc from text input */
2338 if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>0))
2342 if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
2343 event.shift= evt->shift= (event.val==KM_PRESS);
2344 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
2345 event.shift= evt->shift = 3; // define?
2347 else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
2348 event.ctrl= evt->ctrl= (event.val==KM_PRESS);
2349 if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
2350 event.ctrl= evt->ctrl = 3; // define?
2352 else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
2353 event.alt= evt->alt= (event.val==KM_PRESS);
2354 if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
2355 event.alt= evt->alt = 3; // define?
2357 else if (event.type==OSKEY) {
2358 event.oskey= evt->oskey= (event.val==KM_PRESS);
2359 if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
2360 event.oskey= evt->oskey = 3; // define?
2363 if(event.val==KM_PRESS && event.keymodifier==0)
2364 evt->keymodifier= event.type; /* only set in eventstate, for next event */
2365 else if(event.val==KM_RELEASE && event.keymodifier==event.type)
2366 event.keymodifier= evt->keymodifier= 0;
2369 /* this case happens on some systems that on holding a key pressed,
2370 generate press events without release, we still want to keep the
2371 modifier in win->eventstate, but for the press event of the same
2372 key we don't want the key modifier */
2373 if(event.keymodifier == event.type)
2374 event.keymodifier= 0;
2376 /* if test_break set, it catches this. XXX Keep global for now? */
2377 if(event.type==ESCKEY)
2380 wm_event_add(win, &event);
2385 case GHOST_kEventWheel: {
2386 GHOST_TEventWheelData* wheelData = customdata;
2388 if (wheelData->z > 0)
2389 event.type= WHEELUPMOUSE;
2391 event.type= WHEELDOWNMOUSE;
2393 event.val= KM_PRESS;
2394 wm_event_add(win, &event);
2398 case GHOST_kEventTimer: {
2400 event.custom= EVT_DATA_TIMER;
2401 event.customdata= customdata;
2402 wm_event_add(win, &event);
2407 case GHOST_kEventUnknown:
2408 case GHOST_kNumEventTypes:
2411 case GHOST_kEventWindowDeactivate: {
2412 event.type= WINDEACTIVATE;
2413 wm_event_add(win, &event);