Backport support for mouse buttons 4 + 5 (#1076, #1068)

This is a backport of PR 1081 by @Albrecht-S from master to branch-1.3.
This commit is contained in:
Adam Halim
2024-10-03 11:10:42 +02:00
committed by Albrecht Schlosser
parent 3aaab84e40
commit f83c0042f3
6 changed files with 201 additions and 86 deletions
+17 -8
View File
@@ -535,9 +535,11 @@ enum Fl_When { // Fl_Widget::when():
/*@{*/ /*@{*/
#define FL_LEFT_MOUSE 1 ///< The left mouse button #define FL_LEFT_MOUSE 1 ///< The left mouse button
#define FL_MIDDLE_MOUSE 2 ///< The middle mouse button #define FL_MIDDLE_MOUSE 2 ///< The middle mouse button
#define FL_RIGHT_MOUSE 3 ///< The right mouse button #define FL_RIGHT_MOUSE 3 ///< The right mouse button
#define FL_BACK_MOUSE 4 ///< The back mouse button (side button 1)
#define FL_FORWARD_MOUSE 5 ///< The forward mouse button (side button 2)
/*@}*/ // group: Mouse Buttons /*@}*/ // group: Mouse Buttons
@@ -562,11 +564,18 @@ enum Fl_When { // Fl_Widget::when():
// correct for XFree86 // correct for XFree86
#define FL_SCROLL_LOCK 0x00800000 ///< The scroll lock is on #define FL_SCROLL_LOCK 0x00800000 ///< The scroll lock is on
// correct for XFree86 // correct for XFree86
#define FL_BUTTON1 0x01000000 ///< Mouse button 1 is pushed
#define FL_BUTTON2 0x02000000 ///< Mouse button 2 is pushed // Mouse buttons
#define FL_BUTTON3 0x04000000 ///< Mouse button 3 is pushed
#define FL_BUTTONS 0x7f000000 ///< Any mouse button is pushed #define FL_BUTTON1 0x01000000 ///< Mouse button 1 is pushed (L)
#define FL_BUTTON(n) (0x00800000<<(n)) ///< Mouse button n (n > 0) is pushed #define FL_BUTTON2 0x02000000 ///< Mouse button 2 is pushed (M)
#define FL_BUTTON3 0x04000000 ///< Mouse button 3 is pushed (R)
#define FL_BUTTON4 0x08000000 ///< Mouse button 4 is pushed (BACK)
#define FL_BUTTON5 0x10000000 ///< Mouse button 5 is pushed (FORWARD)
#define FL_BUTTONS 0x1f000000 ///< Bitmask: any mouse button (1-5) is pushed
#define FL_BUTTON(n) (0x00800000<<(n)) ///< Mouse button n (n = 1..5) is pushed,
///< *undefined* if n outside 1..5
#define FL_KEY_MASK 0x0000ffff ///< All keys are 16 bit for now #define FL_KEY_MASK 0x0000ffff ///< All keys are 16 bit for now
// FIXME: Unicode needs 24 bits! // FIXME: Unicode needs 24 bits!
+46 -23
View File
@@ -679,8 +679,10 @@ int main() {
This returns garbage if the most recent event was not a FL_PUSH or FL_RELEASE event. This returns garbage if the most recent event was not a FL_PUSH or FL_RELEASE event.
\retval FL_LEFT_MOUSE \retval FL_LEFT_MOUSE
\retval FL_MIDDLE_MOUSE \retval FL_MIDDLE_MOUSE
\retval FL_RIGHT_MOUSE. \retval FL_RIGHT_MOUSE
\see Fl::event_buttons() \retval FL_BACK_MOUSE
\retval FL_FORWARD_MOUSE.
\see Fl::event_buttons(), Fl::event_state()
*/ */
static int event_button() {return e_keysym-FL_Button;} static int event_button() {return e_keysym-FL_Button;}
/** /**
@@ -689,24 +691,35 @@ int main() {
This is a bitfield of what shift states were on and what mouse buttons This is a bitfield of what shift states were on and what mouse buttons
were held down during the most recent event. were held down during the most recent event.
The legal event state bits are: \note FLTK platforms differ in what Fl::event_state() returns when it is called
while a modifier key or mouse button is being pressed or released.
- FL_SHIFT - Under X11 and Wayland, Fl::event_state() indicates the state of the modifier keys and
- FL_CAPS_LOCK mouse buttons just \b prior to the event. Thus, during the \c FL_KEYDOWN event generated
- FL_CTRL when pressing the shift key, for example, the \c FL_SHIFT bit of event_state() is 0 and
- FL_ALT becomes 1 only at the next event which can be any other event, including e.g. \c FL_MOVE.
- FL_NUM_LOCK - Under other platforms the reported state of modifier keys or mouse buttons includes that
- FL_META of the key or button being pressed or released.
- FL_SCROLL_LOCK - Fl::event_state() returns the same value under all platforms when it's called while a
- FL_BUTTON1 non-modifier key (e.g. a letter or function key) is being pressed or released.
- FL_BUTTON2 - X servers do not agree on shift states, and \c FL_NUM_LOCK, \c FL_META, and \c FL_SCROLL_LOCK
- FL_BUTTON3 may not work.
- The values were selected to match the XFree86 server on Linux.
X servers do not agree on shift states, and FL_NUM_LOCK, FL_META, and \note This inconsistency \b may be fixed (on X11 and Wayland) in a later release.
FL_SCROLL_LOCK may not work. The values were selected to match the The legal event state bits are:
XFree86 server on Linux. In addition there is a bug in the way X works | Device | State Bit | Function | Since |
so that the shift state is not correctly reported until the first event |----------|----------------|-------------------------|--------|
<I>after</I> the shift key is pressed or released. | Keyboard | FL_SHIFT | Shift | |
| Keyboard | FL_CAPS_LOCK | Caps Lock | |
| Keyboard | FL_CTRL | Ctrl | |
| Keyboard | FL_ALT | Alt | |
| Keyboard | FL_NUM_LOCK | Num Lock | |
| Keyboard | FL_META | Meta, e.g. "Windows" | |
| Keyboard | FL_SCROLL_LOCK | Scroll Lock | |
| Mouse | FL_BUTTON1 | left button | |
| Mouse | FL_BUTTON2 | middle button | |
| Mouse | FL_BUTTON3 | right button | |
| Mouse | FL_BUTTON4 | side button 1 (back) | 1.3.10 |
| Mouse | FL_BUTTON5 | side button 2 (forward) | 1.3.10 |
*/ */
static int event_state() {return e_state;} static int event_state() {return e_state;}
@@ -1156,7 +1169,7 @@ int main() {
time of the event. During an FL_RELEASE event, the state time of the event. During an FL_RELEASE event, the state
of the released button will be 0. To find out, which button of the released button will be 0. To find out, which button
caused an FL_RELEASE event, you can use Fl::event_button() instead. caused an FL_RELEASE event, you can use Fl::event_button() instead.
\return a bit mask value like { [FL_BUTTON1] | [FL_BUTTON2] | [FL_BUTTON3] } \return a bit mask value like { [FL_BUTTON1] | [FL_BUTTON2] | ... | [FL_BUTTON5] }
*/ */
static int event_buttons() {return e_state&0x7f000000;} static int event_buttons() {return e_state&0x7f000000;}
/** /**
@@ -1165,15 +1178,25 @@ int main() {
*/ */
static int event_button1() {return e_state&FL_BUTTON1;} static int event_button1() {return e_state&FL_BUTTON1;}
/** /**
Returns non-zero if button 2 is currently held down. Returns non-zero if mouse button 2 is currently held down.
For more details, see Fl::event_buttons(). For more details, see Fl::event_buttons().
*/ */
static int event_button2() {return e_state&FL_BUTTON2;} static int event_button2() {return e_state&FL_BUTTON2;}
/** /**
Returns non-zero if button 3 is currently held down. Returns non-zero if mouse button 3 is currently held down.
For more details, see Fl::event_buttons(). For more details, see Fl::event_buttons().
*/ */
static int event_button3() {return e_state&FL_BUTTON3;} static int event_button3() {return e_state&FL_BUTTON3;}
/**
Returns non-zero if mouse button 4 is currently held down.
For more details, see Fl::event_buttons().
*/
static int event_button4() {return e_state & FL_BUTTON4;}
/**
Returns non-zero if mouse button 5 is currently held down.
For more details, see Fl::event_buttons().
*/
static int event_button5() {return e_state & FL_BUTTON5;}
/** @} */ /** @} */
/** /**
+5 -1
View File
@@ -1133,7 +1133,7 @@ static void cocoaMagnifyHandler(NSEvent *theEvent)
*/ */
static void cocoaMouseHandler(NSEvent *theEvent) static void cocoaMouseHandler(NSEvent *theEvent)
{ {
static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2 }; static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2, FL_Button+4, FL_Button+5 };
static int px, py; static int px, py;
fl_lock_function(); fl_lock_function();
@@ -1156,11 +1156,15 @@ static void cocoaMouseHandler(NSEvent *theEvent)
if (btn == 1) Fl::e_state |= FL_BUTTON1; if (btn == 1) Fl::e_state |= FL_BUTTON1;
else if (btn == 3) Fl::e_state |= FL_BUTTON2; else if (btn == 3) Fl::e_state |= FL_BUTTON2;
else if (btn == 2) Fl::e_state |= FL_BUTTON3; else if (btn == 2) Fl::e_state |= FL_BUTTON3;
else if (btn == 4) Fl::e_state |= FL_BUTTON4;
else if (btn == 5) Fl::e_state |= FL_BUTTON5;
} }
else if (etype == NSLeftMouseUp || etype == NSRightMouseUp || etype == NSOtherMouseUp) { else if (etype == NSLeftMouseUp || etype == NSRightMouseUp || etype == NSOtherMouseUp) {
if (btn == 1) Fl::e_state &= ~FL_BUTTON1; if (btn == 1) Fl::e_state &= ~FL_BUTTON1;
else if (btn == 3) Fl::e_state &= ~FL_BUTTON2; else if (btn == 3) Fl::e_state &= ~FL_BUTTON2;
else if (btn == 2) Fl::e_state &= ~FL_BUTTON3; else if (btn == 2) Fl::e_state &= ~FL_BUTTON3;
else if (btn == 4) Fl::e_state &= ~FL_BUTTON4;
else if (btn == 5) Fl::e_state &= ~FL_BUTTON5;
} }
switch ( etype ) { switch ( etype ) {
+21 -4
View File
@@ -955,9 +955,12 @@ static int mouse_event(Fl_Window *window, int what, int button,
if (wParam & MK_SHIFT) state |= FL_SHIFT; if (wParam & MK_SHIFT) state |= FL_SHIFT;
if (wParam & MK_CONTROL) state |= FL_CTRL; if (wParam & MK_CONTROL) state |= FL_CTRL;
#endif #endif
if (wParam & MK_LBUTTON) state |= FL_BUTTON1; if (wParam & MK_LBUTTON) state |= FL_BUTTON1; // left
if (wParam & MK_MBUTTON) state |= FL_BUTTON2; if (wParam & MK_MBUTTON) state |= FL_BUTTON2; // right
if (wParam & MK_RBUTTON) state |= FL_BUTTON3; if (wParam & MK_RBUTTON) state |= FL_BUTTON3; // middle
if (wParam & MK_XBUTTON1) state |= FL_BUTTON4; // side button 1 (back)
if (wParam & MK_XBUTTON2) state |= FL_BUTTON5; // side button 2 (forward)
Fl::e_state = state; Fl::e_state = state;
switch (what) { switch (what) {
@@ -1206,7 +1209,21 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
case WM_RBUTTONDOWN: mouse_event(window, 0, 3, wParam, lParam); return 0; case WM_RBUTTONDOWN: mouse_event(window, 0, 3, wParam, lParam); return 0;
case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0; case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0;
case WM_RBUTTONUP: mouse_event(window, 2, 3, wParam, lParam); return 0; case WM_RBUTTONUP: mouse_event(window, 2, 3, wParam, lParam); return 0;
case WM_XBUTTONDOWN: {
int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5;
mouse_event(window, 0, xbutton, wParam, lParam);
return 0;
}
case WM_XBUTTONDBLCLK: {
int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5;
mouse_event(window, 1, xbutton, wParam, lParam);
return 0;
}
case WM_XBUTTONUP: {
int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5;
mouse_event(window, 2, xbutton, wParam, lParam);
return 0;
}
case WM_MOUSEMOVE: case WM_MOUSEMOVE:
#ifdef USE_TRACK_MOUSE #ifdef USE_TRACK_MOUSE
if (track_mouse_win != window) { if (track_mouse_win != window) {
+81 -19
View File
@@ -1315,6 +1315,18 @@ char fl_key_vector[32]; // used by Fl::get_key()
static int px, py; static int px, py;
static ulong ptime; static ulong ptime;
static unsigned int xbutton_state = 0; // extended button state (back, forward)
// Define the state bits we're interested in for Fl::event_state().
// Note that we ignore Button4Mask and Button5Mask (vertical scroll wheel).
// X11 doesn't define masks for Button6 and Button7 (horizontal scroll wheel)
// and any higher button numbers.
static const unsigned int event_state_mask =
ShiftMask | LockMask | ControlMask |
Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask |
Button1Mask | Button2Mask | Button3Mask;
static void set_event_xy() { static void set_event_xy() {
# if FLTK_CONSOLIDATE_MOTION # if FLTK_CONSOLIDATE_MOTION
send_motion = 0; send_motion = 0;
@@ -1323,7 +1335,7 @@ static void set_event_xy() {
Fl::e_x = fl_xevent->xbutton.x; Fl::e_x = fl_xevent->xbutton.x;
Fl::e_y_root = fl_xevent->xbutton.y_root; Fl::e_y_root = fl_xevent->xbutton.y_root;
Fl::e_y = fl_xevent->xbutton.y; Fl::e_y = fl_xevent->xbutton.y;
Fl::e_state = fl_xevent->xbutton.state << 16; Fl::e_state = ((fl_xevent->xbutton.state & event_state_mask) << 16) | xbutton_state;
fl_event_time = fl_xevent->xbutton.time; fl_event_time = fl_xevent->xbutton.time;
# ifdef __sgi # ifdef __sgi
// get the meta key off PC keyboards: // get the meta key off PC keyboards:
@@ -2169,26 +2181,56 @@ int fl_handle(const XEvent& thisevent)
Fl::e_is_click = 0; } Fl::e_is_click = 0; }
break; break;
case ButtonPress: // Mouse button "press" event:
Fl::e_keysym = FL_Button + xevent.xbutton.button; // ---------------------------
// X11 uses special conventions for mouse "button" numbers:
// 1-3: standard mouse buttons left, middle, right in this order
// 4-5: scroll wheel up, down - not reflected in Fl::event_state()
// 6-7: scroll wheel left, right - not reflected in Fl::event_state()
// 8-9: side buttons back, forward - mapped to 4-5, see below
// Since X11 pseudo button numbers 4-7 are useless for Fl::event_state() we map
// real button numbers 8 and 9 to 4 and 5, respectively in FLTK's button numbers
// and in the event state (Fl::event_state()).
// Variable `xbutton_state` is used internally to store the status of the extra
// mouse buttons 4 (back) and 5 (forward) since X11 doesn't keep their status.
case ButtonPress: {
int mb = xevent.xbutton.button; // mouse button
if (mb < 1 || mb > 9) return 0; // unknown or unsupported button, ignore
// FIXME(?): here we set some event related variables although we *might*
// ignore an event sent by X because we don't know or want it. This may lead to
// inconsistencies in Fl::event_key(), Fl::event_state() and more (see set_event_xy).
// For now we ignore this fact though, it's likely that it never happens.
// Albrecht, Sep 27, 2024
Fl::e_keysym = 0; // init: not used (zero) for scroll wheel events
set_event_xy(); set_event_xy();
Fl::e_dx = Fl::e_dy = 0; Fl::e_dx = Fl::e_dy = 0;
if (xevent.xbutton.button == Button4) { if (mb == Button4 && !Fl::event_shift()) {
Fl::e_dy = -1; // Up Fl::e_dy = -1; // Up
event = FL_MOUSEWHEEL; event = FL_MOUSEWHEEL;
} else if (xevent.xbutton.button == Button5) { } else if (mb == Button5 && !Fl::event_shift()) {
Fl::e_dy = +1; // Down Fl::e_dy = +1; // Down
event = FL_MOUSEWHEEL; event = FL_MOUSEWHEEL;
} else if (xevent.xbutton.button == 6) { } else if (mb == 6 || mb == Button4 && Fl::event_shift()) {
Fl::e_dx = -1; // Left Fl::e_dx = -1; // Left
event = FL_MOUSEWHEEL; event = FL_MOUSEWHEEL;
} else if (xevent.xbutton.button == 7) { } else if (mb == 7 || mb == Button5 && Fl::event_shift()) {
Fl::e_dx = +1; // Right Fl::e_dx = +1; // Right
event = FL_MOUSEWHEEL; event = FL_MOUSEWHEEL;
} else { } else if (mb < 4 || mb > 7) { // real mouse *buttons*, not scroll wheel
Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1)); if (mb > 7) // 8 = back, 9 = forward
mb -= 4; // map to 4 and 5, resp.
Fl::e_keysym = FL_Button + mb;
Fl::e_state |= (FL_BUTTON1 << (mb-1)); // set button state
if (mb == 4) xbutton_state |= FL_BUTTON4; // save extra button state internally
if (mb == 5) xbutton_state |= FL_BUTTON5; // save extra button state internally
event = FL_PUSH; event = FL_PUSH;
checkdouble(); checkdouble();
} else { // unknown button or shift combination
return 0;
} }
#if FLTK_CONSOLIDATE_MOTION #if FLTK_CONSOLIDATE_MOTION
@@ -2196,6 +2238,7 @@ int fl_handle(const XEvent& thisevent)
#endif #endif
in_a_window = true; in_a_window = true;
break; break;
} // ButtonPress
case PropertyNotify: case PropertyNotify:
if (xevent.xproperty.atom == fl_NET_WM_STATE) { if (xevent.xproperty.atom == fl_NET_WM_STATE) {
@@ -2234,19 +2277,38 @@ int fl_handle(const XEvent& thisevent)
break; break;
# endif # endif
case ButtonRelease: // Mouse button release event: for details see ButtonPress above
Fl::e_keysym = FL_Button + xevent.xbutton.button;
case ButtonRelease: {
int mb = xevent.xbutton.button; // mouse button
switch (mb) { // figure out which real button this is
case 1: // left
case 2: // middle
case 3: // right
break; // continue
case 8: // side button 1 (back)
case 9: // side button 2 (forward)
mb -= 4; // map to 4 and 5, respectively
break; // continue
default: // unknown button or scroll wheel:
return 0; // don't send FL_RELEASE event
}
Fl::e_keysym = FL_Button + mb; // == FL_BUTTON1 .. FL_BUTTON5
set_event_xy(); set_event_xy();
Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
if (xevent.xbutton.button == Button4 || Fl::e_state &= ~(FL_BUTTON1 << (mb-1));
xevent.xbutton.button == Button5) return 0; if (mb == 4) xbutton_state &= ~FL_BUTTON4; // clear internal button state
if (mb == 5) xbutton_state &= ~FL_BUTTON5; // clear internal button state
event = FL_RELEASE; event = FL_RELEASE;
#if FLTK_CONSOLIDATE_MOTION #if FLTK_CONSOLIDATE_MOTION
fl_xmousewin = window; fl_xmousewin = window;
#endif #endif // FLTK_CONSOLIDATE_MOTION
in_a_window = true; in_a_window = true;
break; break;
} // ButtonRelease
case EnterNotify: case EnterNotify:
if (xevent.xcrossing.detail == NotifyInferior) break; if (xevent.xcrossing.detail == NotifyInferior) break;
+31 -31
View File
@@ -651,43 +651,17 @@ Function {make_window()} {open
xywh {440 35 20 10} box THIN_UP_BOX selection_color 3 labelsize 8 xywh {440 35 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
} }
Fl_Button {} { Fl_Button {} {
label whl label bck
user_data 0x800 user_data_type {void*} user_data FL_BUTTON4 user_data_type {void*}
callback shift_cb callback shift_cb
xywh {460 35 20 10} box THIN_UP_BOX selection_color 3 labelsize 8 xywh {460 35 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
} }
Fl_Button {} { Fl_Button {} {
label {?} label fwd
user_data 0x1000 user_data_type {void*} user_data FL_BUTTON5 user_data_type {void*}
callback shift_cb callback shift_cb selected
xywh {400 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8 xywh {400 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
} }
Fl_Button {} {
label {?}
user_data 0x2000 user_data_type {void*}
callback shift_cb
xywh {420 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
}
Fl_Button {} {
label {?}
user_data 0x4000 user_data_type {void*}
callback shift_cb
xywh {440 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
}
Fl_Button {} {
label {?}
user_data 0x8000 user_data_type {void*}
callback shift_cb
xywh {460 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
}
Fl_Output key_output {
label {Fl::event_key():}
xywh {15 20 170 30} labelsize 9 align 5
}
Fl_Box {} {
label {Fl::event_state():}
xywh {400 15 80 40} labelsize 9 align 5
}
Fl_Output text_output { Fl_Output text_output {
label {Fl::event_text():} label {Fl::event_text():}
xywh {195 20 190 30} labelsize 9 align 5 xywh {195 20 190 30} labelsize 9 align 5
@@ -714,5 +688,31 @@ Function {make_window()} {open
callback wheel_cb callback wheel_cb
xywh {460 70 20 20} box ROUND_UP_BOX selection_color 49 labelsize 9 align 5 step 0.1 xywh {460 70 20 20} box ROUND_UP_BOX selection_color 49 labelsize 9 align 5 step 0.1
} }
Fl_Box {} {
label {Fl::event_state():}
xywh {400 15 80 0} labelsize 9 align 5
}
Fl_Output key_output {
label {Fl::event_key():}
xywh {15 20 170 30} labelsize 9 align 5
}
Fl_Button {} {
label {?}
user_data 0x8000 user_data_type {void*}
callback shift_cb
xywh {460 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
}
Fl_Button {} {
label {?}
user_data 0x4000 user_data_type {void*}
callback shift_cb
xywh {440 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
}
Fl_Button {} {
label {?}
user_data 0x2000 user_data_type {void*}
callback shift_cb
xywh {420 45 20 10} box THIN_UP_BOX selection_color 3 labelsize 8
}
} }
} }