5 button mouse support from b333rt in IRC with some edits for X11.
[blender.git] / source / blender / windowmanager / intern / wm_event_system.c
index d8c1e8a58889bdcf43806cc10e4eba8b68a19b9e..ebb7adc3cd51ed869f237223d81df6e6aa15ea72 100644 (file)
@@ -31,8 +31,9 @@
 
 #include "DNA_listBase.h"
 #include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
 #include "DNA_windowmanager_types.h"
-#include "DNA_userdef_types.h" /* U.flag & TWOBUTTONMOUSE */
+#include "DNA_userdef_types.h"
 
 #include "MEM_guardedalloc.h"
 
 #include "BKE_context.h"
 #include "BKE_idprop.h"
 #include "BKE_global.h"
+#include "BKE_object.h"
 #include "BKE_report.h"
+#include "BKE_scene.h"
 #include "BKE_utildefines.h"
+#include "BKE_pointcache.h"
 
-#include "ED_anim_api.h"
+#include "ED_fileselect.h"
+#include "ED_info.h"
 #include "ED_screen.h"
 #include "ED_space_api.h"
 #include "ED_util.h"
@@ -73,7 +78,7 @@ void wm_event_add(wmWindow *win, wmEvent *event_to_add)
        BLI_addtail(&win->queue, event);
 }
 
