Quantcast
Username/Email:  Password: 

Mouse Programming with libgpm

A description of how to work with gpm for event handling, menus and more.

When I was first introduced to
programming (back with DOS), I used to add mouse support to my
applications to impress my teachers. Programming the mouse largely
consisted of putting the right values in the registers (like 0x00
in the ax register to reset the mouse) and calling the interrupt
service routine with int86 ( a function provided by Turbo C to
access interrupt service routines). So when I put my hands on a
Linux box, I looked for some thing similar. I soon found out that
only root can access hardware registers directly. But there's an
elegant way to handle this.Welcome to the world of gpm and its client libraries. When I
started searching for useful information about how to program
mouses in UNIX (Linux in particular), I found some C code that let
people access and program the mouse through /dev/mouse . But I was
overwhelmed by the intricacies of handling different mice and was
forced to look for other avenues. After pouring over the man pages
of gpm, I learned to program the
mouse with ease, and in no time, I was playing with my mouse. I was
quite impressed by the extent of services these libraries provided
and was compelled to explore them further.It is quite easy to program with gpm and write portable and
robust applications with a few lines of code. In this article, I
will explain the concepts involved in programming the mouse with
simple but effective examples. To compile them you will need
libgpm, which can be obtained from
ftp.prosa.it/pub/gpm.
To compile the program successfully, you will have to add the -lgpm
flag:gcc [a c file that uses libgpm]
-lgpmSome programs require
ncurses, which can be obtained
from
ftp.gnu.org/pub/gnu/ncurses.
The programs that require curses have to be compiled with the
-lncurses flag:gcc [a c file which uses both libgpm and
libncurses] -lgpm -lncursesGPM (General Purpose Mouse)GPM (General Purpose Mouse): A Cut an Paste Utility
and Mouser server for Linux Virtual Consoles.
These are the first lines of the man page on gpm. To put it
simply gpm is a server that says "Hey! I know about the mouse
installed here. I know how to handle its events. And I am here to
serve you with whatever you are interested in." The clients simply
have to connect to the server and inform it of which events they
are interested in. They need not worry about the intricacies of the
different types of mice. This client-server philosophy is put to
its best in Unices. X is a very good example of how this
architecture is put to useful ends. The server knows about the
hardware, and it will handle all the dirty hardware interrupts,
registers, etc. What the clients see is a unified interface that
they can use to interact with the hardware. Henceforth, I will use
the term gpm to refer to gpm and its libraries
and the term server to refer to the gpm
dæmon.Simple ExampleTo handle the mouse with gpm, the client has to connect to
the server and specify which events it wants to handle. The
function Gpm_Open() takes a structure of type Gpm_Connect, whose
variables have to be filled accordingly. The variables of interest
in the Gpm_Connect structure are the eventMask, defaultMask, minMod
and maxMod. The variable eventMask is a bitmask of the events the
client is interested in. defaultMask specifies which events the
client wants the server to handle in the default manner. Thus, we
can explicitly specify the events we want to handle and tell the
server to handle the others events in the default manner.The variables minMod and maxMod can be used to access
keyboard modifiers like ALT, CTRL and SHIFT. The client will not be
informed about mouse events that are less than the set minimum and
more than the set maximum. The following code explains these
concepts and also shows how to read events.

#include <stdio.h>
#include <gpm.h>
int my_handler(Gpm_Event *event, void *data)
{       printf("Event Type : %d at x=%d y=%d\n", event->type, event->x, event->y);
        return 0;       
}
int main()
{       Gpm_Connect conn;
        int c;
        conn.eventMask  = ~0;   /* Want to know about all the events */
        conn.defaultMask = 0;   /* don't handle anything by default  */
        conn.minMod     = 0;    /* want everything                   */  
        conn.maxMod     = ~0;   /* all modifiers included            */
        
        if(Gpm_Open(&conn, 0) == -1)
                printf("Cannot connect to mouse server\n");
        
        gpm_handler = my_handler;
        while((c = Gpm_Getc(stdin)) != EOF)
                printf("%c", c);
        Gpm_Close();
        return 0;
}

