Adding *full* 5 Button Mouse Support (Windows)

Post tutorials on how to do certain tasks within game or engine code here.
Post Reply
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Adding *full* 5 Button Mouse Support (Windows)

Post by Baker »

Yes, this isn't quite a tutorial and more are instructions as it doesn't explain anything like a proper tutorial should, yet for the most part a lot of this wouldn't need explanation in a tutorial (but some of it would).

These instructions implement support for 5 mouse buttons in Windows both without and with -dinput (DirectInput) turned on.

1. keys.c - Add the yellow or change to make like the yellow. Yes add even MOUSE6, MOUSE7, MOUSE8 even though there is no way known to me to get more than 5 mouse buttons to work through the Windows API.
{"MOUSE1", K_MOUSE1},
{"MOUSE2", K_MOUSE2},
{"MOUSE3", K_MOUSE3},
{"MOUSE4", K_MOUSE4}, // Baker: 5 button support
{"MOUSE5", K_MOUSE5},
{"MOUSE6", K_MOUSE6},
{"MOUSE7", K_MOUSE7},
{"MOUSE8", K_MOUSE8},
2. keys.h - You have to change all teh numbers as a result of inserting these new ones.
#define K_MOUSE4 203 // Baker - begin 8 button support
#define K_MOUSE5 204
#define K_MOUSE6 205
#define K_MOUSE7 206
#define K_MOUSE8 207 // Baker: end 8 button support

//
// joystick buttons
//
#define K_JOY1 208
#define K_JOY2 209
#define K_JOY3 210
#define K_JOY4 211

//
// aux keys are for multi-buttoned joysticks to generate so they can use
// the normal binding process
//
#define K_AUX1 213
#define K_AUX2 214
#define K_AUX3 215
#define K_AUX4 216
#define K_AUX5 217
#define K_AUX6 218
#define K_AUX7 219
#define K_AUX8 220
#define K_AUX9 221
#define K_AUX10 222
#define K_AUX11 223
#define K_AUX12 224
#define K_AUX13 225
#define K_AUX14 226
#define K_AUX15 227
#define K_AUX16 228
#define K_AUX17 229
#define K_AUX18 230
#define K_AUX19 231
#define K_AUX20 232
#define K_AUX21 233
#define K_AUX22 234
#define K_AUX23 235
#define K_AUX24 236
#define K_AUX25 237
#define K_AUX26 238
#define K_AUX27 239
#define K_AUX28 240
#define K_AUX29 241
#define K_AUX30 242
#define K_AUX31 243
#define K_AUX32 244

// JACK: Intellimouse(c) Mouse Wheel Support

#define K_MWHEELUP 245
#define K_MWHEELDOWN 246
3. in_win.c
*/
// in_win.c -- windows 95 mouse and joystick code
// 02/21/97 JCB Added extended DirectInput code to support external controllers.

#define DIRECTINPUT_VERSION 0x0700
#include <dinput.h>
#include "quakedef.h"
#include "winquake.h"
and ...
typedef struct MYDATA {
LONG lX; // X axis goes here
LONG lY; // Y axis goes here
LONG lZ; // Z axis goes here
BYTE bButtonA; // One button goes here
BYTE bButtonB; // Another button goes here
BYTE bButtonC; // Another button goes here
BYTE bButtonD; // Another button goes here
BYTE bButtonE; // Baker: DINPUT fix for 8 buttons
BYTE bButtonF; // Baker: DINPUT fix for 8 buttons
BYTE bButtonG; // Baker: DINPUT fix for 8 buttons
BYTE bButtonH; // Baker: DINPUT fix for 8 buttons

} MYDATA;

static DIOBJECTDATAFORMAT rgodf[] = {
{ &GUID_XAxis, FIELD_OFFSET(MYDATA, lX), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
{ &GUID_YAxis, FIELD_OFFSET(MYDATA, lY), DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
{ &GUID_ZAxis, FIELD_OFFSET(MYDATA, lZ), 0x80000000 | DIDFT_AXIS | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonA), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonB), DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonC), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonD), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonE), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonF), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonG), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
{ 0, FIELD_OFFSET(MYDATA, bButtonH), 0x80000000 | DIDFT_BUTTON | DIDFT_ANYINSTANCE, 0,},
};
mouse_buttons = 8;

