c# - Strange behaviour when toggling RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY -


i'm writing mouse object in c# uses raw input. device registers , gets data , stuff, it's working in regard. however, on object have property called "exclusive" meant mimic exclusive mode in direct input.

when toggle property true, call registerrawinputdevices dwflags member of rawinputdevice set to: ridev_capturemouse | ridev_nolegacy. , when set property false, set 0.

now problem when mouse button down/up event. on mouse object assign mouse button down event set exclusive true , on mouse set false. when run application events fire , exclusive mode set , reset. weird stuff begins happen:

  1. after mouse event , exclusive mode disabled, window doesn't respond mouse on events in window decorations (e.g. close button won't highlight , can't click on it). can't exit application hitting alt+f4. however, when click on window once or twice regular window behaviour comes back.

  2. after application closed, windows explorer , other application windows react same behaviour. have left , right click multiple times them go normal.

  3. very rarely, window lose focus weird reason. , throws chaos exclusive state (the code set unbind device when window deactivated , restore when activated again). said before, rare occurance, still problematic.

when set/reset exclusive mode using key down , key event, works , none of above happens. , quite baffling.

i have tried code on 2 computers, different mice , 1 running windows 7 x64 , other running windows 8.1 x64.

i have done great deal of searching on in last few days , have come empty, i'm wondering if might have thoughts on why it's behaving in manner? not setting correct flags? calling registerrawinputdevices on , on cause issues?

here's code sample program i'm using test issue:

_mouse = _input.createpointingdevice(_form); _keyboard = _input.createkeyboard(_form);  _mouse.pointingdevicedown += (sender, args) =>                              {                                  if ((args.buttons & pointingdevicebuttons.right) != pointingdevicebuttons.right)                                  {                                      return;                                  }                                   _mouse.exclusive = true;                              };  _mouse.pointingdevicemove += (sender, args) =>                              {                                  _form.text = string.format("{0}x{1}", args.position.x, args.position.y);                              };  _mouse.pointingdeviceup += (sender, args) =>                            {                                if ((args.buttons & pointingdevicebuttons.right) != pointingdevicebuttons.right)                                {                                    return;                                }                                 _mouse.cursorvisible = true;                                _mouse.exclusive = false;                            }; 

here code i'm using register , unregister mouse:

/// <summary> /// function bind input device. /// </summary> protected override void binddevice() {     boundcontrol.mouseleave -= owner_mouseleave;      unbinddevice();      if (_messagefilter != null)     {         _messagefilter.rawinputpointingdevicedata -= getrawdata;         _messagefilter.rawinputpointingdevicedata += getrawdata;     }      _device.usagepage = hidusagepage.generic;     _device.usage = (ushort)hidusage.mouse;     _device.flags = rawinputdeviceflags.none;      // enable background access.     if (allowbackground)     {         _device.flags |= rawinputdeviceflags.inputsink;     }      // enable exclusive access.     if (exclusive)     {         _device.flags |= rawinputdeviceflags.capturemouse | rawinputdeviceflags.nolegacy;     }      _device.windowhandle = boundcontrol.handle;      // attempt register device.     if (!win32api.registerrawinputdevices(_device))     {         throw new gorgonexception(gorgonresult.drivererror, resources.gorinp_raw_cannot_bind_pointing_device);     }      if (!exclusive)     {         onwindowbound(boundcontrol);     } }      /// <summary>     /// function unbind input device.     /// </summary>     protected override void unbinddevice()     {         if (_messagefilter != null)         {             _messagefilter.rawinputpointingdevicedata -= getrawdata;         }          _device.usagepage = hidusagepage.generic;         _device.usage = (ushort)hidusage.mouse;         _device.flags = rawinputdeviceflags.remove;         _device.windowhandle = intptr.zero;          // attempt register device.         if (!win32api.registerrawinputdevices(_device))         {             throw new gorgonexception(gorgonresult.drivererror, resources.gorinp_raw_cannot_unbind_pointing_device);         }          boundcontrol.mouseleave -= owner_mouseleave;     } 

