Mouse Programming with libgpm

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

As 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 Mouse

Whenever 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 Library

As 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.

______________________

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