// if a fullscreen video mode was set before the mouse was initialized,
// set the mouse state appropriately
if (mouseactivatetoggle)
IN_ActivateMouse ();
case DIMOFS_BUTTON2:
if (od.dwData & 0x80)
mstate_di |= (1<<2);
else
mstate_di &= ~(1<<2);
break;

case DIMOFS_BUTTON3:
if (od.dwData & 0x80)
mstate_di |= (1<<3);
else
mstate_di &= ~(1<<3);
break;

case DIMOFS_BUTTON4:
if (od.dwData & 0x80)
mstate_di |= (1<<4);
else
mstate_di &= ~(1<<4);
break;
4. gl_vidnt.c or vid_wgl.c (depending on the engine -- most use those names)
// this is complicated because Win32 seems to pack multiple mouse events into
// one update sometimes, so we always check all states and look for events
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
temp = 0;

if (wParam & MK_LBUTTON)
temp |= 1;

if (wParam & MK_RBUTTON)
temp |= 2;

if (wParam & MK_MBUTTON)
temp |= 4;

// Baker: these defines are so that an engine
// compiles using the gcc compiler that comes with MinGW
// which apparently doesn't have them defined in windows.h
// Really these defines should be placed in winquake.h
// But for the sake of a simpler tutorial, you can put 'em here
// MK_XBUTTON1 and MK_XBUTTON2 are already defined in
// windows.h if using Visual Studio but why not write the tutorial
// for usage with either compiler I say.
#ifndef MK_XBUTTON1
#define MK_XBUTTON1 0x0020
#endif
#ifndef MK_XBUTTON2
#define MK_XBUTTON2 0x0040
#endif

if (wParam & MK_XBUTTON1)
temp |= 8;

if (wParam & MK_XBUTTON2)
temp |= 16;


IN_MouseEvent (temp);
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

More than 5 buttons needs driver support; the generic mouse driver that most Windows installs use won't cut it. Not even the Raw Input API (which DirectInput uses behind the scenes anyway) supports them.

It would be interesting to test a theory here. A Con_Printf of all messages that get passed to DefWindowProc might be able to identify what message numbers the extra buttons use (assuming that they use any, that they're consistent across different makes and models, and that you don't have to code to a vendor-specific API).

Just want to draw attention to this line:

Code: Select all

#define DIRECTINPUT_VERSION 0x0700
If you're compiling with a more recent DirectX SDK then this is needed to prevent throwing a warning. The precise warning is:
DIRECTINPUT_VERSION undefined. Defaulting to version 0x0800
It's very important that this be defined to 0x0700 not 0x0800 for Quake, otherwise DirectInput will fail to start up. Reason why is because Quake uses the old DirectInput interface (DirectInputCreate) rather than the newer one (DirectInput8Create), and startup will fail if you call DirectInputCreate with a DIRECTINPUT_VERSION of 0x0800.

If you've rewritten your input code none of this is needed, of course.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Adding *full* 5 Button Mouse Support (Windows)

Post by mankrip »

I'm getting this error:
src\in_win.c|834|error C2065: 'DIMOFS_BUTTON4' : undeclared identifier|

DIMOFS_BUTTON3 isn't triggering any errors, though. And I did add the #define DIRECTINPUT_VERSION 0x0700 to the beginning of the file.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Adding *full* 5 Button Mouse Support (Windows)

Post by mh »

What dinput.h are you using? Microsoft's has this:

Code: Select all

#define DIMOFS_X        FIELD_OFFSET(DIMOUSESTATE, lX)
#define DIMOFS_Y        FIELD_OFFSET(DIMOUSESTATE, lY)
#define DIMOFS_Z        FIELD_OFFSET(DIMOUSESTATE, lZ)
#define DIMOFS_BUTTON0 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 0)
#define DIMOFS_BUTTON1 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 1)
#define DIMOFS_BUTTON2 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 2)
#define DIMOFS_BUTTON3 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 3)
#if (DIRECTINPUT_VERSION >= 0x0700)
#define DIMOFS_BUTTON4 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 4)
#define DIMOFS_BUTTON5 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 5)
#define DIMOFS_BUTTON6 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 6)
#define DIMOFS_BUTTON7 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 7)
#endif
It may be possible to just copy/paste that block to your in_win.c, add a few #ifdefs, and get things working.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Adding *full* 5 Button Mouse Support (Windows)

Post by revelator »

Hmm if its from MinGW64 it looks like this