This example introduces us to a useful function, Gpm_Getc(),
that works like the ordinary getch() function, except that when a
mouse event occurs it calls the gpm_handler with the event
structure as a parameter. gpm_handler is a global variable
initialized to null. Clients are required to initialize this
variable with the function that defines the way they wish to handle
events.Now that we have seen how easy it is to get mouse events with
gpm, let's dive into the details. The variables in the connection
structure can be manipulated to handle only the events of our
choice. For example, we don't want to handle motion and drag of the
mouse and are interested only in the clicks of the mouse. Then we
can use an eventMask like conn.eventMask =
Gpm_Down;
and a conn.defaultMask =
~conn.eventMask;
. This will only report the mouse button
down events to us. Similarly a prudent choice of minMod and maxMod
can simplify programming and reduce overhead. It is also useful if
you want to pass on the events to other processes.If we set minMod and maxMod to zero, only mouse events are
reported. If we are interested in only mouse + modifier events and
not in mouse-only events, then minMod should be a combination
ofconn.minMod = 1<<KG_SHIFT; conn.maxMod =
~0;
which tells the server that the client is interested in shift
+ mouse events but not interested in mouse-only events. This is how
gpm-root works. gpm-root takes only the events with modifiers (by
default CTRL only) and draws menus accordingly. But we have to
remember that gpm-root knows about these events only if the
application running on the console is not handling the events or if
it has passed them to the default handler. That is why choosing the
correct set of events is very important.Note: You must include linux/keyboard.h to make use of the
define constants KG_SHIFT, etc.More about Gpm_EventAs you have seen in the above example, the variable event
contains details about the event that gpm reports to us. If we look
at the event structure, the variables that are of the most interest
to us are type, buttons, modifiers, x and y.The variable type contains the type of the events. This is a
bitmask of the various possible events, such as move, drag, click,
down, up, etc. To find out about the event, we can test against the
enum types GPM_MOVE, GPM_DOWN, GPM_UP, etc., using an expression
like

        if(event->type & GPM_MOVE)
                printf("Move event has occured\n");

Similarly the variables buttons and modifiers contain
information regarding which button(s) and which modifier(s) are
pressed.Listing 1. Use of the Variables in
the Event Structure
Gpm_GetEvent() vs Gpm_Getc()There is one more useful function to get events:
Gpm_GetEvent() takes a parameter of type Gpm_Event and sets the
variables of the structure when an event is reported. The main
difference between these two functions, as mentioned in the man
page, is that Gpm_GetEvent() tries to read the event with a read
call; so it immediately blocks if there is no event to be reported.
Hence it should be called only when gpm_fd (mouse file descriptor)
is reported readable by select(). Gpm_Getc() will do this work for
you, and it calls the handler when an event is reported. So what is
the main reason to use Gpm_GetEvent()? Though this function is
meant to be used for internal purposes by the GPM client library,
if you really want to block for events with the mouse and don't
want to do anything else, then you could go for this. Also, if you
want a quick and dirty way of reading events without the hassle of
handler functions, Gpm_GetEvent() function may be used.Handling Menus with the MouseWhenever we think of a GUI or a mouse-sensitive application,
menus, dialog boxes and buttons spring to mind. Managing GUI
widgets (menus, dialog boxes, etc.) is always difficult and runs
into many lines of code, especially those weird switch-case
statements. With gpm and curses I tried to create a simple menu; it
is shown in Listing 2.Listing 2. Creating a Menu that
Highlights with the Mouse Pointer
Listing 2 creates a menu, and whenever the mouse passes over
one option it gets highlighted. When the mouse is clicked on one
choice, the choice is reported. This example illustrates the
concept of "faking the keys". Generally the mouse is considered to
be a shortcut or an easy way of handling the difficult task of
remembering key combinations. In the handler we can return values
that may or may not be equivalent to key codes, so that we can use
the same switch case statement to perform functions. As an example,
say a user has clicked on a menu item equivalent to the F1 key,
then we can return KEY_F1. We can use a single switch statement in
the main loop to do the appropriate work. Though this is not a
requirement it does simplify coding and debugging. I often attach
mouse support to an existing curses application by using this
feature. Dialog is a good example of how we can put this to use
though exact values are not returned in this case.The return value from the handler is interpreted as follows
by the Gpm_Getc() family of functions.

  • EOF: Signals a fatal error and returns the same
    value to the caller and sets gpm_hflag to 1.
  • 0: A zero return value means that the input
    function should go on as before, without returning to the caller.
    The event is considered eaten by the handler and no key-press is
    simulated.
  • Anything Else: Considered a simulated character,
    this feature can be used to implement the fake keys mechanism. It
    also sets the global variable gpm_hflag to indicate that the key
    press is simulated; that is why we tested this flag against
    1.

