1 /* Public Domain Curses */
2 
3 #include <curspriv.h>
4 
5 /*man-start**************************************************************
6 
7 mouse
8 -----
9 
10 ### Synopsis
11 
12     int mouse_set(unsigned long mbe);
13     int mouse_on(unsigned long mbe);
14     int mouse_off(unsigned long mbe);
15     int request_mouse_pos(void);
16     int map_button(unsigned long button);
17     void wmouse_position(WINDOW *win, int *y, int *x);
18     unsigned long getmouse(void);
19     unsigned long getbmap(void);
20 
21     int mouseinterval(int wait);
22     bool wenclose(const WINDOW *win, int y, int x);
23     bool wmouse_trafo(const WINDOW *win, int *y, int *x, bool to_screen);
24     bool mouse_trafo(int *y, int *x, bool to_screen);
25     mmask_t mousemask(mmask_t mask, mmask_t *oldmask);
26     int nc_getmouse(MEVENT *event);
27     int ungetmouse(MEVENT *event);
28 
29 ### Description
30 
31    As of PDCurses 3.0, there are two separate mouse interfaces: the
32    classic interface, which is based on the undocumented Sys V
33    mouse functions; and an ncurses-compatible interface. Both are
34    active at all times, and you can mix and match functions from
35    each, though it's not recommended. The ncurses interface is
36    essentially an emulation layer built on top of the classic
37    interface; it's here to allow easier porting of ncurses apps.
38 
39    The classic interface: mouse_set(), mouse_on(), mouse_off(),
40    request_mouse_pos(), map_button(), wmouse_position(),
41    getmouse(), and getbmap(). An application using this interface
42    would start by calling mouse_set() or mouse_on() with a non-zero
43    value, often ALL_MOUSE_EVENTS. Then it would check for a
44    KEY_MOUSE return from getch(). If found, it would call
45    request_mouse_pos() to get the current mouse status.
46 
47    mouse_set(), mouse_on() and mouse_off() are analagous to
48    attrset(), attron() and attroff().  These functions set the
49    mouse button events to trap.  The button masks used in these
50    functions are defined in curses.h and can be or'ed together.
51    They are the group of masks starting with BUTTON1_RELEASED.
52 
53    request_mouse_pos() requests curses to fill in the Mouse_status
54    structure with the current state of the mouse.
55 
56    map_button() enables the specified mouse action to activate the
57    Soft Label Keys if the action occurs over the area of the screen
58    where the Soft Label Keys are displayed.  The mouse actions are
59    defined in curses.h in the group that starts with BUTTON_RELEASED.
60 
61    wmouse_position() determines if the current mouse position is
62    within the window passed as an argument.  If the mouse is
63    outside the current window, -1 is returned in the y and x
64    arguments; otherwise the y and x coordinates of the mouse
65    (relative to the top left corner of the window) are returned in
66    y and x.
67 
68    getmouse() returns the current status of the trapped mouse
69    buttons as set by mouse_set() or mouse_on().
70 
71    getbmap() returns the current status of the button action used
72    to map a mouse action to the Soft Label Keys as set by the
73    map_button() function.
74 
75    The ncurses interface: mouseinterval(), wenclose(),
76    wmouse_trafo(), mouse_trafo(), mousemask(), nc_getmouse(), and
77    ungetmouse(). A typical application using this interface would
78    start by calling mousemask() with a non-zero value, often
79    ALL_MOUSE_EVENTS. Then it would check for a KEY_MOUSE return
80    from getch(). If found, it would call nc_getmouse() to get the
81    current mouse status.
82 
83    mouseinterval() sets the timeout for a mouse click. On all
84    current platforms, PDCurses receives mouse button press and
85    release events, but must synthesize click events. It does this
86    by checking whether a release event is queued up after a press
87    event. If it gets a press event, and there are no more events
88    waiting, it will wait for the timeout interval, then check again
89    for a release. A press followed by a release is reported as
90    BUTTON_CLICKED; otherwise it's passed through as BUTTON_PRESSED.
91    The default timeout is 150ms; valid values are 0 (no clicks
92    reported) through 1000ms. In x11, the timeout can also be set
93    via the clickPeriod resource. The return value from
94    mouseinterval() is the old timeout. To check the old value
95    without setting a new one, call it with a parameter of -1. Note
96    that although there's no classic equivalent for this function
97    (apart from the clickPeriod resource), the value set applies in
98    both interfaces.
99 
100    wenclose() reports whether the given screen-relative y, x
101    coordinates fall within the given window.
102 
103    wmouse_trafo() converts between screen-relative and window-
104    relative coordinates. A to_screen parameter of TRUE means to
105    convert from window to screen; otherwise the reverse. The
106    function returns FALSE if the coordinates aren't within the
107    window, or if any of the parameters are NULL. The coordinates
108    have been converted when the function returns TRUE.
109 
110    mouse_trafo() is the stdscr version of wmouse_trafo().
111 
112    mousemask() is nearly equivalent to mouse_set(), but instead of
113    OK/ERR, it returns the value of the mask after setting it. (This
114    isn't necessarily the same value passed in, since the mask could
115    be altered on some platforms.) And if the second parameter is a
116    non-null pointer, mousemask() stores the previous mask value
117    there. Also, since the ncurses interface doesn't work with
118    PDCurses' BUTTON_MOVED events, mousemask() filters them out.
119 
120    nc_getmouse() returns the current mouse status in an MEVENT
121    struct. This is equivalent to ncurses' getmouse(), renamed to
122    avoid conflict with PDCurses' getmouse(). But if you define
123    NCURSES_MOUSE_VERSION (preferably as 2) before including
124    curses.h, it defines getmouse() to nc_getmouse(), along with a
125    few other redefintions needed for compatibility with ncurses
126    code. nc_getmouse() calls request_mouse_pos(), which (not
127    getmouse()) is the classic equivalent.
128 
129    ungetmouse() is the mouse equivalent of ungetch(). However,
130    PDCurses doesn't maintain a queue of mouse events; only one can
131    be pushed back, and it can overwrite or be overwritten by real
132    mouse events.
133 
134 ### Portability
135                              X/Open    BSD    SYS V
136     mouse_set                   -       -      4.0
137     mouse_on                    -       -      4.0
138     mouse_off                   -       -      4.0
139     request_mouse_pos           -       -      4.0
140     map_button                  -       -      4.0
141     wmouse_position             -       -      4.0
142     getmouse                    -       -      4.0
143     getbmap                     -       -      4.0
144     mouseinterval               -       -       -
145     wenclose                    -       -       -
146     wmouse_trafo                -       -       -
147     mouse_trafo                 -       -       -
148     mousemask                   -       -       -
149     nc_getmouse                 -       -       -
150     ungetmouse                  -       -       -
151 
152 **man-end****************************************************************/
153 
154 #include <string.h>
155 
156 static bool ungot = FALSE;
157 
mouse_set(unsigned long mbe)158 int mouse_set(unsigned long mbe)
159 {
160     PDC_LOG(("mouse_set() - called: event %x\n", mbe));
161 
162     SP->_trap_mbe = mbe;
163     return PDC_mouse_set();
164 }
165 
mouse_on(unsigned long mbe)166 int mouse_on(unsigned long mbe)
167 {
168     PDC_LOG(("mouse_on() - called: event %x\n", mbe));
169 
170     SP->_trap_mbe |= mbe;
171     return PDC_mouse_set();
172 }
173 
mouse_off(unsigned long mbe)174 int mouse_off(unsigned long mbe)
175 {
176     PDC_LOG(("mouse_off() - called: event %x\n", mbe));
177 
178     SP->_trap_mbe &= ~mbe;
179     return PDC_mouse_set();
180 }
181 
map_button(unsigned long button)182 int map_button(unsigned long button)
183 {
184     PDC_LOG(("map_button() - called: button %x\n", button));
185 
186 /****************** this does nothing at the moment ***************/
187     SP->_map_mbe_to_key = button;
188 
189     return OK;
190 }
191 
request_mouse_pos(void)192 int request_mouse_pos(void)
193 {
194     PDC_LOG(("request_mouse_pos() - called\n"));
195 
196     Mouse_status = pdc_mouse_status;
197 
198     return OK;
199 }
200 
wmouse_position(WINDOW * win,int * y,int * x)201 void wmouse_position(WINDOW *win, int *y, int *x)
202 {
203     PDC_LOG(("wmouse_position() - called\n"));
204 
205     if (win && wenclose(win, MOUSE_Y_POS, MOUSE_X_POS))
206     {
207         if (y)
208             *y = MOUSE_Y_POS - win->_begy;
209         if (x)
210             *x = MOUSE_X_POS - win->_begx;
211     }
212     else
213     {
214         if (y)
215             *y = -1;
216         if (x)
217             *x = -1;
218     }
219 }
220 
getmouse(void)221 unsigned long getmouse(void)
222 {
223     PDC_LOG(("getmouse() - called\n"));
224 
225     return SP->_trap_mbe;
226 }
227 
getbmap(void)228 unsigned long getbmap(void)
229 {
230     PDC_LOG(("getbmap() - called\n"));
231 
232     return SP->_map_mbe_to_key;
233 }
234 
235 /* ncurses mouse interface */
236 
mouseinterval(int wait)237 int mouseinterval(int wait)
238 {
239     int old_wait;
240 
241     PDC_LOG(("mouseinterval() - called: %d\n", wait));
242 
243     old_wait = SP->mouse_wait;
244 
245     if (wait >= 0 && wait <= 1000)
246         SP->mouse_wait = wait;
247 
248     return old_wait;
249 }
250 
wenclose(const WINDOW * win,int y,int x)251 bool wenclose(const WINDOW *win, int y, int x)
252 {
253     PDC_LOG(("wenclose() - called: %p %d %d\n", win, y, x));
254 
255     return (win && y >= win->_begy && y < win->_begy + win->_maxy
256                 && x >= win->_begx && x < win->_begx + win->_maxx);
257 }
258 
wmouse_trafo(const WINDOW * win,int * y,int * x,bool to_screen)259 bool wmouse_trafo(const WINDOW *win, int *y, int *x, bool to_screen)
260 {
261     int newy, newx;
262 
263     PDC_LOG(("wmouse_trafo() - called\n"));
264 
265     if (!win || !y || !x)
266         return FALSE;
267 
268     newy = *y;
269     newx = *x;
270 
271     if (to_screen)
272     {
273         newy += win->_begy;
274         newx += win->_begx;
275 
276         if (!wenclose(win, newy, newx))
277             return FALSE;
278     }
279     else
280     {
281         if (wenclose(win, newy, newx))
282         {
283             newy -= win->_begy;
284             newx -= win->_begx;
285         }
286         else
287             return FALSE;
288     }
289 
290     *y = newy;
291     *x = newx;
292 
293     return TRUE;
294 }
295 
mouse_trafo(int * y,int * x,bool to_screen)296 bool mouse_trafo(int *y, int *x, bool to_screen)
297 {
298     PDC_LOG(("mouse_trafo() - called\n"));
299 
300     return wmouse_trafo(stdscr, y, x, to_screen);
301 }
302 
mousemask(mmask_t mask,mmask_t * oldmask)303 mmask_t mousemask(mmask_t mask, mmask_t *oldmask)
304 {
305     PDC_LOG(("mousemask() - called\n"));
306 
307     if (oldmask)
308         *oldmask = SP->_trap_mbe;
309 
310     /* The ncurses interface doesn't work with our move events, so
311        filter them here */
312 
313     mask &= ~(BUTTON1_MOVED | BUTTON2_MOVED | BUTTON3_MOVED);
314 
315     mouse_set(mask);
316 
317     return SP->_trap_mbe;
318 }
319 
nc_getmouse(MEVENT * event)320 int nc_getmouse(MEVENT *event)
321 {
322     int i;
323     mmask_t bstate = 0;
324 
325     PDC_LOG(("nc_getmouse() - called\n"));
326 
327     if (!event)
328         return ERR;
329 
330     ungot = FALSE;
331 
332     request_mouse_pos();
333 
334     event->id = 0;
335 
336     event->x = Mouse_status.x;
337     event->y = Mouse_status.y;
338     event->z = 0;
339 
340     for (i = 0; i < 3; i++)
341     {
342         if (Mouse_status.changes & (1 << i))
343         {
344             int shf = i * 5;
345             short button = Mouse_status.button[i] & BUTTON_ACTION_MASK;
346 
347             if (button == BUTTON_RELEASED)
348                 bstate |= (BUTTON1_RELEASED << shf);
349             else if (button == BUTTON_PRESSED)
350                 bstate |= (BUTTON1_PRESSED << shf);
351             else if (button == BUTTON_CLICKED)
352                 bstate |= (BUTTON1_CLICKED << shf);
353             else if (button == BUTTON_DOUBLE_CLICKED)
354                 bstate |= (BUTTON1_DOUBLE_CLICKED << shf);
355             else if (button == BUTTON_TRIPLE_CLICKED)
356                 bstate |= (BUTTON1_TRIPLE_CLICKED << shf);
357 
358             button = Mouse_status.button[i] & BUTTON_MODIFIER_MASK;
359 
360             if (button & PDC_BUTTON_SHIFT)
361                 bstate |= BUTTON_MODIFIER_SHIFT;
362             if (button & PDC_BUTTON_CONTROL)
363                 bstate |= BUTTON_MODIFIER_CONTROL;
364             if (button & PDC_BUTTON_ALT)
365                 bstate |= BUTTON_MODIFIER_ALT;
366         }
367     }
368 
369     if (MOUSE_WHEEL_UP)
370         bstate |= BUTTON4_PRESSED;
371     else if (MOUSE_WHEEL_DOWN)
372         bstate |= BUTTON5_PRESSED;
373 
374     /* extra filter pass -- mainly for button modifiers */
375 
376     event->bstate = bstate & SP->_trap_mbe;
377 
378     return OK;
379 }
380 
ungetmouse(MEVENT * event)381 int ungetmouse(MEVENT *event)
382 {
383     int i;
384     unsigned long bstate;
385 
386     PDC_LOG(("ungetmouse() - called\n"));
387 
388     if (!event || ungot)
389         return ERR;
390 
391     ungot = TRUE;
392 
393     pdc_mouse_status.x = event->x;
394     pdc_mouse_status.y = event->y;
395 
396     pdc_mouse_status.changes = 0;
397     bstate = event->bstate;
398 
399     for (i = 0; i < 3; i++)
400     {
401         int shf = i * 5;
402         short button = 0;
403 
404         if (bstate & ((BUTTON1_RELEASED | BUTTON1_PRESSED |
405             BUTTON1_TRIPLE_CLICKED |
406             BUTTON1_CLICKED | BUTTON1_DOUBLE_CLICKED) << shf))
407         {
408             pdc_mouse_status.changes |= 1 << i;
409 
410             if (bstate & (BUTTON1_PRESSED << shf))
411                 button = BUTTON_PRESSED;
412             if (bstate & (BUTTON1_CLICKED << shf))
413                 button = BUTTON_CLICKED;
414             if (bstate & (BUTTON1_DOUBLE_CLICKED << shf))
415                 button = BUTTON_DOUBLE_CLICKED;
416             if (bstate & (BUTTON1_TRIPLE_CLICKED << shf))
417                 button = BUTTON_TRIPLE_CLICKED;
418 
419             if (bstate & BUTTON_MODIFIER_SHIFT)
420                 button |= PDC_BUTTON_SHIFT;
421             if (bstate & BUTTON_MODIFIER_CONTROL)
422                 button |= PDC_BUTTON_CONTROL;
423             if (bstate & BUTTON_MODIFIER_ALT)
424                 button |= PDC_BUTTON_ALT;
425         }
426 
427         pdc_mouse_status.button[i] = button;
428     }
429 
430     if (bstate & BUTTON4_PRESSED)
431         pdc_mouse_status.changes |= PDC_MOUSE_WHEEL_UP;
432     else if (bstate & BUTTON5_PRESSED)
433         pdc_mouse_status.changes |= PDC_MOUSE_WHEEL_DOWN;
434 
435     return ungetch(KEY_MOUSE);
436 }
437