Another Way to Handle Input
AKA Pointers Are Your Friends
Before we begin, to read this article you must know:
- What a Windows Message is and how it can be handled
- What a Function Pointer is
In this article I'm going to explain another way to handle input apart from the traditional keys[ ] array. I used to mess up the code with the keys[ ] because I always add new keys or even keyboard modes when I'm coding. One incorrect brace can drive the compiler mad. So, my aim is to make a system where you can just register a key and its function. It will support several keyboard modes (menu mode, game mode, limbo mode,Â…..). I will also add a function for text editing and a function to scan what number a key has.
Source code can be found at the end of this article... with that said... here we go...
First, we make a header file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #ifndef kbdctrl
#define kbdctrl
void KC_init( LRESULT (*)( HWND , UINT , WPARAM , LPARAM ));
void KC_SetMode( int );
int KC_GetMode ( void );
void KC_SetKey( int , void ( *)( int , int ));
void KC_SetKey( char , void ( *)( int , int ));
void KC_SetMouse( int , void ( *)( int , int ));
void KC_Editor( char *, int , bool *);
void KC_Scan( int *, bool *);
LRESULT KC_Parse( HWND , UINT , WPARAM , LPARAM );
#endif
|
Then, the source file.
1 2 3 4 | #include <windows.h>
#include "kbdctrl.h"
|
The principle is simple, but the syntax is not. The base of our ‘system’ is a two dimensional array of pointers: the first index number is the mode, the second indicates the key the function is bound to, the value is a pointer to the function bound to that key. All the functions must return void and accept two integers as argument. I’ll reserve space for 8 modes, 256 keyboard keys and 10 mouse keys. (The keyword static can be omitted).
1 | static void ( *event_ptr[8][266])( int , int );
|
As you see, the syntax is rather odd; this is why function pointers are used so seldom.
1 | static LRESULT ( *nextproc)( HWND , UINT , WPARAM , LPARAM ) = &DefWindowProc;
|
This variable will hold the pointer to the next Windows message handler. All unprocessed messages will be directed to it. It is DefWindowProc (the default handler) by default.
This int holds the number of the current mode (between 0 and 7).
1 2 3 4 5 | static bool editor = false ;
static char *ed_string;
static int ed_offset = 0;
static int ed_length = 0;
static bool *ed_done;
|
These are the global variables used by the editor mode.
1 2 3 | static bool scan = false ;
static bool *sc_done;
static int *sc_value;
|
For the scan mode.
1 2 3 4 5 | void KC_EXEC( int pos, int x, int y)
{
if (event_ptr[curmod][pos] != NULL)
(*event_ptr[curmod][pos])(x,y);
}
|
This little function invokes the pos function in the current mode with parameters x and y if the pointer is not null. If the pointer is null, there is no function and no execution is needed.
1 2 3 4 | void KC_init( LRESULT (*next)( HWND , UINT , WPARAM , LPARAM ))
{
nextproc = next;
}
|
This function is used to register the Windows message handler that can handle the unprocessed messages.
1 2 3 4 5 6 7 8 9 | void KC_SetMode( int mod)
{
curmod = mod;
}
int KC_GetMode ( void )
{
return curmod;
}
|
Two functions to get and set the mode.
1 2 3 4 | void KC_SetKey( int token, void ( *event)( int , int ))
{
event_ptr[curmod][token] = event;
}
|
This function binds a function to a key: token holds the key number and void(*event)(int,int) holds a pointer to a function.
1 2 3 4 | void KC_SetKey( char token, void ( *event)( int , int ))
{
event_ptr[curmod][token] = event;
}
|
This function is an overloaded version of the former and can be altered if the conversion char-int is incorrect.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void KC_SetMouse( int token, void ( *event)( int , int ))
{
event_ptr[curmod][token+256] = event;
}
|
This function is used to register a mouse key:
0 |
left double-click |
1 |
left pressed |
2 |
left released |
3 |
right double-click |
4 |
right pressed |
5 |
right released |
6 |
middle double-click |
7 |
middle pressed |
8 |
middle released |
9 |
mouse moved |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void KC_Editor( char *str, int len, bool *done)
{
ed_string = str;
ed_offset = 0;
ed_length = len;
ed_done = done;
editor = true ;
}
void KC_Scan( int *val, bool *done)
{
sc_value = val;
sc_done = done;
scan = true ;
}
|
These two functions register the values for the editor and scanner and enable them. The use of these functions might need a little explanation. Because we can’t wait for the functions to return data (the engine must go on), the data and state are stored in a variable the parent function can access (the pointer is passed, not the value). When the *done is true, the parent function knows the correct value is in the variable (str[ ] and *val) and can take ‘the appropriate steps’.
Now the big one...
1 | LRESULT KC_Parse( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
The KC_Parse funtion is to be used as a Windows message handler, this explains the rather odd data types used as return value and arguments.
1 2 3 4 5 6 7 8 9 10 11 | {
if (scan)
{
if (uMsg == WM_KEYDOWN)
{
*sc_value = wParam;
*sc_done = true ;
scan = false ;
return 0;
}
}
|
This is clear, I guess, if we are in scan mode and a key is held down, store the key number and set *sc_done to true, so the parent function knows the number is ready. 0 is returned, so, when in scan mode, there is no other mouse or keyboard input possible.
If in editor mode...
and a WM_CHAR message is received (this message contains a character, not a key number, so it can be used like that).
1 2 3 4 5 6 | if (wParam == VK_RETURN)
{
editor = false ;
*ed_done = true ;
return 0;
}
|
If enter is pressed, end editor mode, and tell the parent the string is ready (AND return null).
1 2 3 4 5 | if ((wParam == VK_BACK) && (ed_offset != 0) )
{
ed_string[--ed_offset] = NULL;
return 0;
}
|
If backspace is pressed and the cursor is not on the 0th position, set the previous character in the string to null and point the offset(‘cursor’ position) to it. (AND return 0).
1 2 3 4 | if (ed_offset < ed_length)
{
ed_string[ed_offset++] = wParam;
}
|
If the string isnÂ’t full (offset < length and not offset
return 0, so, that if in editor mode, a character message ends the function. The KEYDOWN message (that always comes with the character message) is not processed because of the else statement on the next line. This, in short, means that all keyboard commands are blocked, BUT, mouse input is still processed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | else
{
switch (uMsg)
{
case WM_KEYDOWN:
{
KC_EXEC(wParam,1,0);
return 0;
}
case WM_KEYUP:
{
KC_EXEC(wParam,0,0);
return 0;
}
};
}
|
So, when we arenÂ’t in edit mode, keyboard input is processed. A KEYDOWN message executes the function corresponding with the key code, NOT to the character code (they often match, but not always). The function gets 1,0 as parameters. If a KEYUP is received, the parameters are 0,0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | switch (uMsg)
{
case WM_LBUTTONDBLCLK:
{
KC_EXEC(256,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_LBUTTONDOWN:
{
KC_EXEC(257,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_LBUTTONUP:
{
KC_EXEC(258,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_MBUTTONDBLCLK:
{
KC_EXEC(259,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_MBUTTONDOWN:
{
KC_EXEC(260,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_MBUTTONUP:
{
KC_EXEC(261,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_RBUTTONDBLCLK:
{
KC_EXEC(262,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_RBUTTONDOWN:
{
KC_EXEC(263,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_RBUTTONUP:
{
KC_EXEC(264,LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_MOUSEMOVE:
{
KC_EXEC(265,LOWORD(lParam),HIWORD(lParam));
return 0;
}
|
The mouse code. If a message is received, the corresponding function is executed. The parameters are the x and y position of the mouse relative to the upper left corner of the window. (for the LOWORD and HIGHWORD stuff, please refer to MSDN).
1 2 3 4 5 | };
}
return ( *nextproc)(hWnd,uMsg,wParam,lParam);
}
|
The unprocessed messages are sent to the next handler (DefWindowProc by default).
So, to make the use clear, I will implement a NeHe program using some misc functions, and full 3D movement. It, however, is only a test suit I used for tracing bugs, so it isnÂ’t exactly esthetical (if any one wants to make something more fancy, please do so and let me know).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdlib.h>
#include <stdio.h>
#include <windows.h> // Header File For Windows
#include <stdio.h> // Header File For Standard Input/Output
#include <gl\gl.h> // Header File For The OpenGL32 Library
#include <gl\glu.h> // Header File For The GLu32 Library
#include <gl\glaux.h> // Header File For The Glaux Library
#include <math.h>
#include "kbdctrl.h"
#define STRIDE 2 // Length Of One ‘pace’
#define piover180 0.01745329252f
GLYPHMETRICSFLOAT gmf[96];
GLuint fontbase;
|
Variables for 3D movement.
1 2 3 4 5 6 7 8 | GLfloat xpos,ypos,zpos;
GLfloat xrot, yrot;
bool done;
bool active = TRUE;
bool fullscreen = TRUE;
GLfloat modif = 2.0f;
|
Mouse sensitivity (the smaller the more sensitive).
Enable or disable the test data output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | LRESULT CALLBACK WndProc( HWND , UINT , WPARAM , LPARAM );
void func1 ( int , int );
GLvoid BuildFont(GLvoid)
{
HFONT font;
HFONT oldfont;
fontbase = glGenLists(96);
font = CreateFont( -10,
0,
0,
0,
FW_BOLD,
FALSE,
FALSE,
FALSE,
ANSI_CHARSET,
OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY,
FF_DONTCARE|DEFAULT_PITCH,
"Courier New" );
oldfont = ( HFONT )SelectObject(hDC, font);
wglUseFontOutlines(hDC, 32, 96,fontbase,0.0f,0.0f,WGL_FONT_POLYGONS,gmf);
SelectObject(hDC, oldfont);
DeleteObject(font);
}
GLvoid KillFont(GLvoid)
{
glDeleteLists(fontbase, 96);
}
|
The buildfont and killfont functions, they are explained in lesson 13 and 14 of the NeHe Tuts.
1 2 | GLvoid glPrint(GLfloat xpos, GLfloat ypos, GLfloat zpos, GLfloat scale, char *string)
{
|
An adapted glPrint function, with scaling (making the font bigger or smaller).
1 2 3 4 5 6 7 8 9 | glPushAttrib(GL_LIST_BIT);
glPushMatrix();
glListBase(fontbase - 32);
glTranslatef(xpos,ypos,zpos);
glScalef(scale,scale,1.0f);
glCallLists( strlen (string), GL_UNSIGNED_BYTE, string);
glPopMatrix();
glPopAttrib();
}
|
This should be clear.
1 2 3 4 5 | void mousemove ( int x, int y)
{
xrot += (x-320)/modif;
yrot += (y-240)/modif;
}
|
The simplest mouse function there is, unfortunately, to simple to work
1 2 3 4 5 6 7 8 9 10 11 12 13 | void straferight ( int x, int y)
{
GLfloat cosx = cos (xrot * piover180);
xpos += STRIDE * cos (yrot * piover180) * cosx;
zpos += STRIDE * sin (yrot * piover180) * cosx;
}
void strafeleft ( int x, int y)
{
GLfloat cosx = cos (xrot * piover180);
xpos -= STRIDE * cos (yrot * piover180) * cosx;
zpos -= STRIDE * sin (yrot * piover180) * cosx;
}
|
Functions for strafe left and right, and no y movement (for the math, it isnÂ’t to hard to find that out, so, take a piece of paper and get it done)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void downmove( int x, int y)
{
GLfloat cosx = cos (xrot * piover180);
xpos += STRIDE * sin (yrot * piover180) * cosx;
ypos -= STRIDE * sin (xrot * piover180);
zpos += STRIDE * cos (yrot * piover180) * cosx;
}
void upmove( int x, int y)
{
GLfloat cosx = cos (xrot * piover180);
xpos -= STRIDE * sin (yrot * piover180) * cosx;
ypos += STRIDE * sin (xrot * piover180);
zpos -= STRIDE * cos (yrot * piover180) * cosx;
}
|
Moving forward and backward
1 2 | void MO_mousemove( int x, int y)
{
|
This is a more decent mouse function.
The keyword static might need some explanation: if a variable is declared static, it is allocated statically, so each time you call this function, the value is still there from the last run. The only way of removing it from memory is program termination. It has all the properties of a global variable, except that it is only visible in this function. The variable hold the position the cursor had when the function was called the last time.
1 2 | yrot += (MO_x - x) /modif;
xrot += (MO_y - y) /modif;
|
The x and y movements are calculated relative to the last time the function was used and dived by the speed modifier.
1 2 3 4 5 6 | if (x < 20 || x > 620 || y < 20 || y > 460)
{
MO_x = 320;
MO_y = 240;
SetCursorPos(320,240);
}
|
If the cursor approaches on of the borders of the window, reset it to the center.
1 2 3 4 5 6 | else
{
MO_x = x;
MO_y = y;
}
}
|
If not, store the current position for the next run.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | void stop( int x, int y)
{
done=TRUE;
}
void AN_act( int x, int y)
{
KC_SetMode(1);
}
void act( int x, int y)
{
ShowCursor( true );
KC_SetMode(0);
}
void modif_up( int x, int y)
{
modif += 0.1f;
}
void modif_down( int x, int y)
{
modif -= 0.1f;
}
void MO_act( int x, int y)
{
ShowCursor( false );
KC_SetMode(2);
}
void reset( int x, int y)
{
xrot = 0.0f;
yrot = 0.0f;
xpos = 0.0f;
ypos = 0.0f;
zpos = 0.0f;
}
|
Some more functions to be bound to the keys, most of them are self-explanatory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void oneshot( int x, int y)
{
static bool active = false ;
if (!active && x == 1)
{
active = true ;
test=!test;
}
if (x == 0)
{
active = false ;
}
}
|
This is the one shot function, it is used to ‘switch’ (holding the button only triggers it once). Once more, I use a static variable and not a global. It is not, like it might seem, set to false each time the function is triggered, but only when the program is started.
1 2 | void ED_act( int x, int y)
{
|
This one is tricky, it has itÂ’s own main loop, so seems to stop the engine and go on on itÂ’s own.
The mode is set to three, (an empty one, so no input is processed except the edit line)
1 2 | bool ED_done = false ;
char ED_string [30];
|
The two variables need by the editor: the string and the status.
1 | memset (ED_string,NULL,30);
|
Fill the string with 0 (otherwise it might do weird things).
1 | KC_Editor(ED_string,30,&ED_done);
|
Start the editor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | MSG msg;
while (!ED_done)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message==WM_QUIT)
{
done=TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glPrint(0.0f,0.0f,-20.0f,0.5f, ED_string);
SwapBuffers(hDC);
}
}
KC_SetMode(2);
}
|
The special main loop, going as long as the editor hasnÂ’t given an OK and printing out the string (see tutorial 1).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | GLvoid StartKC(GLvoid)
{
KC_SetMode(0);
KC_SetKey(VK_F1, &func1);
KC_SetKey(VK_UP, &upmove);
KC_SetKey(VK_DOWN, &downmove);
KC_SetKey(VK_LEFT, &strafeleft);
KC_SetKey(VK_RIGHT, &straferight);
KC_SetMouse(9, &mousemove);
KC_SetKey(VK_ESCAPE, &stop);
KC_SetKey( 'A' , &act);
KC_SetKey( 'Z' , &AN_act);
KC_SetKey( 'E' , &MO_act);
KC_SetKey( 'R' , &reset);
KC_SetKey( 'T' , &oneshot);
KC_SetKey( 'Y' , &ED_act);
KC_SetMode(1);
KC_SetKey(VK_F1, &func1);
KC_SetKey(VK_UP, &upmove);
KC_SetKey(VK_DOWN, &downmove);
KC_SetKey(VK_LEFT, &strafeleft);
KC_SetKey(VK_RIGHT, &straferight);
KC_SetMouse(9, &MO_mousemove);
KC_SetKey(VK_ESCAPE, &stop);
KC_SetKey( 'A' , &act);
KC_SetKey( 'Z' , &AN_act);
KC_SetKey( 'E' , &MO_act);
KC_SetKey( 'R' , &reset);
KC_SetKey( 'T' , &oneshot);
KC_SetMode(2);
KC_SetKey(VK_F1, &func1);
KC_SetKey(VK_UP, &modif_up);
KC_SetKey(VK_DOWN, &modif_down);
KC_SetMouse(9, &MO_mousemove);
KC_SetKey(VK_ESCAPE, &stop);
KC_SetKey( 'A' , &act);
KC_SetKey( 'Z' , &AN_act);
KC_SetKey( 'E' , &MO_act);
KC_SetKey( 'R' , &reset);
KC_SetKey( 'T' , &oneshot);
}
|
Registration of the keys and their functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 | int InitGL(GLvoid)
{
glEnable(GL_TEXTURE_2D);
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
StartKC();
BuildFont();
return TRUE;
}
|
Two lines must be inserted: StartKC(); and BuildFont();
1 2 3 4 | int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
|
Clear the buffers and reset view.
1 2 3 4 5 6 7 8 | if (test)
{
char str[50];
sprintf (str, "%d %f " , KC_GetMode(), modif);
glPrint(0.0f,0.0f,-10.0f,0.5f, str);
sprintf (str, "%f %f" , xrot, yrot);
glPrint(-2.0f,-2.0f,-10.0f,0.5f, str);
}
|
If test mode is on, print out the mode and mouse modification variable an under it the x and y rotation.
1 2 3 | glRotatef(-xrot,1.0,0.0,0.0);
glRotatef(-yrot,0.0,1.0,0.0);
glTranslatef(-xpos, -ypos, -zpos);
|
Transformations for 3D movement (all following code should move correct).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | glBegin(GL_LINES);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f(-10.0f, 0.0f , 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(10.0f, 0.0f , 0.0f);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(0.0f, -10.0f , 0.0f);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(0.0f, 10.0f , 0.0f);
glColor3f(1.0f,0.0f,1.0f);
glVertex3f(0.0f, 0.0f , -10.0f);
glColor3f(1.0f,1.0f,0.0f);
glVertex3f(0.0f, 0.0f , 10.0f);
glEnd();
|
A very simple cross indicating the axis.
1 2 3 | glColor3f(1.0f,0.0f,0.0f);
glPrint(0.0f,0.0f,0.0f,5.0f, "help" );
glPrint(0.0f,1.0f,12.0f,5.0f, "help2" );
|
Two times the word help, to see if you are moving upward down or not.
On the end of the KillGLWindow, the line.
Must be inserted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | void func1 ( int x, int y)
{
static bool active = false ;
if (!active && x == 1)
{
active = true ;
KillGLWindow();
fullscreen=!fullscreen;
CreateGLWindow( "NeHe's Texture Mapping Tutorial" ,640,480,16,fullscreen)
}
if (x == 0)
{
active = false ;
}
}
|
A switch function for the F1 key.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | LRESULT CALLBACK WndProc( HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_ACTIVATE:
{
if (!HIWORD(wParam))
{
active=TRUE;
}
else
{
active=FALSE;
}
return 0;
}
case WM_SYSCOMMAND:
{
switch (wParam)
{
case SC_SCREENSAVE:
case SC_MONITORPOWER:
return 0;
}
break ;
}
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
case WM_SIZE:
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));
return 0;
}
}
return KC_Parse(hWnd,uMsg,wParam,lParam);
}
|
the altered WndProc, passing all unprocessed message to the parser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
done=FALSE;
if (MessageBox(NULL, "Would You Like To Run In Fullscreen Mode?" , "Start FullScreen?" ,MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE;
}
if (!CreateGLWindow( "NeHe's Texture Mapping Tutorial" ,640,480,16,fullscreen))
{
return 0;
}
while (!done)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message==WM_QUIT)
{
done=TRUE;
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
if ((active && !DrawGLScene()))
{
done=TRUE;
}
else
{
SwapBuffers(hDC);
}
}
}
KillGLWindow();
return (msg.wParam);
}
|
The WinMain, no longer handling the keyboard.
So, to end, I would like to discuss some obvious modifications of the code:
-adepting the number of modes: just changing the array were it was declared. -single mode use: changing the array, the exec function and the SetKey and SetMouse functions, removing the curmod variable and the GetMode and SetMode. -usage as a class (when different modes have their own main loop and their own instance of the class): just make the static global variables private and non static (some might be used static, such as the nextproc) and make the functions public members.
Wouter De Borger
PS. The edit command always prints the key character as first character. This can be avoided in two ways. If you will never trigger the edit function with the mouse, the initial offset can be set to –1 and a new filter added before the if(ed_offset < ed_length)
1 2 3 4 5 | if (ed_offset < 0)
{
ed_offset++;
return 0;
}
|
The other solution is using postmessage(hWnd, WM_CHAR, VK_RETURN,0); after starting the editor.
* Download Code For This Article
|
|