-static void wm_event_free(wmEvent *event)
+void wm_event_free(wmEvent *event)
 {
        if(event->customdata && event->customdatafree)
                MEM_freeN(event->customdata);
@@ -93,7 +98,7 @@ void wm_event_free_all(wmWindow *win)
 /* ********************* notifiers, listeners *************** */
 
 /* XXX: in future, which notifiers to send to other windows? */
-void WM_event_add_notifier(bContext *C, unsigned int type, void *reference)
+void WM_event_add_notifier(const bContext *C, unsigned int type, void *reference)
 {
        wmNotifier *note= MEM_callocN(sizeof(wmNotifier), "notifier");
        
@@ -125,26 +130,60 @@ static wmNotifier *wm_notifier_next(wmWindowManager *wm)
 void wm_event_do_notifiers(bContext *C)
 {
        wmWindowManager *wm= CTX_wm_manager(C);
-       wmNotifier *note;
+       wmNotifier *note, *next;
        wmWindow *win;
        
        if(wm==NULL)
                return;
        
-       /* cache & catch WM level notifiers, such as frame change */
-       /* XXX todo, multiwindow scenes */
+       /* cache & catch WM level notifiers, such as frame change, scene/screen set */
        for(win= wm->windows.first; win; win= win->next) {
                int do_anim= 0;
                
-               for(note= wm->queue.first; note; note= note->next) {
-                       if(note->window==win)
-                               if(note->category==NC_SCENE)
-                                       if(note->data==ND_FRAME)
+               CTX_wm_window_set(C, win);
+               
+               for(note= wm->queue.first; note; note= next) {
+                       next= note->next;
+
+                       if(note->category==NC_WM) {
+                               if( ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
+                                       wm->file_saved= 1;
+                                       wm_window_title(wm, win);
+                               }
+                               else if(note->data==ND_DATACHANGED)
+                                       wm_window_title(wm, win);
+                       }
+                       if(note->window==win) {
+                               if(note->category==NC_SCREEN) {
+                                       if(note->data==ND_SCREENBROWSE) {
+                                               ED_screen_set(C, note->reference);      // XXX hrms, think this over!
+                                               printf("screen set %p\n", note->reference);
+                                       }
+                                       else if(note->data==ND_SCREENDELETE) {
+                                               ED_screen_delete(C, note->reference);   // XXX hrms, think this over!
+                                               printf("screen delete %p\n", note->reference);
+                                       }
+                               }
+                               else if(note->category==NC_SCENE) {
+                                       if(note->data==ND_SCENEBROWSE) {
+                                               ED_screen_set_scene(C, note->reference);        // XXX hrms, think this over!
+                                               printf("scene set %p\n", note->reference);
+                                       }
+                                       if(note->data==ND_SCENEDELETE) {
+                                               ED_screen_delete_scene(C, note->reference);     // XXX hrms, think this over!
+                                               printf("scene delete %p\n", note->reference);
+                                       }
+                                       else if(note->data==ND_FRAME)
                                                do_anim= 1;
+                               }
+                       }
+                       if(ELEM4(note->category, NC_SCENE, NC_OBJECT, NC_GEOM, NC_SCENE)) {
+                               ED_info_stats_clear(CTX_data_scene(C));
+                               WM_event_add_notifier(C, NC_SPACE|ND_SPACE_INFO, NULL);
+                       }
                }
                if(do_anim) {
                        /* depsgraph gets called, might send more notifiers */
-                       CTX_wm_window_set(C, win);
                        ED_update_for_newframe(C, 1);
                }
        }
@@ -154,35 +193,42 @@ void wm_event_do_notifiers(bContext *C)
                wmWindow *win;
                
                for(win= wm->windows.first; win; win= win->next) {
-                       ScrArea *sa;
-                       ARegion *ar;
+                       
+                       /* filter out notifiers */
+                       if(note->category==NC_SCREEN && note->reference && note->reference!=win->screen);
+                       else if(note->category==NC_SCENE && note->reference && note->reference!=win->screen->scene);
+                       else {
+                               ScrArea *sa;
+                               ARegion *ar;
 
-                       /* XXX context in notifiers? */
-                       CTX_wm_window_set(C, win);
+                               /* XXX context in notifiers? */
+                               CTX_wm_window_set(C, win);
 
-                       /* printf("notifier win %d screen %s\n", win->winid, win->screen->id.name+2); */
-                       ED_screen_do_listen(win, note);
+                               /* printf("notifier win %d screen %s cat %x\n", win->winid, win->screen->id.name+2, note->category); */
+                               ED_screen_do_listen(win, note);
 
-                       for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
-                               ED_region_do_listen(ar, note);
-                       }
-                       
-                       for(sa= win->screen->areabase.first; sa; sa= sa->next) {
-                               ED_area_do_listen(sa, note);
-                               for(ar=sa->regionbase.first; ar; ar= ar->next) {
+                               for(ar=win->screen->regionbase.first; ar; ar= ar->next) {
                                        ED_region_do_listen(ar, note);
                                }
+                               
+                               for(sa= win->screen->areabase.first; sa; sa= sa->next) {
+                                       ED_area_do_listen(sa, note);
+                                       for(ar=sa->regionbase.first; ar; ar= ar->next) {
+                                               ED_region_do_listen(ar, note);
+                                       }
+                               }
                        }
                }
                
-               CTX_wm_window_set(C, NULL);
-               
                MEM_freeN(note);
        }
        
        /* cached: editor refresh callbacks now, they get context */
        for(win= wm->windows.first; win; win= win->next) {
+               Scene *sce, *scene= win->screen->scene;
                ScrArea *sa;
+               Base *base;
+               
                CTX_wm_window_set(C, win);
                for(sa= win->screen->areabase.first; sa; sa= sa->next) {
                        if(sa->do_refresh) {
@@ -190,12 +236,49 @@ void wm_event_do_notifiers(bContext *C)
                                ED_area_do_refresh(C, sa);
                        }
                }
+               
+               if(G.rendering==0) { // XXX make lock in future, or separated derivedmesh users in scene
+                       
+                       /* update all objects, ipos, matrices, displists, etc. Flags set by depgraph or manual, 
+                               no layer check here, gets correct flushed */
+                       /* sets first, we allow per definition current scene to have dependencies on sets */
+                       if(scene->set) {
+                               for(SETLOOPER(scene->set, base))
+                                       object_handle_update(scene, base->object);
+                       }
+                       
+                       for(base= scene->base.first; base; base= base->next) {
+                               object_handle_update(scene, base->object);
+                       }
+
+                       BKE_ptcache_quick_cache_all(scene);
+               }               
        }
        CTX_wm_window_set(C, NULL);
 }
 
 /* ********************* operators ******************* */
 
+int WM_operator_poll(bContext *C, wmOperatorType *ot)
+{
+       wmOperatorTypeMacro *otmacro;
+       
+       for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
+               wmOperatorType *ot= WM_operatortype_find(otmacro->idname, 0);
+               
+               if(0==WM_operator_poll(C, ot))
+                       return 0;
+       }
+       
+       /* python needs operator type, so we added exception for it */
+       if(ot->pyop_poll)
+               return ot->pyop_poll(C, ot);
+       else if(ot->poll)
+               return ot->poll(C);
+
+       return 1;
+}
+
 /* if repeat is true, it doesn't register again, nor does it free */
 static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
 {
@@ -204,7 +287,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
        if(op==NULL || op->type==NULL)
                return retval;
        
-       if(op->type->poll && op->type->poll(C)==0)
+       if(0==WM_operator_poll(C, op->type))
                return retval;
        
        if(op->type->exec)
@@ -219,8 +302,8 @@ static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
                        ED_undo_push_op(C, op);
                
                if(repeat==0) {
-                       if(op->type->flag & OPTYPE_REGISTER)
-                               wm_operator_register(CTX_wm_manager(C), op);
+                       if((op->type->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
+                               wm_operator_register(C, op);
                        else
                                WM_operator_free(op);
                }
@@ -265,11 +348,31 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, P
 
        /* initialize error reports */
        if (reports) {
-               op->reports= reports; /* must be initialized alredy */
+               op->reports= reports; /* must be initialized already */
        }
        else {
                op->reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
-               BKE_reports_init(op->reports, RPT_STORE);
+               BKE_reports_init(op->reports, RPT_STORE|RPT_FREE);
+       }
+       
+       /* recursive filling of operator macro list */
+       if(ot->macro.first) {
+               static wmOperator *motherop= NULL;
+               wmOperatorTypeMacro *otmacro;
+               
+               /* ensure all ops are in execution order in 1 list */
+               if(motherop==NULL) 
+                       motherop= op;
+               
+               for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
+                       wmOperatorType *otm= WM_operatortype_find(otmacro->idname, 0);
+                       wmOperator *opm= wm_operator_create(wm, otm, otmacro->ptr, NULL);
+                       
+                       BLI_addtail(&motherop->macro, opm);
+                       opm->opm= motherop; /* pointer to mom, for modal() */
+               }
+               
+               motherop= NULL;
        }
        
        return op;
@@ -277,24 +380,36 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, P
 
 static void wm_operator_print(wmOperator *op)
 {
-       char *buf = WM_operator_pystring(op);
+       char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
        printf("%s\n", buf);
        MEM_freeN(buf);
 }
 
-static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties)
+static void wm_region_mouse_co(bContext *C, wmEvent *event)
+{
+       ARegion *ar= CTX_wm_region(C);
+       if(ar) {
+               /* compatibility convention */
+               event->mval[0]= event->x - ar->winrct.xmin;
+               event->mval[1]= event->y - ar->winrct.ymin;
+       }
+}
+
+static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties, ReportList *reports)
 {
        wmWindowManager *wm= CTX_wm_manager(C);
        int retval= OPERATOR_PASS_THROUGH;
 
-       if(ot->poll==NULL || ot->poll(C)) {
-               wmOperator *op= wm_operator_create(wm, ot, properties, NULL);
+       if(WM_operator_poll(C, ot)) {
+               wmOperator *op= wm_operator_create(wm, ot, properties, reports); /* if reports==NULL, theyll be initialized */
                
                if((G.f & G_DEBUG) && event && event->type!=MOUSEMOVE)
                        printf("handle evt %d win %d op %s\n", event?event->type:0, CTX_wm_screen(C)->subwinactive, ot->idname); 
                
-               if(op->type->invoke && event)
-                       retval= (*op->type->invoke)(C, op, event);
+               if(op->type->invoke && event) {
+                       wm_region_mouse_co(C, event);
+                       retval= op->type->invoke(C, op, event);
+               }
                else if(op->type->exec)
                        retval= op->type->exec(C, op);
                else
@@ -313,23 +428,29 @@ static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, P
                        if(ot->flag & OPTYPE_UNDO)
                                ED_undo_push_op(C, op);
                        
-                       if(ot->flag & OPTYPE_REGISTER)
-                               wm_operator_register(wm, op);
+                       if((ot->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
+                               wm_operator_register(C, op);
                        else
                                WM_operator_free(op);
                }
-               else if(!(retval & OPERATOR_RUNNING_MODAL)) {
-                       WM_operator_free(op);
+               else if(retval & OPERATOR_RUNNING_MODAL) {
+                       /* grab cursor during blocking modal ops (X11) */
+                       if(ot->flag & OPTYPE_BLOCKING)
+                               WM_cursor_grab(CTX_wm_window(C), 1);
                }
+               else
+                       WM_operator_free(op);
        }
 
        return retval;
 }
 
-/* invokes operator in context */
-int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
+/* WM_operator_name_call is the main accessor function
+ * this is for python to access since its done the operator lookup
+ * 
+ * invokes operator in context */
+static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
 {
-       wmOperatorType *ot= WM_operatortype_find(opstring);
        wmWindow *window= CTX_wm_window(C);
        wmEvent *event;
        
@@ -357,7 +478,7 @@ int WM_operator_name_call(bContext *C, const char *opstring, int context, Pointe
                                                CTX_wm_region_set(C, ar1);
                                }
                                
-                               retval= wm_operator_invoke(C, ot, event, properties);
+                               retval= wm_operator_invoke(C, ot, event, properties, reports);
                                
                                /* set region back */
                                CTX_wm_region_set(C, ar);
@@ -372,7 +493,7 @@ int WM_operator_name_call(bContext *C, const char *opstring, int context, Pointe
                                ARegion *ar= CTX_wm_region(C);
 
                                CTX_wm_region_set(C, NULL);
-                               retval= wm_operator_invoke(C, ot, event, properties);
+                               retval= wm_operator_invoke(C, ot, event, properties, reports);
                                CTX_wm_region_set(C, ar);
 
                                return retval;
@@ -387,7 +508,7 @@ int WM_operator_name_call(bContext *C, const char *opstring, int context, Pointe
 
                                CTX_wm_region_set(C, NULL);
                                CTX_wm_area_set(C, NULL);
-                               retval= wm_operator_invoke(C, ot, window->eventstate, properties);
+                               retval= wm_operator_invoke(C, ot, event, properties, reports);
                                CTX_wm_region_set(C, ar);
                                CTX_wm_area_set(C, area);
 
@@ -396,27 +517,51 @@ int WM_operator_name_call(bContext *C, const char *opstring, int context, Pointe
                        case WM_OP_EXEC_DEFAULT:
                                event= NULL;    /* pass on without break */
                        case WM_OP_INVOKE_DEFAULT:
-                               return wm_operator_invoke(C, ot, event, properties);
+                               return wm_operator_invoke(C, ot, event, properties, reports);
                }
        }
        
        return 0;
 }
 
+
+/* invokes operator in context */
+int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties)
+{
+       wmOperatorType *ot= WM_operatortype_find(opstring, 0);
+       if(ot)
+               return wm_operator_call_internal(C, ot, context, properties, NULL);
+
+       return 0;
+}
+
 /* Similar to WM_operator_name_call called with WM_OP_EXEC_DEFAULT context.
    - wmOperatorType is used instead of operator name since python alredy has the operator type
    - poll() must be called by python before this runs.
    - reports can be passed to this function (so python can report them as exceptions)
 */
-int WM_operator_call_py(bContext *C, wmOperatorType *ot, PointerRNA *properties, ReportList *reports)
+int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports)
 {
+       int retval= OPERATOR_CANCELLED;
+
+#if 0
+       wmOperator *op;
        wmWindowManager *wm=    CTX_wm_manager(C);
-       wmOperator *op=                 wm_operator_create(wm, ot, properties, reports);
-       int retval=                             op->type->exec(C, op);
+       op= wm_operator_create(wm, ot, properties, reports);
+
+       if (op->type->exec)
+               retval= op->type->exec(C, op);
+       else
+               printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name);
+#endif
+
+       retval= wm_operator_call_internal(C, ot, context, properties, reports);
        
-       if (reports)
-               op->reports= NULL; /* dont let the operator free reports passed to this function */
-       WM_operator_free(op);
+       /* keep the reports around if needed later */
+       if (retval & OPERATOR_RUNNING_MODAL || ot->flag & OPTYPE_REGISTER)
+       {
+               reports->flag |= RPT_FREE;
+       }
        
        return retval;
 }
@@ -424,10 +569,44 @@ int WM_operator_call_py(bContext *C, wmOperatorType *ot, PointerRNA *properties,
 
 /* ********************* handlers *************** */
 
-/* not handler itself, is called by UI to move handlers to other queues, so don't close modal ones */
+/* future extra customadata free? */
 static void wm_event_free_handler(wmEventHandler *handler)
 {
+       MEM_freeN(handler);
+}
+
+/* only set context when area/region is part of screen */
+static void wm_handler_op_context(bContext *C, wmEventHandler *handler)
+{
+       bScreen *screen= CTX_wm_screen(C);
        
+       if(screen && handler->op) {
+               if(handler->op_area==NULL)
+                       CTX_wm_area_set(C, NULL);
+               else {
+                       ScrArea *sa;
+                       
+                       for(sa= screen->areabase.first; sa; sa= sa->next)
+                               if(sa==handler->op_area)
+                                       break;
+                       if(sa==NULL) {
+                               /* when changing screen layouts with running modal handlers (like render display), this
+                                  is not an error to print */
+                               if(handler->op==NULL)
+                                       printf("internal error: handler (%s) has invalid area\n", handler->op->type->idname);
+                       }
+                       else {
+                               ARegion *ar;
+                               CTX_wm_area_set(C, sa);
+                               for(ar= sa->regionbase.first; ar; ar= ar->next)
+                                       if(ar==handler->op_region)
+                                               break;
+                               /* XXX no warning print here, after full-area and back regions are remade */
+                               if(ar)
+                                       CTX_wm_region_set(C, ar);
+                       }
+               }
+       }
 }
 
 /* called on exit or remove area, only here call cancel callback */
@@ -444,8 +623,7 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers)
                                ScrArea *area= CTX_wm_area(C);
                                ARegion *region= CTX_wm_region(C);
                                
-                               CTX_wm_area_set(C, handler->op_area);
-                               CTX_wm_region_set(C, handler->op_region);
+                               wm_handler_op_context(C, handler);
 
                                handler->op->type->cancel(C, handler->op);
 
@@ -454,22 +632,25 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers)
                        }
 
                        WM_operator_free(handler->op);
+                       WM_cursor_grab(CTX_wm_window(C), 0);
                }
                else if(handler->ui_remove) {
                        ScrArea *area= CTX_wm_area(C);
                        ARegion *region= CTX_wm_region(C);
+                       ARegion *menu= CTX_wm_menu(C);
                        
                        if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
                        if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
+                       if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
 
                        handler->ui_remove(C, handler->ui_userdata);
 
                        CTX_wm_area_set(C, area);
                        CTX_wm_region_set(C, region);
+                       CTX_wm_menu_set(C, menu);
                }
 
                wm_event_free_handler(handler);