When programming like this, we can return the same key code
that a keyboard generates, or we can pack it into a value so that
the main loop knows the mouse is generating this event. I used the
second approach in the above example. Personally, I prefer using
the first approach when I want to add functionality to an existing
curses application with a lot of manipulation through keys. But the
second approach gives more freedom (and sometimes it gives bizarre
results too).High Level LibraryAs the complexity of the programs and the GUI increases, it
becomes difficult to handle different regions and complex widgets.
The GPM high level library tries to address this problem. Functions
are available to manage regions cleanly and easily. Let's see them
in action in Listing 3.Listing 3. Sample High Level
Library
Listing 3 illustrates the main concepts of the high level
library. In this example, four windows are created and mouse
regions are also initialized. When the user does something in the
region, the event is reported in the window. The high level library
uses the concept of ROIs (Region of Interest). We tell the library
the region we are interested in, and the library informs us when an
event occurs in the region. The function Gpm_PushRoi() does this.
We can arrange for the handler to be called when some event happens
in the region and we can specify the usual mask of events in which
we are interested.In the example in Listing 3, I used a single handler. By
making use of the client data, I kept track of the window in which
I was working. When we initialize the ROI with Gpm_PushRoi(), the
last parameter to the function is the client data which will be
passed later to the handler. So we can store useful information in
the client data and then use it in the handler.In the above example I passed the window information required
to the handler so that it could prints the messages in the correct
window. This simplifies programming and makes management of regions
easy. The events GPM_ENTER and GPM_LEAVE tell us when the mouse is
coming into or going out of the region. These can be used to do
pre-processing or post-processing for the region.Say we have written a menu and want it to be displayed only
when the user passes over a region and then disappear when the user
leaves the region. The function Gpm_PopRoi() removes the Roi on the
top of the stack. This function can be called when we are no longer
interested in the region. The functions Gpm_RaiseRoi() and
Gpm_LowerRoi() raise and lower the specified roi in the stack.
These functions are useful when we want to keep track of which roi
is on top of the stack. Whenever the mouse generates an event in a
region it will be reported, even though it is not on top of stack.
So these functions are useful when we want to do something special
only with the Roi on the top of the stack or with a specific roi.
The hltest program that comes with the gpm code uses this concept
to make the window on the top of the stack active and handle events
related to that window only.Global VariablesLet's look at some global variables that are essential in any
program using gpm libraries. I have used some of them in the above
examples, so let me explain some important ones here. The
explanations also contain tricks to make some programs work.

  • gpm_fd: This is the file descriptor used to connect
    to the server. Though we may never need to use it directly, this is
    useful in few occasions. The gpm_fd will be -2 if we run the
    programs in an xterm. It is useful to diagnose whether the program
    is running in an xterm or on the console. Another use is to select
    it and wait for input. We can fire select call on gpm_fd to see
    when the data becomes available and then use Gpm_GetEvent() to get
    events.
  • gpm_zerobased: This variable can be set to 1 to
    force gpm to return zero-based coordinates of the mouse. It is
    off(0) by default.
  • gpm_visiblepointer: This variable can be set to 1
    to force the pointer to be visible all the time. The pointer
    becomes invisible when keyboard focus is on. Especially when curses
    are initialized, the pointer becomes invisible. So we need to force
    gpm to show the pointer by setting this variable to 1.
  • gpm_hflag & gpm_morekeys: gpm_hflag is used to
    decide if the key code generated is from the mouse or not. It is
    useful when generating key codes for mouse events. gpm_morekeys is
    useful when we want to generate more than one key code for the
    event. When this variable is set, the mouse handler is invoked
    again for the event.
  • gpm_roi: This is the linked list of the regions in
    the stack; it's a pointer to the top. With this variable we can do
    lot of weird things any time, any where.
  • gmp_current_roi: This is the pointer to the region
    that received the last event. I usually use this for debugging
    purposes. While interested in regions on only the top of the stack,
    I always want to know where the mouse pointer is wandering. This
    variable comes to my rescue for such applications.