#if DIRECTINPUT_VERSION >= 0x0700
/* "Standard" Mouse report for DInput 7... */
typedef struct DIMOUSESTATE2 {
LONG lX;
LONG lY;
LONG lZ;
BYTE rgbButtons[8];
} DIMOUSESTATE2;
#endif /* DI7 */

#define DIMOFS_X FIELD_OFFSET(DIMOUSESTATE, lX)
#define DIMOFS_Y FIELD_OFFSET(DIMOUSESTATE, lY)
#define DIMOFS_Z FIELD_OFFSET(DIMOUSESTATE, lZ)
#define DIMOFS_BUTTON0 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 0)
#define DIMOFS_BUTTON1 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 1)
#define DIMOFS_BUTTON2 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 2)
#define DIMOFS_BUTTON3 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 3)
#if DIRECTINPUT_VERSION >= 0x0700
#define DIMOFS_BUTTON4 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 4)
#define DIMOFS_BUTTON5 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 5)
#define DIMOFS_BUTTON6 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 6)
#define DIMOFS_BUTTON7 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 7)
#endif /* DI7 */

seems mostly the same ?.

If its stock MinGW you might need to use the MS directx sdk as its lacking a lot of the DirectX headers.
Allegro has some MinGW specific replacement headers but i allways had to hack them a bit for them to work :/ guess the api they where made against has had some changes since.
Productivity is a state of mind.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Adding *full* 5 Button Mouse Support (Windows)

Post by mankrip »

The one in my path (C:\Dev\Tools\DXSDK\Include)also has those lines,

Code: Select all

/****************************************************************************
 *
 *  Copyright (C) 1996-2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       dinput.h
 *  Content:    DirectInput include file
 *
 ****************************************************************************/

[...]

/*
 *  To build applications for older versions of DirectInput
 *
 *  #define DIRECTINPUT_VERSION [ 0x0300 | 0x0500 | 0x0700 ]
 *
 *  before #include <dinput.h>.  By default, #include <dinput.h>
 *  will produce a DirectX 8-compatible header file.
 *
 */

#define DIRECTINPUT_HEADER_VERSION  0x0800
#ifndef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION         DIRECTINPUT_HEADER_VERSION
#pragma message(__FILE__ ": DIRECTINPUT_VERSION undefined. Defaulting to version 0x0800")
#endif

[...]

#define DIMOFS_X        FIELD_OFFSET(DIMOUSESTATE, lX)
#define DIMOFS_Y        FIELD_OFFSET(DIMOUSESTATE, lY)
#define DIMOFS_Z        FIELD_OFFSET(DIMOUSESTATE, lZ)
#define DIMOFS_BUTTON0 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 0)
#define DIMOFS_BUTTON1 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 1)
#define DIMOFS_BUTTON2 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 2)
#define DIMOFS_BUTTON3 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 3)
#if (DIRECTINPUT_VERSION >= 0x0700)
#define DIMOFS_BUTTON4 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 4)
#define DIMOFS_BUTTON5 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 5)
#define DIMOFS_BUTTON6 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 6)
#define DIMOFS_BUTTON7 (FIELD_OFFSET(DIMOUSESTATE2, rgbButtons) + 7)
#endif
... but... :shock: I guess I'm using the one bundled with Quake's source release (\QuakeDev\Sources\exe\Makaqu\src\s_win32\dxsdk\SDK\INC); it doesn't have those lines.

Code: Select all

/****************************************************************************
 *
 *  Copyright (C) 1996 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       dinput.h
 *  Content:    DirectInput include file
 *
 ****************************************************************************/

[...]

#define DIRECTINPUT_VERSION         0x0300

[...]

#define DIMOFS_X        FIELD_OFFSET(DIMOUSESTATE, lX)
#define DIMOFS_Y        FIELD_OFFSET(DIMOUSESTATE, lY)
#define DIMOFS_Z        FIELD_OFFSET(DIMOUSESTATE, lZ)
#define DIMOFS_BUTTON0 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 0)
#define DIMOFS_BUTTON1 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 1)
#define DIMOFS_BUTTON2 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 2)
#define DIMOFS_BUTTON3 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 3)
[edit] Okay, I've replaced the DirectX library and include paths in the project file, and it's compiling fine now. Thanks!
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Adding *full* 5 Button Mouse Support (Windows)

Post by revelator »

Holy crap thats an ancient DirectX :D
Good you found the culprit though.
Productivity is a state of mind.
Post Reply