here code processes wm_input message:

/// <summary> /// object representing message loop filter. /// </summary> internal class messagefilter     : system.windows.forms.imessagefilter {     #region events.     /// <summary>     /// event fired when raw input keyboard event occours.     /// </summary>     public event eventhandler<rawinputkeyboardeventargs> rawinputkeyboarddata = null;     /// <summary>     /// event fired when pointing device event occurs.     /// </summary>     public event eventhandler<rawinputpointingdeviceeventargs> rawinputpointingdevicedata = null;     /// <summary>     /// event fired when hid event occurs.     /// </summary>     public event eventhandler<rawinputhideventargs> rawinputhiddata = null;     #endregion      #region variables.     private readonly int _headersize = directaccess.sizeof<rawinputheader>();   // size of input data in bytes.     #endregion      #region imessagefilter members     /// <summary>     /// filters out message before dispatched.     /// </summary>     /// <param name="m">the message dispatched. cannot modify message.</param>     /// <returns>     /// true filter message , stop being dispatched; false allow message continue next filter or control.     /// </returns>     public bool prefiltermessage(ref system.windows.forms.message m)     {         // handle raw input messages.         if ((windowmessages)m.msg != windowmessages.rawinput)         {             return false;         }          unsafe         {             int datasize = 0;              // data size.                        int result = win32api.getrawinputdata(m.lparam, rawinputcommand.input, intptr.zero, ref datasize, _headersize);              if (result == -1)             {                 throw new gorgonexception(gorgonresult.cannotread, resources.gorinp_raw_cannot_read_data);             }              // actual data.             var rawinputptr = stackalloc byte[datasize];             result = win32api.getrawinputdata(m.lparam, rawinputcommand.input, (intptr)rawinputptr, ref datasize, _headersize);              if ((result == -1) || (result != datasize))             {                 throw new gorgonexception(gorgonresult.cannotread, resources.gorinp_raw_cannot_read_data);             }              var rawinput = (rawinput*)rawinputptr;              switch (rawinput->header.type)             {                 case rawinputtype.mouse:                     if (rawinputpointingdevicedata != null)                     {                         rawinputpointingdevicedata(this,                                                    new rawinputpointingdeviceeventargs(rawinput->header.device, ref rawinput->union.mouse));                     }                     break;                 case rawinputtype.keyboard:                     if (rawinputkeyboarddata != null)                     {                         rawinputkeyboarddata(this, new rawinputkeyboardeventargs(rawinput->header.device, ref rawinput->union.keyboard));                     }                     break;                 default:                     if (rawinputhiddata != null)                     {                         var hiddata = new byte[rawinput->union.hid.size * rawinput->union.hid.count];                         var hiddataptr = ((byte*)rawinput) + _headersize + 8;                          fixed (byte* buffer = &hiddata[0])                         {                             directaccess.memorycopy(buffer, hiddataptr, hiddata.length);                         }                          rawinputhiddata(this, new rawinputhideventargs(rawinput->header.device, ref rawinput->union.hid, hiddata));                     }                     break;             }         }          return false;     }     #endregion } 

here's code fires mouse events after processing wm_input:

/// <summary> /// function retrieve , parse raw pointing device data. /// </summary> /// <param name="sender">sender of event.</param> /// <param name="e">event data examine.</param> private void getrawdata(object sender, rawinputpointingdeviceeventargs e) {     if ((boundcontrol == null) || (boundcontrol.disposing))     {         return;     }      if ((_devicehandle != intptr.zero) && (_devicehandle != e.handle))     {         return;     }      if ((exclusive) && (!acquired))     {         // attempt recapture.         if (boundcontrol.focused)         {             acquired = true;         }         else         {             return;         }     }      // nothing if we're outside , have exclusive mode turned off.     if (!exclusive)     {         if (!windowrectangle.contains(boundcontrol.pointtoclient(system.windows.forms.cursor.position)))          {             _outside = true;             return;         }          if (_outside)          {             // if we're inside place position @ entry point.             _outside = false;             position = boundcontrol.pointtoclient(system.windows.forms.cursor.position);         }     }      // wheel data.     if ((e.pointingdevicedata.buttonflags & rawmousebuttons.mousewheel) != 0)     {         onpointingdevicewheelmove((short)e.pointingdevicedata.buttondata);     }      // if we're outside of delay, restart double click cycle.     if (_doubleclicker.milliseconds > doubleclickdelay)     {         _doubleclicker.reset();         _clickcount = 0;     }      // button data.     if ((e.pointingdevicedata.buttonflags & rawmousebuttons.leftdown) != 0)     {         begindoubleclick(pointingdevicebuttons.left);         onpointingdevicedown(pointingdevicebuttons.left);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.rightdown) != 0)     {         begindoubleclick(pointingdevicebuttons.right);         onpointingdevicedown(pointingdevicebuttons.right);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.middledown) != 0)     {         begindoubleclick(pointingdevicebuttons.middle);         onpointingdevicedown(pointingdevicebuttons.middle);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.button4down) != 0)     {         begindoubleclick(pointingdevicebuttons.button4);         onpointingdevicedown(pointingdevicebuttons.button4);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.button5down) != 0)     {         begindoubleclick(pointingdevicebuttons.button5);         onpointingdevicedown(pointingdevicebuttons.button5);     }      // if have 'up' event on buttons, remove flag.     if ((e.pointingdevicedata.buttonflags & rawmousebuttons.leftup) != 0)     {         if (isdoubleclick(pointingdevicebuttons.left))         {             _clickcount += 1;         }         else         {             _doubleclicker.reset();             _clickcount = 0;         }          onpointingdeviceup(pointingdevicebuttons.left, _clickcount);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.rightup) != 0)     {         if (isdoubleclick(pointingdevicebuttons.right))         {             _clickcount += 1;         }         else         {             _doubleclicker.reset();             _clickcount = 0;         }          onpointingdeviceup(pointingdevicebuttons.right, _clickcount);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.middleup) != 0)     {         if (isdoubleclick(pointingdevicebuttons.middle))         {             _clickcount += 1;         }         else         {             _doubleclicker.reset();             _clickcount = 0;         }          onpointingdeviceup(pointingdevicebuttons.middle, _clickcount);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.button4up) != 0)     {         if (isdoubleclick(pointingdevicebuttons.button4))         {             _clickcount += 1;         }         else         {             _doubleclicker.reset();             _clickcount = 0;         }          onpointingdeviceup(pointingdevicebuttons.button4, _clickcount);     }      if ((e.pointingdevicedata.buttonflags & rawmousebuttons.button5up) != 0)     {         if (isdoubleclick(pointingdevicebuttons.button5))         {             _clickcount += 1;         }         else         {             _doubleclicker.reset();             _clickcount = 0;         }          onpointingdeviceup(pointingdevicebuttons.button5, _clickcount);     }      // fire events.     relativeposition = new pointf(e.pointingdevicedata.lastx, e.pointingdevicedata.lasty);     onpointingdevicemove(new pointf(position.x + e.pointingdevicedata.lastx, position.y + e.pointingdevicedata.lasty), false);     updatecursorposition(); } 

well, after days of pulling little hair have left out, find no rhyme or reason why happening. so, devised rather ugly hack fake exclusive mode.

first removed nolegacy , capturemouse flags device registration , locked cursor center of window receiving input via cursor.position. modified window message filter throw away window messages wm_mousemove , wm_keydown wouldn't intercepted window (except system command handles alt+f4) while device in exclusive mode.

while not elegant solution, working wanted. however, if find better way handle situation while still using nolegacy/capturemouse flags, i'll gladly mark correct answer.


Comments

Popular posts from this blog

javascript - Jquery show_hide, what to add in order to make the page scroll to the bottom of the hidden field once button is clicked -

javascript - Highcharts multi-color line -

javascript - Enter key does not work in search box -