-               MEM_freeN(handler);
        }
 }
 
@@ -521,9 +702,23 @@ static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *kmi)
 {
        int kmitype= wm_userdef_event_map(kmi->type);
 
+       if(kmi->inactive) return 0;
+
+       /* exception for middlemouse emulation */
+       if((U.flag & USER_TWOBUTTONMOUSE) && (kmi->type == MIDDLEMOUSE)) {
+               if(winevent->type == LEFTMOUSE && winevent->alt) {
+                       wmKeymapItem tmp= *kmi;
+
+                       tmp.type= winevent->type;
+                       tmp.alt= winevent->alt;
+                       if(wm_eventmatch(winevent, &tmp))
+                               return 1;
+               }
+       }
+
        /* the matching rules */
        if(kmitype==KM_TEXTINPUT)
-               if(ISKEYBOARD(winevent->type)) return 1;
+               if(ISKEYBOARD(winevent->type) && winevent->ascii) return 1;
        if(kmitype!=KM_ANY)
                if(winevent->type!=kmitype) return 0;
        
@@ -539,21 +734,15 @@ static int wm_eventmatch(wmEvent *winevent, wmKeymapItem *kmi)
                if(winevent->alt != kmi->alt && !(winevent->alt & kmi->alt)) return 0;
        if(kmi->oskey!=KM_ANY)
                if(winevent->oskey != kmi->oskey && !(winevent->oskey & kmi->oskey)) return 0;
+       
        if(kmi->keymodifier)
                if(winevent->keymodifier!=kmi->keymodifier) return 0;
-       
-       /* happens on tweak failure */
-       /* weak code, testing only now! (ton) */
-       if(kmi->is_tweak) {
-               /* only after tweak keymap we allow the hack */
-               if(winevent->no_tweak) {
-                       winevent->no_tweak= 2;
-                       return 0;
-               }
-       }
-       
-       if(winevent->no_tweak==1)
-               return 0;
+               
+       /* key modifiers always check when event has it */
+       /* otherwise regular keypresses with keymodifier still work */
+       if(winevent->keymodifier)
+               if(ISKEYBOARD(winevent->type)) 
+                       if(winevent->keymodifier!=kmi->keymodifier) return 0;
        
        return 1;
 }
