Section 13 Home page Section 15

14. The Mouse: Appearance and Events

The mouse is an important device for interacting with GEM programs. In most cases, the AES manages how the mouse moves over the screen, triggers events in our windows, and selects menus. However, sometimes we need to have more control over the mouse. We may need to respond to mouse movement or clicks within our windows, and we may even want to change how the mouse cursor looks.

14.1. Mouse Appearance

GEM allows us to determine the mouse pointer’s appearance. The function that does this is:

graf_mouse (form, mouse_form);

form takes one of the values in the following list, and sets the mouse form:

// mouse forms

#define ARROW             0
#define TEXT_CRSR         1
#define HOURGLASS         2
#define BUSYBEE           2
#define POINT_HAND        3
#define FLAT_HAND         4
#define THIN_CROSS        5
#define THICK_CROSS       6
#define OUTLN_CROSS       7
#define USER_DEF        255
#define M_OFF           256     // <1>
#define M_ON            257     // <2>
  1. Used to turn the mouse off, e.g. when updating the display.

  2. Used to turn the mouse back on, e.g. after updating the display.

mouse_form is used for form USER_DEF, the user-defined mouse form. In most cases, we don’t use this argument, and can provide 0L as its value (as is done in draw_interior in the sample code). CManShip contains an example of creating a custom mouse form; see the code for chapter 12 at https://github.com/petercrlane/cmanship

14.2. Mouse Events

There are two kinds of events for the mouse which EvntMulti can respond to. The first is a click/double-click on the screen - the event type is MU_BUTTON. The second is when the mouse moves in or out of a region; up to two regions can be monitored at a time - the event types are MU_M1 and MU_M2.

The following code, from the Sketch example program, illustrates these three events. The code looks for a single mouse click down or release in the window, and whether the mouse is within the working area of the window or not. The mouse cursor is changed to a cross hair when within the working area.

        ev.ev_mflags = MU_BUTTON | MU_M1 | MU_M2 | MU_MESAG;    // <1>

        do {
                int event_type;
                int x, y, w, h;

                /* if we have not started a line, look for left button down
                   else look for left button up
                 */
                ev.ev_mbclicks = 1; /* look for a single click     <2> */
                ev.ev_bmask = 1; /* look for the left button */
                ev.ev_mbstate = (in_line ? 0 : 1); /* 0 if in line, is button up */

                /* set the dimensions of the window in M1 and M2 events
                   this must be done every loop, as window size may change
                 */
                wind_get (wd->handle, WF_WORKXYWH, &x, &y, &w, &h);
                ev.ev_mm1flags = false;                         // <3>
                ev.ev_mm1x = x;
                ev.ev_mm1y = y;
                ev.ev_mm1width = w;
                ev.ev_mm1height = h;
                ev.ev_mm2flags = true;                          // <4>
                ev.ev_mm2x = x;
                ev.ev_mm2y = y;
                ev.ev_mm2width = w;
                ev.ev_mm2height = h;

                event_type = EvntMulti (&ev);

                /* If left button clicked / released; start / draw line */
                if (event_type & MU_BUTTON) {                   // <5>
                        if (in_line) {
                                /* finished line, so draw line and start again */
                                pxy[2] = ev.ev_mmox;
                                pxy[3] = ev.ev_mmoy;
                                graf_mouse (M_OFF, 0L);
                                v_pline (app_handle, 2, pxy);
                                graf_mouse (M_ON, 0L);

                                in_line = false;
                        } else {
                                /* starting line, so record position */
                                pxy[0] = ev.ev_mmox;
                                pxy[1] = ev.ev_mmoy;
                                in_line = true;
                        }
                }

                /* If mouse entered our window, cursor to cross hair */
                if (event_type & MU_M1) {                       // <6>
                        graf_mouse (THIN_CROSS, 0L);
                }

                /* If mouse left our window, cursor to arrow */
                if (event_type & MU_M2) {                       // <7>
                        graf_mouse (ARROW, 0L);
                }

                if (event_type & MU_MESAG) {

                        // CODE TO HANDLE EVENTS

                }
        } while (ev.ev_mmgpbuf[0] != WM_CLOSED);
}
  1. The flags are set to respond to four different events

  2. Information for the MU_BUTTON event, waiting for a mouse button event. Here we look for a single click with the left button. We look for the button going down or the button coming up, depending on whether we are currently within a line.

  3. MU_M1 looks for mouse entering the work area of the window.

  4. MU_M2 looks for mouse leaving the work area of the window.

  5. Respond to mouse button event by either storing the line’s start, or drawing the line.

  6. Respond to MU_M1 event by setting mouse cursor to cross hair.

  7. Respond to MU_M2 event by setting mouse cursor to arrow.

For the MU_BUTTON events, which listen for the mouse button events, we must determine if we are listening for single or double clicks, and for one or both of the buttons.

  • ev_mbclicks is set to 1 for a single click, 2 for double click.

  • ev_bmask is the mask, to determine which button or buttons to listen to. Bit 0 is for the left button, and bit 1 for the right button. So set this to 1 for the left button, 2 for the right button, or 3 for both buttons.

  • ev_mbstate determines if we are listening for a mouse down (0) or a mouse up (1) event.

Once an MU_BUTTON event has occurred, we receive various information about the event:

  • ev_mmox is the x-coordinate of the mouse pointer

  • ev_mmoy is the y-coordinate of the mouse pointer

  • ev_mmobutton tells us which button(s) caused the event: bit 0 for the left button, bit 1 for the right button.

14.3. Sample Program: Sketch

For the sample program, we create a simple drawing program "Sketch". This provides a simple window, and is purely to illustrate the use of the mouse events. While the mouse is over the window, the pointer will show as a cross hair, returning to an arrow when outside its window. Holding the left button down will start a line; releasing the left button will draw the line on the screen. Note: there is no record maintained of the lines, so the drawn lines will disappear if you obscure the window. This program is also badly behaved, as it’s possible to draw lines outside its window.