1 /* 2 Widgets for the Midnight Commander 3 4 Copyright (C) 2016-2024 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 <http://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, /* */ 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) /* */ 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) /* */ 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 /* --------------------------------------------------------------------------------------------- */ 206 /*** public functions ****************************************************************************/ 207 /* --------------------------------------------------------------------------------------------- */ 208 209 /** 210 * Translate GPM event to high-level event and process it 211 * 212 * @param w Widget object 213 * @param event GPM event 214 * 215 * @return result of mouse event handling 216 */ 217 int 218 mouse_handle_event (Widget *w, Gpm_Event *event) /* */ 219 { 220 mouse_event_t me; 221 222 me = mouse_translate_event (w, event); 223 224 return mouse_process_event (w, &me); 225 } 226 227 /* --------------------------------------------------------------------------------------------- */