1 /*
2 Widgets for the Midnight Commander
3
4 Copyright (C) 2016-2025
5 Free Software Foundation, Inc.
6
7 Authors:
8 Human beings.
9
10 This file is part of the Midnight Commander.
11
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
16
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26 /** \file mouse.c
27 * \brief Header: High-level mouse API
28 */
29
30 #include <config.h>
31
32 #include "lib/global.h"
33 #include "lib/widget.h"
34
35 #include "lib/widget/mouse.h"
36
37 /*** global variables ****************************************************************************/
38
39 /*** file scope macro definitions ****************************************************************/
40
41 /*** file scope type declarations ****************************************************************/
42
43 /*** forward declarations (file scope functions) *************************************************/
44
45 /*** file scope variables ************************************************************************/
46
47 /* --------------------------------------------------------------------------------------------- */
48 /*** file scope functions ************************************************************************/
49 /* --------------------------------------------------------------------------------------------- */
50
51 /**
52 * Constructs a mouse event structure.
53 *
54 * It receives a Gpm_Event event and translates it into a higher level protocol.
55 *
56 * Tip: for details on the C mouse API, see MC's lib/tty/mouse.h,
57 * or GPM's excellent 'info' manual:
58 *
59 * http://www.fifi.org/cgi-bin/info2www?(gpm)Event+Types
60 */
61 static void
62 init_mouse_event (mouse_event_t *event, mouse_msg_t msg, const Gpm_Event *global_gpm,
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
63 const Widget *w)
64 {
65 event->msg = msg;
66 event->x = global_gpm->x - w->rect.x - 1; // '-1' because Gpm_Event is 1-based.
67 event->y = global_gpm->y - w->rect.y - 1;
68 event->count = global_gpm->type & (GPM_SINGLE | GPM_DOUBLE | GPM_TRIPLE);
69 event->buttons = global_gpm->buttons;
70 event->result.abort = FALSE;
71 event->result.repeat = FALSE;
72 }
73
74 /* --------------------------------------------------------------------------------------------- */
75
76 /**
77 * Translate GPM event to high-level event,
78 *
79 * @param w Widget object
80 * @param event GPM event
81 *
82 * @return high level mouse event
83 */
84 static mouse_event_t
85 mouse_translate_event (Widget *w, Gpm_Event *event)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
86 {
87 gboolean in_widget;
88 mouse_msg_t msg = MSG_MOUSE_NONE;
89 mouse_event_t local;
90
91 /*
92 * Very special widgets may want to control area outside their bounds.
93 * For such widgets you will have to turn on the 'forced_capture' flag.
94 * You'll also need, in your mouse handler, to inform the system of
95 * events you want to pass on by setting 'event->result.abort' to TRUE.
96 */
97 in_widget = w->mouse.forced_capture || mouse_global_in_widget (event, w);
98
99 if ((event->type & GPM_DOWN) != 0)
100 {
101 if (in_widget)
102 {
103 if ((event->buttons & GPM_B_UP) != 0)
104 msg = MSG_MOUSE_SCROLL_UP;
105 else if ((event->buttons & GPM_B_DOWN) != 0)
106 msg = MSG_MOUSE_SCROLL_DOWN;
107 else
108 {
109 /* Handle normal buttons: anything but the mouse wheel's.
110 *
111 * (Note that turning on capturing for the mouse wheel
112 * buttons doesn't make sense as they don't generate a
113 * mouse_up event, which means we'd never get uncaptured.)
114 */
115 w->mouse.capture = TRUE;
116 msg = MSG_MOUSE_DOWN;
117
118 w->mouse.last_buttons_down = event->buttons;
119 }
120 }
121 }
122 else if ((event->type & GPM_UP) != 0)
123 {
124 /* We trigger the mouse_up event even when !in_widget. That's
125 * because, for example, a paint application should stop drawing
126 * lines when the button is released even outside the canvas. */
127 if (w->mouse.capture)
128 {
129 w->mouse.capture = FALSE;
130 msg = MSG_MOUSE_UP;
131
132 /*
133 * When using xterm, event->buttons reports the buttons' state
134 * after the event occurred (meaning that event->buttons is zero,
135 * because the mouse button is now released). When using GPM,
136 * however, that field reports the button(s) that was released.
137 *
138 * The following makes xterm behave effectively like GPM:
139 */
140 if (event->buttons == 0)
141 event->buttons = w->mouse.last_buttons_down;
142 }
143 }
144 else if ((event->type & GPM_DRAG) != 0)
145 {
146 if (w->mouse.capture)
147 msg = MSG_MOUSE_DRAG;
148 }
149 else if ((event->type & GPM_MOVE) != 0)
150 {
151 if (in_widget)
152 msg = MSG_MOUSE_MOVE;
153 }
154
155 init_mouse_event (&local, msg, event, w);
156
157 return local;
158 }
159
160 /* --------------------------------------------------------------------------------------------- */
161
162 /**
163 * Call widget mouse handler to process high-level mouse event.
164 *
165 * Besides sending to the widget the event itself, this function may also
166 * send one or more pseudo events. Currently, MSG_MOUSE_CLICK is the only
167 * pseudo event in existence but in the future (e.g., with the introduction
168 * of a drag-drop API) there may be more.
169 *
170 * @param w Widget object
171 * @param event high level mouse event
172 *
173 * @return result of mouse event handling
174 */
175 static int
176 mouse_process_event (Widget *w, mouse_event_t *event)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
177 {
178 int ret = MOU_UNHANDLED;
179
180 if (event->msg != MSG_MOUSE_NONE)
181 {
182 w->mouse_callback (w, event->msg, event);
183
184 /* If a widget aborts a MSG_MOUSE_DOWN, we uncapture it so it
185 * doesn't steal events from other widgets. */
186 if (event->msg == MSG_MOUSE_DOWN && event->result.abort)
187 w->mouse.capture = FALSE;
188
189 /* Upon releasing the mouse button: if the mouse hasn't been dragged
190 * since the MSG_MOUSE_DOWN, we also trigger a click. */
191 if (event->msg == MSG_MOUSE_UP && w->mouse.last_msg == MSG_MOUSE_DOWN)
192 w->mouse_callback (w, MSG_MOUSE_CLICK, event);
193
194 // Record the current event type for the benefit of the next event.
195 w->mouse.last_msg = event->msg;
196
197 if (!event->result.abort)
198 ret = event->result.repeat ? MOU_REPEAT : MOU_NORMAL;
199 }
200
201 return ret;
202 }
203
204 /* --------------------------------------------------------------------------------------------- */
205 /*** public functions ****************************************************************************/
206 /* --------------------------------------------------------------------------------------------- */
207
208 /**
209 * Translate GPM event to high-level event and process it
210 *
211 * @param w Widget object
212 * @param event GPM event
213 *
214 * @return result of mouse event handling
215 */
216 int
217 mouse_handle_event (Widget *w, Gpm_Event *event)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
218 {
219 mouse_event_t me;
220
221 me = mouse_translate_event (w, event);
222
223 return mouse_process_event (w, &me);
224 }
225
226 /* --------------------------------------------------------------------------------------------- */