Brass SSE Developer Guide - Tutorials
Tutorial 4 - Responding To User
Input
So far we have some text displayed on-screen, and
an understanding of how to use variables. Let's do something useful
and handle some user input too. Keep adding to the plugin code we've
created over the last few tutorials as we'll build on what we've
learned.
Download the source
for this tutorial
Handling User Input
Most plugins you write will need to respond to user
input in some way, for example loading a website or opening an application
when clicked. It's very easy to handle user input through, you guessed
it, an event handler.
There are lots of handlers for user input listed in
the Handler Wizardbar, but this time we're going to use the OnMouseLButtonUp
handler. As the name suggests, it's called when the user releases
the left mouse button in the plugin display panel.
Select OnMouseLButtonUp in
the Handler Wizardbar and click "Add Handler"
The first thing you'll notice about this handler is
it has two parameters, the X and Y position where the button was
released. These are relative to the top left corner of the display
panel, so a click 10 pixels in from the left and 20 from the top
would set iX to 10 and iY to 20. You can use the iX and iY parameters
as normal variables, as you'll see in a second.
Reacting to Input
The easiest way to test our handler is to display
something on-screen. Let's change our Render handler code so that
it displays an "X" wherever the mouse is clicked.
The first task is to get the X and Y position of the
click out of the OnMouseLButtonUp handler and into the Render handler.
The easiest and simplest way to do that is to make our PosX and
PosY variables global.
Move the 2 declare statements
for PosX and PosY to the top of the file and make them global
The top of your code should now look like this:
// The font used for displaying text
declare global MyFont font;
// The X and Y position of a click
declare global PosX int;
declare global PosY int;
// **********************************************
// Called once when the plugin is started.
// **********************************************
function Init()
{
Next we want to remove the 2 lines in the Render handler
that assign PosX and PosY the fixed values. We'll also change our
drawtext statement to draw an "X" instead of the test
string. Finally let's also change the setfontcol statement to set
the font to bright white - much easier to see.
Change your Render handler
code to the following:
function Render()
{
selectfont(MyFont);
setfontcol(MyFont, RGB(255, 255, 255));
drawtext("X", PosX, PosY);
}
Now all that's left to do is to set PosX and PosY
to the X and Y position of the mouseclick.
In the OnMouseLButtonUp handler,
add the following code:
function OnMouseLButtonUp(int iX, int iY)
{
PosX = iX;
PosY = iY;
}
All done. Build and test your plugin.
Click the "Compile" icon (
)on the toolbar
Click the "Execute" icon (
) on the toolbar
Uhoh, what happened?
This was a little test to see if you were paying attention.
You're going to run into this "bug" a lot, so remember
this!
No doubt you just tried clicking around in your display
panel and nothing happened. Confused? Don't be - all the code is
correct. The reason your plugin didn't appear to respond is because
the Render handler wasn't called. And as we know, the Render handler
is the only handler that may do any drawing, and it's only called
by Brass when your plugin must update itself.
There are a lot of technical reasons behind
this, but in a nutshell it's because Windows is smart. Windows
only requires applications to update their interface when it's
absolutely necessary, which makes things much faster than if
every application had to constantly redraw its interface.
What we need to do is to tell Brass to call our Render
handler so we can update the display panel. Fortunately that's extremely
easy, we simply use the redraw statement.
At the bottom of your OnMouseLButtonUp handler, add
the following code:
function OnMouseLButtonUp(int
iX, int iY)
{
PosX = iX;
PosY = iY;
redraw();
}
The redraw statement says to Brass, "I've just
made a change that needs to be updated in the display panel, redraw
me please".
If you make any change that means your
plugin must update its display, use the redraw statement once.
Calling redraw once at the end of a handler for all changes
is much better than calling it 3 times at different places in
the handler.
Build and test your code again and you'll see everything
works exactly as expected.
Why, why, why?
Right now you probably have one of two questions.
Here are the answers:
- Why do we have to manually
call redraw() - why can't Brass do
it automatically?
This one is a simple one. If Brass called redraw automatically
for every event handler your plugin would be constantly updating
itself for no reason. There are lots of good reasons why a plugin
sometimes won't want to respond to a mouseclick - maybe it displays
static text, or maybe its disabled part of the interface. Either
way if Brass just assumed a plugin needs to be redrawn all the time
it would be wrong. It's better to give you - the plugin developer
- the control over when your plugin should be redrawn than to wrongly
assume it must be constantly updated.
- Why can't we just call the
Render handler directly from the OnMouseLButtonUp handler?
Good question. The reason is the Brass and SSE framework.
To allow you to display anything you want in the plugin display
panel Brass must do a lot of background work to interface with Windows.
Brass only does this work when absolutely necessary - there's no
point setting up for drawing if we're in the Init handler. That's
why the Render handler is the only time you can draw to the display
panel - that's the only time Brass has prepared Windows to accept
your drawing.
There are a number of handlers that behave
in this way. The RegRead handler is a good example - it's the
only handler in which you can read your plugin's configuration
from the Registry, because it's the only place in which Brass
has prepared the Registry access system for you. We'll cover
this in more detail in a later tutorial
Starting Our Game
Now we have the basis for a game - displaying output and responding
to user input. We're going to code a tic-tac-toe game (or noughts
and crosses, if you're not from the USA).
There are 3 basic components to a tictactoe game - a grid, the
user input to select a position, and the "computer" input
to select a position as the other player. Let's start with the grid.
Here's a mockup of what we want it to look like:

The grid will start 1/4 of the way down the panel and finish 1/4
of the way from the bottom. It will stretch across almost the entire
width of the plugin which may make it look a bit out of proportion
if the panel is resized, but we'll accept that as a limitation for
now (don't worry, it's easy to fix).
Drawing the Grid
Before we start, remove all the code in your Render handler. Don't
delete the setup of MyFont in the Init handler or the global variable
as we'll use them later. We'll start with a clean and empty Render
handler for our drawing.
I'm going to use the C-style declarations
and assignment for our plugin. If you're more comfortable with
the VB-style declare system then feel free to use that instead.
The first requirement is that the grid is half the height of the
display panel, and begins 1/4 of the way down the panel. That pretty
clearly means we need to get the current dimensions of the display
panel. We do this with the getpanelwidth
and getpanelheight statements. These
statement take no parameters and simply return the current width
and height in pixels of the display panel.
In the empty Render handler,
add the following code:
int iWidth = getpanelwidth();
int iHeight = getpanelheight();
As you typed getpanelwidth you probably noticed the
popup menu that appeared:

If you've used any other IDE before you'll instantly
recognise this - SSEdit supports autocompletion (or Intellisense)
for your code. Simply press the down arrow key and hit return or
double click an item in the list to automatically insert it.
You use getpanelwidth() and getpanelheight()
to get the width and height of your plugin's display panel
Now that we have the width and height of the display
panel, let's calculate where our grid should start and how big it
should be.
Add the following code to
the Render handler:
int iGridTopX = 10;
int iGridWidth = iWidth - iGridTopX - 10;
int iGridTopY = iHeight / 4;
int iGridHeight = iHeight / 2;
This code simply calculates the position and size
of the grid for us. We set the X position of the grid as a fixed
10 pixels in from the left side of the plugin. We set the width
of the grid to the width of the display panel minus the 10 pixel
margin on the left, minus another 10 pixels for a margin on the
right.
For the Y position we simply divide the height of
the panel by 4 to calculate how many pixels are in 1/4 of the panel.
For the height we calculate how many pixels are in half the height
of the panel.
Remember, we must perform these calculations
in the Render handler so that our grid updates if the display
panel is resized.
We've made our calculations, let's draw the grid.
The Advanced Type: Pen
Whenever we want to draw lines in our display panel
we use a "pen". The pen specifies the style, colour and
width of the lines we want to draw. By default Brass will draw 1
pixel wide black lines for us (no pen needed), but we want thick
white lines for our grid. For that we need to set up a pen.
Setting up a pen is very similar to setting up a font.
Firstly we need to declare a variable of type pen
- we'll do this at global scope so we can perform the initialization
in the Init handler.
At the top of your source,
add the following code:
// The font used for
displaying text
declare global MyFont font;
// The X and Y position of a click
declare global PosX int;
declare global PosY int;
// The pen for the grid
declare global
GridPen pen;
Next we add the pen initialization to the Init
handler.
In the Init handler, add the
following code:
function Init()
{
createfont(MyFont, "Arial",
12);
createpen(GridPen,
0, 2, RGB(255, 255, 255));
}
The createpen statement has the following prototype:
createpen(variable
as pen type,
pen
style as integer,
RGB
colour as integer
);
The "pen style" is the only new parameter
here, and it allows you to control whether the pen is solid, hatched
or patterned. For now we specify 0 for the style parameter, which
is a solid pen.
If you're curious, at the end of this tutorial
when you have the code working try changing the style to 1,
2 or 3 and see what happens.
Finally we need to tell Brass to use our pen for the
grid, just like we do with fonts.
In the Render handler, add
the following code:
int iGridTopY = iHeight
/ 4;
int iGridHeight = iHeight / 2;
selectpen(GridPen);
That's the pen ready for use, now where were we...
Back to the Grid
To draw lines to the panel we use the drawline
statement. Here's its prototype:
drawline(StartX, StartY,
EndX, EndY);
We'll draw the vertical lines first. We know the width
and top of the grid so drawing 2 lines is nice and easy. We want
the lines to be evenly spaced so we'll calculate a quarter of the
width first.
Add the following code to
the Render handler:
selectpen(GridPen);
int iGridThirdW = iGridWidth / 3;
drawline(iGridTopX + iGridThirdW, iGridTopY, iGridTopX + iGridThirdW,
iGridTopY
+ iGridHeight);
drawline(iGridTopX + (iGridThirdW * 2), iGridTopY, iGridTopX
+
(iGridThirdW
* 2), iGridTopY + iGridHeight);
The first drawline statement draws the left vertical
line of our grid 1/3 of the way across, the second draws the right
line 2/3 of the way across.
Now we'll draw the horizontal lines.
Add the following code to
the Render handler:
int iGridThirdH = iGridHeight / 3;
drawline(iGridTopX, iGridTopY + iGridThirdH, iGridTopX + iGridWidth,
iGridTopY
+ iGridThirdH);
drawline(iGridTopX, iGridTopY + (iGridThirdH * 2), iGridTopX
+ iGridWidth,
iGridTopY
+ (iGridThirdH * 2));
That should be our grid drawn for us. Build and test
your code.
Click the "Compile" icon (
)on the toolbar
Click the "Execute" icon (
) on the toolbar
If everything worked as it should your display panel should look
like this:

A Quick Recap
We covered a lot in this tutorial, and we got the
basis of our tictactoe game working.
In this tutorial you saw:
- How to create a pen
- How to draw lines to the display
panel
- How to handle user input
- The principles behind scope
In the next tutorial we're going to start drawing
O's and X's on the grid in response to user input, and then we'll
start adding extra features like timers, winner tables and more!
|