5 button mouse support from b333rt in IRC with some edits for X11.
[blender.git] / source / blender / windowmanager / intern / wm_event_system.c
index d40e6c60bfddb51f0d2e224b844bde0eeb3edf1b..ebb7adc3cd51ed869f237223d81df6e6aa15ea72 100644 (file)
@@ -52,6 +52,7 @@
 #include "BKE_pointcache.h"
 
 #include "ED_fileselect.h"
+#include "ED_info.h"
 #include "ED_screen.h"
 #include "ED_space_api.h"
 #include "ED_util.h"
@@ -129,7 +130,7 @@ 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)
@@ -141,7 +142,9 @@ void wm_event_do_notifiers(bContext *C)
                
                CTX_wm_window_set(C, win);
                
-               for(note= wm->queue.first; note; note= note->next) {
+               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;
@@ -156,16 +159,28 @@ void wm_event_do_notifiers(bContext *C)
                                                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 */
@@ -244,20 +259,23 @@ void wm_event_do_notifiers(bContext *C)
 
 /* ********************* operators ******************* */
 
-static int wm_operator_poll(bContext *C, wmOperatorType *ot)
+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))
+               if(0==WM_operator_poll(C, ot))
                        return 0;
        }
        
-       if(ot->poll)
+       /* 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;
 }
 
@@ -269,7 +287,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, int repeat)
        if(op==NULL || op->type==NULL)
                return retval;
        
-       if(0==wm_operator_poll(C, op->type))
+       if(0==WM_operator_poll(C, op->type))
                return retval;
        
        if(op->type->exec)
@@ -330,11 +348,11 @@ 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 */
@@ -362,7 +380,7 @@ static wmOperator *wm_operator_create(wmWindowManager *wm, wmOperatorType *ot, P
 
 static void wm_operator_print(wmOperator *op)
 {
-       char *buf = WM_operator_pystring(op->type, op->ptr, 1);
+       char *buf = WM_operator_pystring(NULL, op->type, op->ptr, 1);
        printf("%s\n", buf);
        MEM_freeN(buf);
 }
@@ -377,13 +395,13 @@ static void wm_region_mouse_co(bContext *C, wmEvent *event)
        }
 }
 
-static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, PointerRNA *properties)
+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(wm_operator_poll(C, ot)) {
-               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); 
@@ -427,10 +445,12 @@ static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, P
        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, 0);
        wmWindow *window= CTX_wm_window(C);
        wmEvent *event;
        
@@ -458,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);
@@ -473,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;
@@ -488,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, event, properties);
+                               retval= wm_operator_invoke(C, ot, event, properties, reports);
                                CTX_wm_region_set(C, ar);
                                CTX_wm_area_set(C, area);
 
@@ -497,32 +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)
 {
-       wmWindowManager *wm=    CTX_wm_manager(C);
-       wmOperator *op=                 wm_operator_create(wm, ot, properties, reports);
        int retval= OPERATOR_CANCELLED;
-       
+
+#if 0
+       wmOperator *op;
+       wmWindowManager *wm=    CTX_wm_manager(C);
+       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;
 }
@@ -664,10 +703,22 @@ 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;
        
@@ -683,8 +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;
+               
+       /* 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;
 }
@@ -786,7 +844,7 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                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)
@@ -845,9 +903,7 @@ static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHa
        switch(event->val) {
                case EVT_FILESELECT_OPEN: 
                case EVT_FILESELECT_FULL_OPEN: 
-                       {
-                               char *dir= NULL; char *path= RNA_string_get_alloc(handler->op->ptr, "filename", NULL, 0);
-                                       
+                       {       
                                if(event->val==EVT_FILESELECT_OPEN)
                                        ED_area_newspace(C, handler->op_area, SPACE_FILE);
                                else
@@ -858,8 +914,6 @@ static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHa
                                sfile->op= handler->op;
 
                                ED_fileselect_set_params(sfile);
-                               dir = NULL;
-                               MEM_freeN(path);
                                
                                action= WM_HANDLER_BREAK;
                        }
@@ -870,7 +924,7 @@ static int wm_handler_fileselect_call(bContext *C, ListBase *handlers, wmEventHa
                        {
                                /* XXX validate area and region? */
                                bScreen *screen= CTX_wm_screen(C);
-                               char *path= RNA_string_get_alloc(handler->op->ptr, "filename", NULL, 0);
+                               char *path= RNA_string_get_alloc(handler->op->ptr, "path", NULL, 0);
                                
                                if(screen != handler->filescreen)
                                        ED_screen_full_prevspace(C);
@@ -963,16 +1017,19 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
                                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;
+                                               }
                                        }
                                }
                        }
@@ -988,8 +1045,12 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
                                action= wm_handler_operator_call(C, handlers, handler, event, NULL);
                        }
 
-                       if(!always_pass && action==WM_HANDLER_BREAK)
-                               break;
+                       if(action==WM_HANDLER_BREAK) {
+                               if(always_pass)
+                                       action= WM_HANDLER_CONTINUE;
+                               else
+                                       break;
+                       }
                }
                
                /* fileread case */
@@ -1001,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) {
@@ -1102,58 +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;
-                       }
                        
                        /* builtin tweak, if action is break it removes tweak */
-                       if(!wm_event_always_pass(event))
-                               wm_tweakevent_test(C, event, action);
+                       wm_tweakevent_test(C, event, action);
                        
-                       if(wm_event_always_pass(event) || action==WM_HANDLER_CONTINUE) {
+                       if(action==WM_HANDLER_CONTINUE) {
                                ScrArea *sa;
                                ARegion *ar;
                                int doit= 0;
                                
                                /* XXX to solve, here screen handlers? */
-                               if(!wm_event_always_pass(event)) {
-                                       if(event->type==MOUSEMOVE) {
-                                               /* state variables in screen, cursors */
-                                               ED_screen_set_subwinactive(win, event); 
-                                               /* for regions having custom cursors */
-                                               wm_paintcursor_test(C, event);
-                                       }
+                               if(event->type==MOUSEMOVE) {
+                                       /* 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) {
@@ -1200,7 +1275,7 @@ void WM_event_fileselect_event(bContext *C, void *ophandle, int eventval)
        }
 }
 
-/* operator is supposed to have a filled "filename" property */
+/* 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.
@@ -1220,7 +1295,7 @@ void WM_event_add_fileselect(bContext *C, wmOperator *op)
        handler->op_region= CTX_wm_region(C);
        handler->filescreen= CTX_wm_screen(C);
        
-       BLI_addhead(&win->handlers, handler);
+       BLI_addhead(&win->modalhandlers, handler);
        
        WM_event_fileselect_event(C, op, full?EVT_FILESELECT_FULL_OPEN:EVT_FILESELECT_OPEN);
 }
@@ -1231,9 +1306,10 @@ 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");
+       wmWindow *win= CTX_wm_window(C);
        
        /* operator was part of macro */
        if(op->opm) {
@@ -1248,15 +1324,20 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, ListBase *handlers, wmOp
        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)
@@ -1270,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;
        
@@ -1283,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);
        
@@ -1294,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;
        
@@ -1447,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;
@@ -1498,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);
                        
@@ -1544,6 +1624,12 @@ void wm_event_add_ghostevent(wmWindow *win, int type, void *customdata)
                                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. XXX Keep global for now? */
                        if(event.type==ESCKEY)