Writing a Mouse-Sensitive Application
The Linux text console is more than a bare terminal. One of its most important features is the availability of a mouse device. In addition to supporting selection, the mouse can be used to interact with user programs. If your computer runs the gpm server your programs can easily benefit from mouse availability under both the Linux console and xterm, and run without complaint under other environments.
The main difference between a conventional text program and a mouse-sensitive application is the way they process input—while the former reads from stdin and writes to stdout, the latter must multiplex input from different sources—we can call it an “event-driven” application. I will consistently use “program” to refer to a stdin-driven process and “application” to refer to an event-driven one.
The gpm client library is meant to allow programmers to easily turn a program into an application by changing only a few lines of original source code. Alternatively, it offers complete support to developers designing an application from scratch. Portability is a major issue here, because you can be tempted to build a nice full-featured application for the Linux console, which reveals itself as completely unusable when you remotely log in to your PC from within an xterm or a bare vt100. Some care must be taken to avoid this, since a networked Linux computer can easily be used from a tty which has nothing to do with its own console.
The internal structure of a console application is represented in Figure 1, which outlines what changes required in the original program so it can respond to mouse events. As you can see, all mouse support code could be hidden in a separate module, and mouse-related code in the main body of the source is limited to the following calls:
Gpm_Open() The open function should be called before reading any input. It is used to connect to the daemon's socket and performs all the setup needed to get back events from the gpm daemon.
Gpm_Getc() Any call to getc() and to getch() should be replaced by the Gpm_-prefixed function. The replacement code manages multiple inputs and dispatches mouse events as needed—more on this later.
Gpm_Close() Before exit()ing, the mouse connection should be closed. This call may be omitted, though it isn't nice to do so.
When writing portable code, these few modifications could be masked out as suggested in the code fragment below. Its role is to define function names which are independent of mouse availability. Such preprocessor-specific code would better reside in a header file, to avoid ugly #ifdefs in the actual source. The approach of choice is to hide Gpm_Open in local_mouse_init, because setting up is more than a function call; conversely, local_mouse_close is a syntactic place holder.
Any other code referring to the mouse can be put in a different source file from the general application code. A correct Makefile (possibly through autoconf) can easily choose which files need to be compiled and which preprocessor defines are needed, without cluttering the code with #ifdef/#endif.
#ifdef CFG_MOUSE<\n> # define local_wgetch(w) Gpm_Wgetch(w)<\n> extern int local_mouse_init(void);<\n> # define local_mouse_close() Gpm_Close()<\n> #else<\n> # define local_wgetch(w) wgetch(w)<\n> # define local_mouse_init() /* nothing */<\n> # define local_mouse_close() /* nothing */<\n> #endif
Choosing a good connection with your mouse device is tricky. The problem is getting the best event resolution while avoiding excessive of context switches. Some simple applications need to be told of only button-press events and can leave cursor-drawing to the server program; more complex applications, on the contrary, might want to be told of any single movement of the mouse, as well as button-press and button-release.
The Gpm_Open function gets as an argument a structure identifying the type of connection requested. The type of connection in turn is characterized by event masks—bitmaps identifying event types. With gpm, two mask are required—the mask of events you want to get and the mask of events you want to be handled in the default way.
The double mask is useful, because the default way is known. In particular, since you know that mouse motions cause the cursor to be drawn, you may often leave motion events to the default management, thus relieving your application of most of the work of handling the mouse.
In addition to event masks, the connecting application must specify two “modifier sets”, that is, sets of keyboard modifiers, such as shift, control, meta (alt) and so on. Within the gpm server, keyboard modifiers are used to multiplex applications on a single console. It is handy to be able to paste selected text in a mouse-sensitive application, while an application taking complete control of the user's pointer would irritate most of the customers.
Each gpm client is asked to specify a “minimum set” and a “maximum set”. The client specifically asks not to be informed about mouse events with less than the minimum set or more than the maximum set of attached modifiers. The minimum set will be 0 for most clients. The gpm-root menu drawer is a client with non-0 minimum mask. This gives selection mouse-only events when there is no other client. Thus, when running Emacs, you can use the Emacs mouse facility (by loading the library t-mouse.el, within the gpm distribution) and have access to selection and gpm-root; the lisp package accepts mouse-only and alt-mouse, the gpm-root server gets ctrl-mouse, and the internal selection mechanism gets any other events. Within this scheme, selection is a catch-all, as if it had an infinite maximum-modifiers mask.
Gpm_Open, then, keeps a stack of connection masks, so you can reopen the connection to modify your mask and get back to the previous behaviour on the next Gpm_Close invocation. This feature can be used to either increase or decrease the amount of events you get. Emacs, for example, disables event reporting in this way when it is stopped, to allow you to use selection normally with your shell. An application drawing a menu, by contrast, can only reopen the connection to get motion events while the menu is kept down. This stack-like feature is managed in the client library, so that misbehaving applications can't lock up the server.
|Designing Electronics with Linux||May 22, 2013|
|Dynamic DNS—an Object Lesson in Problem Solving||May 21, 2013|
|Using Salt Stack and Vagrant for Drupal Development||May 20, 2013|
|Making Linux and Android Get Along (It's Not as Hard as It Sounds)||May 16, 2013|
|Drupal Is a Framework: Why Everyone Needs to Understand This||May 15, 2013|
|Home, My Backup Data Center||May 13, 2013|
- Designing Electronics with Linux
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Dynamic DNS—an Object Lesson in Problem Solving
- Using Salt Stack and Vagrant for Drupal Development
- Validate an E-Mail Address with PHP, the Right Way
- Tech Tip: Really Simple HTTP Server with Python
- Build a Skype Server for Your Home Phone System
- Why Python?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Reply to comment | Linux Journal
21 min 41 sec ago
- Reply to comment | Linux Journal
1 hour 11 min ago
- Not free anymore
5 hours 13 min ago
9 hours 58 sec ago
- Reply to comment | Linux Journal
9 hours 8 min ago
- Understanding the Linux Kernel
11 hours 23 min ago
13 hours 53 min ago
- Kernel Problem
23 hours 56 min ago
- BASH script to log IPs on public web server
1 day 4 hours ago
1 day 7 hours ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi
It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?