@@ -564,6 +753,22 @@ static int wm_event_always_pass(wmEvent *event)
        return ELEM5(event->type, TIMER, TIMER0, TIMER1, TIMER2, TIMERJOBS);
 }
 
+/* operator exists */
+static void wm_event_modalkeymap(wmOperator *op, wmEvent *event)
+{
+       if(op->type->modalkeymap) {
+               wmKeymapItem *kmi;
+               
+               for(kmi= op->type->modalkeymap->keymap.first; kmi; kmi= kmi->next) {
+                       if(wm_eventmatch(event, kmi)) {
+                                       
+                               event->type= EVT_MODAL_MAP;
+                               event->val= kmi->propvalue;
+                       }
+               }
+       }
+}
+
 /* Warning: this function removes a modal handler, when finished */
 static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event, PointerRNA *properties)
 {
@@ -579,8 +784,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                        ScrArea *area= CTX_wm_area(C);
                        ARegion *region= CTX_wm_region(C);
                        
-                       CTX_wm_area_set(C, handler->op_area);
-                       CTX_wm_region_set(C, handler->op_region);
+                       wm_handler_op_context(C, handler);
+                       wm_region_mouse_co(C, event);
+                       wm_event_modalkeymap(op, event);
                        
                        retval= ot->modal(C, op, event);
 
@@ -608,8 +814,8 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                                if(ot->flag & OPTYPE_UNDO)
                                        ED_undo_push_op(C, op);
                                
-                               if(ot->flag & OPTYPE_REGISTER)
-                                       wm_operator_register(CTX_wm_manager(C), op);
+                               if((ot->flag & OPTYPE_REGISTER) || (G.f & G_DEBUG))
+                                       wm_operator_register(C, op);
                                else
                                        WM_operator_free(op);
                                handler->op= NULL;
@@ -621,9 +827,10 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                        
                        /* remove modal handler, operator itself should have been cancelled and freed */
                        if(retval & (OPERATOR_CANCELLED|OPERATOR_FINISHED)) {
+                               WM_cursor_grab(CTX_wm_window(C), 0);
+
                                BLI_remlink(handlers, handler);
                                wm_event_free_handler(handler);
-                               MEM_freeN(handler);
                                
                                /* prevent silly errors from operator users */
                                //retval &= ~OPERATOR_PASS_THROUGH;
@@ -634,10 +841,10 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                        printf("wm_handler_operator_call error\n");
        }
        else {
-               wmOperatorType *ot= WM_operatortype_find(event->keymap_idname);
+               wmOperatorType *ot= WM_operatortype_find(event->keymap_idname, 0);
 
                if(ot)
-                       retval= wm_operator_invoke(C, ot, event, properties);
+                       retval= wm_operator_invoke(C, ot, event, properties, NULL);
        }
 
        if(retval & OPERATOR_PASS_THROUGH)
@@ -650,23 +857,30 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *eve
 {
        ScrArea *area= CTX_wm_area(C);
        ARegion *region= CTX_wm_region(C);
-       int retval;
+       ARegion *menu= CTX_wm_menu(C);
+       int retval, always_pass;
                        
        /* we set context to where ui handler came from */
        if(handler->ui_area) CTX_wm_area_set(C, handler->ui_area);
        if(handler->ui_region) CTX_wm_region_set(C, handler->ui_region);
+       if(handler->ui_menu) CTX_wm_menu_set(C, handler->ui_menu);
+
+       /* in advance to avoid access to freed event on window close */
+       always_pass= wm_event_always_pass(event);
 
        retval= handler->ui_handle(C, event, handler->ui_userdata);
 
        /* putting back screen context */
-       if((retval != WM_UI_HANDLER_BREAK) || wm_event_always_pass(event)) {
+       if((retval != WM_UI_HANDLER_BREAK) || always_pass) {
                CTX_wm_area_set(C, area);
                CTX_wm_region_set(C, region);
+               CTX_wm_menu_set(C, menu);
        }
        else {
                /* this special cases is for areas and regions that get removed */
                CTX_wm_area_set(C, NULL);
                CTX_wm_region_set(C, NULL);
+               CTX_wm_menu_set(C, NULL);
        }
 
        if(retval == WM_UI_HANDLER_BREAK)
@@ -675,16 +889,108 @@ static int wm_handler_ui_call(bContext *C, wmEventHandler *handler, wmEvent *eve
        return WM_HANDLER_CONTINUE;
 }
 
+/* fileselect handlers are only in the window queue, so it's save to switch screens or area types */
+static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHandler *handler, wmEvent *event)
+{
+       SpaceFile *sfile;
+       int action= WM_HANDLER_CONTINUE;
+       
+       if(event->type != EVT_FILESELECT)
+               return action;
+       if(handler->op != (wmOperator *)event->customdata)
+               return action;
+       
+       switch(event->val) {
+               case EVT_FILESELECT_OPEN: 
+               case EVT_FILESELECT_FULL_OPEN: 
+                       {       
+                               if(event->val==EVT_FILESELECT_OPEN)
+                                       ED_area_newspace(C, handler->op_area, SPACE_FILE);
+                               else
+                                       ED_screen_full_newspace(C, handler->op_area, SPACE_FILE);
+                               
+                               /* settings for filebrowser, sfile is not operator owner but sends events */
+                               sfile= (SpaceFile*)CTX_wm_space_data(C);
+                               sfile->op= handler->op;
+
+                               ED_fileselect_set_params(sfile);
+                               
+                               action= WM_HANDLER_BREAK;
+                       }
+                       break;
+                       
+               case EVT_FILESELECT_EXEC:
+               case EVT_FILESELECT_CANCEL:
+                       {
+                               /* XXX validate area and region? */
+                               bScreen *screen= CTX_wm_screen(C);
+                               char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
+                               
+                               if(screen != handler->filescreen)
+                                       ED_screen_full_prevspace(C);
+                               else
+                                       ED_area_prevspace(C);
+                               
+                               /* remlink now, for load file case */
+                               BLI_remlink(handlers, handler);
+                               
+                               if(event->val==EVT_FILESELECT_EXEC) {
+                                       wm_handler_op_context(C, handler);
+                               
+                                       /* a bit weak, might become arg for WM_event_fileselect? */
+                                       /* XXX also extension code in image-save doesnt work for this yet */
+                                       if(strncmp(handler->op->type->name, "Save", 4)==0) {
+                                               /* this gives ownership to pupmenu */
+                                               uiPupMenuSaveOver(C, handler->op, path);
+                                       }
+                                       else {
+                                               int retval= handler->op->type->exec(C, handler->op);
+                                               
+                                               if (retval & OPERATOR_FINISHED)
+                                                       if(G.f & G_DEBUG)
+                                                               wm_operator_print(handler->op);
+                                               
+                                               WM_operator_free(handler->op);
+                                       }
+                                       
+                                       CTX_wm_area_set(C, NULL);
+                               }
+                               else 
+                                       WM_operator_free(handler->op);
+                               
+                               wm_event_free_handler(handler);
+                               MEM_freeN(path);
+                               
+                               action= WM_HANDLER_BREAK;
+                       }
+                       break;
+       }
+       
+       return action;
+}
+
 static int handler_boundbox_test(wmEventHandler *handler, wmEvent *event)
 {
        if(handler->bbwin) {
                if(handler->bblocal) {
                        rcti rect= *handler->bblocal;
                        BLI_translate_rcti(&rect, handler->bbwin->xmin, handler->bbwin->ymin);
-                       return BLI_in_rcti(&rect, event->x, event->y);
+
+                       if(BLI_in_rcti(&rect, event->x, event->y))
+                               return 1;
+                       else if(event->type==MOUSEMOVE && BLI_in_rcti(&rect, event->prevx, event->prevy))
+                               return 1;
+                       else
+                               return 0;
+               }
+               else {
+                       if(BLI_in_rcti(handler->bbwin, event->x, event->y))
+                               return 1;
+                       else if(event->type==MOUSEMOVE && BLI_in_rcti(handler->bbwin, event->prevx, event->prevy))
+                               return 1;
+                       else
+                               return 0;
                }
-               else 
-                       return BLI_in_rcti(handler->bbwin, event->x, event->y);
        }
        return 1;
 }
@@ -693,6 +999,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
 {
        wmEventHandler *handler, *nexthandler;
        int action= WM_HANDLER_CONTINUE;
+       int always_pass;
 
        if(handlers==NULL) return action;
        
@@ -702,35 +1009,48 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
 
                /* optional boundbox */
                if(handler_boundbox_test(handler, event)) {
+                       /* in advance to avoid access to freed event on window close */
+                       always_pass= wm_event_always_pass(event);
                
                        /* modal+blocking handler */
                        if(handler->flag & WM_HANDLER_BLOCKING)
                                action= WM_HANDLER_BREAK;
 
                        if(handler->keymap) {
+                               wmKeyMap *keymap= handler->keymap;
                                wmKeymapItem *kmi;
                                
-                               for(kmi= handler->keymap->first; kmi; kmi= kmi->next) {
-                                       if(wm_eventmatch(event, kmi)) {
-                                               
-                                               event->keymap_idname= kmi->idname;      /* weak, but allows interactive callback to not use rawkey */
-                                               
-                                               action= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
-                                               if(action==WM_HANDLER_BREAK)  /* not wm_event_always_pass(event) here, it denotes removed handler */
-                                                       break;
+                               if(!keymap->poll || keymap->poll(C)) {
+                                       for(kmi= keymap->keymap.first; kmi; kmi= kmi->next) {
+                                               if(wm_eventmatch(event, kmi)) {
+                                                       
+                                                       event->keymap_idname= kmi->idname;      /* weak, but allows interactive callback to not use rawkey */
+                                                       
+                                                       action= wm_handler_operator_call(C, handlers, handler, event, kmi->ptr);
+                                                       if(action==WM_HANDLER_BREAK)  /* not always_pass here, it denotes removed handler */
+                                                               break;
+                                               }
                                        }
                                }
                        }
                        else if(handler->ui_handle) {
                                action= wm_handler_ui_call(C, handler, event);
                        }
+                       else if(handler->type==WM_HANDLER_FILESELECT) {
+                               /* screen context changes here */
+                               action= wm_handler_fileselect_call(C, handlers, handler, event);
+                       }
                        else {
                                /* modal, swallows all */
                                action= wm_handler_operator_call(C, handlers, handler, event, NULL);
                        }
 
-                       if(!wm_event_always_pass(event) && action==WM_HANDLER_BREAK)
-                               break;
+                       if(action==WM_HANDLER_BREAK) {
+                               if(always_pass)
+                                       action= WM_HANDLER_CONTINUE;
+                               else
+                                       break;
+                       }
                }
                
                /* fileread case */
@@ -742,6 +1062,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
 
 static int wm_event_inside_i(wmEvent *event, rcti *rect)
 {
+       if(wm_event_always_pass(event))
+               return 1;
        if(BLI_in_rcti(rect, event->x, event->y))
           return 1;
        if(event->type==MOUSEMOVE) {
@@ -843,50 +1165,70 @@ void wm_event_do_handlers(bContext *C)
                        /* MVC demands to not draw in event handlers... but we need to leave it for ogl selecting etc */
                        wm_window_make_drawable(C, win);
                        
-                       action= wm_handlers_do(C, event, &win->handlers);
+                       /* first we do priority handlers, modal + some limited keymaps */
+                       action= wm_handlers_do(C, event, &win->modalhandlers);
                        
                        /* fileread case */
-                       if(CTX_wm_window(C)==NULL) {
+                       if(CTX_wm_window(C)==NULL)
                                return;
-                       }
                        
-                       if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
+                       /* builtin tweak, if action is break it removes tweak */
+                       wm_tweakevent_test(C, event, action);
+                       
+                       if(action==WM_HANDLER_CONTINUE) {
                                ScrArea *sa;
                                ARegion *ar;
                                int doit= 0;
                                
                                /* XXX to solve, here screen handlers? */
                                if(event->type==MOUSEMOVE) {
-                                       ED_screen_set_subwinactive(win, event); /* state variables in screen, cursors */
+                                       /* state variables in screen, cursors */
+                                       ED_screen_set_subwinactive(win, event); 
+                                       /* for regions having custom cursors */
                                        wm_paintcursor_test(C, event);
                                }
                                
                                for(sa= win->screen->areabase.first; sa; sa= sa->next) {
-                                       if(wm_event_always_pass(event) || wm_event_inside_i(event, &sa->totrct)) {
-                                               
+                                       if(wm_event_inside_i(event, &sa->totrct)) {
                                                CTX_wm_area_set(C, sa);
-                                               CTX_wm_region_set(C, NULL);
-                                               action= wm_handlers_do(C, event, &sa->handlers);
 
-                                               if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
+                                               if(action==WM_HANDLER_CONTINUE) {
                                                        for(ar=sa->regionbase.first; ar; ar= ar->next) {
-                                                               if(wm_event_always_pass(event) || wm_event_inside_i(event, &ar->winrct)) {
+                                                               if(wm_event_inside_i(event, &ar->winrct)) {
                                                                        CTX_wm_region_set(C, ar);
                                                                        action= wm_handlers_do(C, event, &ar->handlers);
 
                                                                        doit |= (BLI_in_rcti(&ar->winrct, event->x, event->y));
                                                                        
-                                                                       if(!wm_event_always_pass(event)) {
-                                                                               if(action==WM_HANDLER_BREAK)
-                                                                                       break;
-                                                                       }
+                                                                       if(action==WM_HANDLER_BREAK)
+                                                                               break;
                                                                }
                                                        }
                                                }
+
+                                               CTX_wm_region_set(C, NULL);
+
+                                               if(action==WM_HANDLER_CONTINUE)
+                                                       action= wm_handlers_do(C, event, &sa->handlers);
+
+                                               CTX_wm_area_set(C, NULL);
+
                                                /* NOTE: do not escape on WM_HANDLER_BREAK, mousemove needs handled for previous area */
                                        }
                                }
                                
+                               if(action==WM_HANDLER_CONTINUE) {
+                                       /* also some non-modal handlers need active area/region */
+                                       CTX_wm_area_set(C, area_event_inside(C, event->x, event->y));
+                                       CTX_wm_region_set(C, region_event_inside(C, event->x, event->y));
+
+                                       action= wm_handlers_do(C, event, &win->handlers);
+
+                                       /* fileread case */
+                                       if(CTX_wm_window(C)==NULL)
+                                               return;
+                               }
+
                                /* XXX hrmf, this gives reliable previous mouse coord for area change, feels bad? 
                                   doing it on ghost queue gives errors when mousemoves go over area borders */
                                if(doit && win->screen->subwinactive != win->screen->mainwin) {
@@ -900,32 +1242,102 @@ void wm_event_do_handlers(bContext *C)
                        wm_event_free(event);
                        
                }
+               
+               /* only add mousemove when queue was read entirely */
+               if(win->addmousemove) {
+                       wmEvent event= *(win->eventstate);
+                       event.type= MOUSEMOVE;
+                       event.prevx= event.x;
+                       event.prevy= event.y;
+                       wm_event_add(win, &event);
+                       win->addmousemove= 0;
+               }
+               
                CTX_wm_window_set(C, NULL);
        }
 }
 
+/* ********** filesector handling ************ */
+
+void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
+{
+       /* add to all windows! */
+       wmWindow *win;
+       
+       for(win= CTX_wm_manager(C)->windows.first; win; win= win->next) {
+               wmEvent event= *win->eventstate;
+               
+               event.type= EVT_FILESELECT;
+               event.val= eventval;
+               event.customdata= ophandle;             // only as void pointer type check
+
+               wm_event_add(win, &event);
+       }
+}
+
+/* operator is supposed to have a filled "path" property */
+/* optional property: filetype (XXX enum?) */
+
+/* Idea is to keep a handler alive on window queue, owning the operator.
+   The filewindow can send event to make it execute, thus ensuring
+   executing happens outside of lower level queues, with UI refreshed. 
+   Should also allow multiwin solutions */
+
+void WM_event_add_fileselect(bContext *C, wmOperator *op)
+{
+       wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "fileselect handler");
+       wmWindow *win= CTX_wm_window(C);
+       int full= 1;    // XXX preset?
+       
+       handler->type= WM_HANDLER_FILESELECT;
+       handler->op= op;
+       handler->op_area= CTX_wm_area(C);
+       handler->op_region= CTX_wm_region(C);
+       handler->filescreen= CTX_wm_screen(C);
+       
+       BLI_addhead(&win->modalhandlers, handler);
+       
+       WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
+}
+
 /* lets not expose struct outside wm? */
 void WM_event_set_handler_flag(wmEventHandler *handler, int flag)
 {
        handler->flag= flag;
 }
 
-wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOperator *op)
+wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
 {
        wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event modal handler");
-       handler->op= op;
+       wmWindow *win= CTX_wm_window(C);
+       
+       /* operator was part of macro */
+       if(op->opm) {
+               /* give the mother macro to the handler */
+               handler->op= op->opm;
+               /* mother macro opm becomes the macro element */
+               handler->op->opm= op;
+       }
+       else
+               handler->op= op;
+       
        handler->op_area= CTX_wm_area(C);               /* means frozen screen context for modal handlers! */
        handler->op_region= CTX_wm_region(C);
        
-       BLI_addhead(handlers, handler);
+       BLI_addhead(&win->modalhandlers, handler);
 
        return handler;
 }
 
-wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap)
+wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
 {
        wmEventHandler *handler;
 
+       if(!keymap) {
+               printf("WM_event_add_keymap_handler called with NULL keymap\n");
+               return NULL;
+       }
+
        /* only allow same keymap once */
        for(handler= handlers->first; handler; handler= handler->next)
                if(handler->keymap==keymap)
@@ -939,7 +1351,7 @@ wmEventHandler *WM_event_add_keymap_handler(ListBase *handlers, ListBase *keymap
 }
 
 /* priorities not implemented yet, for time being just insert in begin of list */
-wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, ListBase *keymap, int priority)
+wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, wmKeyMap *keymap, int priority)
 {
        wmEventHandler *handler;
        
@@ -952,7 +1364,7 @@ wmEventHandler *WM_event_add_keymap_handler_priority(ListBase *handlers, ListBas
        return handler;
 }
 
-wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, ListBase *keymap, rcti *bblocal, rcti *bbwin)
+wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, wmKeyMap *keymap, rcti *bblocal, rcti *bbwin)
 {
        wmEventHandler *handler= WM_event_add_keymap_handler(handlers, keymap);
        
@@ -963,7 +1375,7 @@ wmEventHandler *WM_event_add_keymap_handler_bb(ListBase *handlers, ListBase *key
        return handler;
 }
 
-void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap)
+void WM_event_remove_keymap_handler(ListBase *handlers, wmKeyMap *keymap)
 {
        wmEventHandler *handler;
        
@@ -971,13 +1383,12 @@ void WM_event_remove_keymap_handler(ListBase *handlers, ListBase *keymap)
                if(handler->keymap==keymap) {
                        BLI_remlink(handlers, handler);
                        wm_event_free_handler(handler);
-                       MEM_freeN(handler);
                        break;
                }
        }
 }
 
-wmEventHandler *WM_event_add_ui_handler(bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
+wmEventHandler *WM_event_add_ui_handler(const bContext *C, ListBase *handlers, wmUIHandlerFunc func, wmUIHandlerRemoveFunc remove, void *userdata)
 {
        wmEventHandler *handler= MEM_callocN(sizeof(wmEventHandler), "event ui handler");
        handler->ui_handle= func;
@@ -985,6 +1396,7 @@ wmEventHandler *WM_event_add_ui_handler(bContext *C, ListBase *handlers, wmUIHan
        handler->ui_userdata= userdata;
        handler->ui_area= (C)? CTX_wm_area(C): NULL;
        handler->ui_region= (C)? CTX_wm_region(C): NULL;
+       handler->ui_menu= (C)? CTX_wm_menu(C): NULL;
        
        BLI_addhead(handlers, handler);
        
@@ -999,7 +1411,6 @@ void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHa
                if(handler->ui_handle == func && handler->ui_remove == remove && handler->ui_userdata == userdata) {
                        BLI_remlink(handlers, handler);
                        wm_event_free_handler(handler);
-                       MEM_freeN(handler);
                        break;
                }
        }
@@ -1008,11 +1419,8 @@ void WM_event_remove_ui_handler(ListBase *handlers, wmUIHandlerFunc func, wmUIHa
 void WM_event_add_mousemove(bContext *C)
 {
        wmWindow *window= CTX_wm_window(C);
-       wmEvent event= *(window->eventstate);
-       event.type= MOUSEMOVE;
-       event.prevx= event.x;
-       event.prevy= event.y;
-       wm_event_add(window, &event);
+       
+       window->addmousemove= 1;
 }
 
 /* for modal callbacks, check configuration for how to interpret exit with tweaks  */
@@ -1120,10 +1528,10 @@ static void update_tablet_data(wmWindow *win, wmEvent *event)
        const GHOST_TabletData *td= GHOST_GetTabletData(win->ghostwin);
        
        /* if there's tablet data from an active tablet device then add it */
-       if ((td != NULL) && td->Active) {
+       if ((td != NULL) && td->Active != GHOST_kTabletModeNone) {
                struct wmTabletData *wmtab= MEM_mallocN(sizeof(wmTabletData), "customdata tablet");
                
-               wmtab->Active = td->Active;
+               wmtab->Active = (int)td->Active;
                wmtab->Pressure = td->Pressure;
                wmtab->Xtilt = td->Xtilt;
                wmtab->Ytilt = td->Ytilt;
@@ -1171,14 +1579,13 @@ void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
                                event.type= LEFTMOUSE;
                        else if (bd->button == GHOST_kButtonMaskRight)
                                event.type= RIGHTMOUSE;
+                       else if (bd->button == GHOST_kButtonMaskButton4)
+                               event.type= BUTTON4MOUSE;
+                       else if (bd->button == GHOST_kButtonMaskButton5)
+                               event.type= BUTTON5MOUSE;
                        else
                                event.type= MIDDLEMOUSE;
                        
-                       if(event.val)
-                               event.keymodifier= evt->keymodifier= event.type;
-                       else
-                               event.keymodifier= evt->keymodifier= 0;
-                       
                        update_tablet_data(win, &event);
                        wm_event_add(win, &event);
                        
@@ -1190,7 +1597,7 @@ void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
                        GHOST_TEventKeyData *kd= customdata;
                        event.type= convert_key(kd->key);
                        event.ascii= kd->ascii;
-                       event.val= (type==GHOST_kEventKeyDown); /* XXX eventmatch uses defines, bad code... */
+                       event.val= (type==GHOST_kEventKeyDown)?KM_PRESS:KM_RELEASE;
                        
                        /* exclude arrow keys, esc, etc from text input */
                        if(type==GHOST_kEventKeyUp || (event.ascii<32 && event.ascii>14))
@@ -1198,30 +1605,36 @@ void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
                        
                        /* modifiers */
                        if (event.type==LEFTSHIFTKEY || event.type==RIGHTSHIFTKEY) {
-                               event.shift= evt->shift= event.val;
-                               if(event.val && (evt->ctrl || evt->alt || evt->oskey))
+                               event.shift= evt->shift= (event.val==KM_PRESS);
+                               if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->oskey))
                                   event.shift= evt->shift = 3;         // define?
                        } 
                        else if (event.type==LEFTCTRLKEY || event.type==RIGHTCTRLKEY) {
-                               event.ctrl= evt->ctrl= event.val;
-                               if(event.val && (evt->shift || evt->alt || evt->oskey))
+                               event.ctrl= evt->ctrl= (event.val==KM_PRESS);
+                               if(event.val==KM_PRESS && (evt->shift || evt->alt || evt->oskey))
                                   event.ctrl= evt->ctrl = 3;           // define?
                        } 
                        else if (event.type==LEFTALTKEY || event.type==RIGHTALTKEY) {
-                               event.alt= evt->alt= event.val;
-                               if(event.val && (evt->ctrl || evt->shift || evt->oskey))
+                               event.alt= evt->alt= (event.val==KM_PRESS);
+                               if(event.val==KM_PRESS && (evt->ctrl || evt->shift || evt->oskey))
                                   event.alt= evt->alt = 3;             // define?
                        } 
                        else if (event.type==COMMANDKEY) {
-                               event.oskey= evt->oskey= event.val;
-                               if(event.val && (evt->ctrl || evt->alt || evt->shift))
+                               event.oskey= evt->oskey= (event.val==KM_PRESS);
+                               if(event.val==KM_PRESS && (evt->ctrl || evt->alt || evt->shift))
                                   event.oskey= evt->oskey = 3;         // define?
                        }
+                       else {
+                               if(event.val==KM_PRESS && event.keymodifier==0)
+                                       evt->keymodifier= event.type; /* only set in eventstate, for next event */
+                               else if(event.val==KM_RELEASE && event.keymodifier==event.type)
+                                       event.keymodifier= evt->keymodifier= 0;
+                       }
                        
-                       /* if test_break set, it catches this. Keep global for now? */
+                       /* if test_break set, it catches this. XXX Keep global for now? */
                        if(event.type==ESCKEY)
                                G.afbreek= 1;
-                       
+
                        wm_event_add(win, &event);
                        
                        break;
@@ -1254,4 +1667,3 @@ void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
                        break;
        }
 }
-