AcknowledgementI would like to thank my friend Ravi Parimi for his help in
editing this document. I am grateful to him for his constant
encouragement and motivation.Pradeep Padala is a software
engineer for Hughes Software Systems. He has a degree in Computer
Science from MNREC, Allahabad, India.

email: p_padala@yahoo.com

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Mouse programming

ajayan's picture

really good stuff ,
thanks to author , i got this at right time

Re: Mouse Programming with libgpm

Anonymous's picture

I tried the sample progam but the #includes on

the website did not appear on my netscape.

So I used the obvious includes and it worked

just fine. Here are the includes you need

without angle brackets that this web site can't

digest. (Webmaster, please work on accepting

plain text in comments, we're not all MS junkies

here).

stdio.h

stdlib.h

ncurses.h

gpm.h

However, after exiting, the sample program, I

could not cut and paste as before to or from that

one xterm window. All other windows, xterm or

GUI, were unaffected. I don't know what this

means but I know it works in Xfree86 using Gnome.

I assume that means it works as well on the CRT

80x24 screen as well.

This might be something to follow up on for

anybody writing sam and smit type console programs

for linux administration. I know I hate using the

GUI with pictures, where you don't know if the

little birdie icon means add or delete. I like

the text based GUI that spells it out so you know

what you get when you press return, or maybe, now,

if you click on the highlighted text.

Re: Mouse Programming with libgpm

Anonymous's picture

Which sample program you had problems? I checked all of them on plain console and I have no problems

--pradeep

Re: Mouse Programming with libgpm

Anonymous's picture

This is that code.... after exiting .... just see whether cut copy paste with mouse works ... on the same xterm where u executed this program .

#include
#include
#include
#include

int my_handler(Gpm_Event *event, void *data)
{ printf("Event Type : %d at x=%d y=%d
", event->type, event->x, event->y);
return 0;
}

int main()
{ Gpm_Connect conn;
int c;

conn.eventMask = ~0; /* Want to know about all the events */
conn.defaultMask = 0; /* don't handle anything by default */
conn.minMod = 0; /* want everything */
conn.maxMod = ~0; /* all modifiers included */

if(Gpm_Open(&conn, 0) == -1)
printf("Cannot connect to mouse server
");

gpm_handler = my_handler;
while((c = Gpm_Getc(stdin)) != EOF)
printf("%c", c);
Gpm_Close();
return 0;
}

How to capture special keys like F1 F2 etc...

suneel's picture

Gpm_Getc does not appear to be returning F1 F2... etc other keystrokes. Do u know how to capture those apart fromthis mouse and regular key strokes.

Suneel

Post new comment

  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <pre> <ul> <ol> <li> <dl> <dt> <dd> <i> <b>
  • Lines and paragraphs break automatically.
  • Use to create page breaks.

More information about formatting options