summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlbrecht Schlosser <albrechts.fltk@online.de>2024-09-26 17:59:52 +0200
committerAlbrecht Schlosser <fltk@aljus.de>2024-10-06 18:53:03 +0200
commit4f4a9be15b0c52837dd4d4a04ff021cc3e5d691e (patch)
treea7238b08a93b2af73c4df86a66b7abfa47689e76 /src
parent3fbd4f944f0a6e16630974e56e1e896eb7bbf6f7 (diff)
Support mouse buttons 4 + 5 (aka "side buttons") (#1076, #1068)
This work is based on PR 1068 (patch by @CendioHalim) and extended to store button status (4,5) in Fl::event_state() like it's done for other mouse buttons (1-3). Changes: - new symbol: FL_BUTTON4 = side button 1 = "back" - new symbol: FL_BUTTON5 = side button 2 = "forward" - modified : FL_BUTTONS now includes bits for two side buttons Note: the status of these new buttons is not maintained by X11, therefore we need to maintain them in internal variables for this platform.
Diffstat (limited to 'src')
-rw-r--r--src/Fl_cocoa.mm10
-rw-r--r--src/Fl_win32.cxx24
-rw-r--r--src/Fl_x.cxx130
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx20
4 files changed, 137 insertions, 47 deletions
diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm
index ddb96c6b2..369ce8444 100644
--- a/src/Fl_cocoa.mm
+++ b/src/Fl_cocoa.mm
@@ -1046,7 +1046,7 @@ static void cocoaMagnifyHandler(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;
fl_lock_function();
@@ -1060,7 +1060,7 @@ static void cocoaMouseHandler(NSEvent *theEvent)
float s = Fl::screen_driver()->scale(0);
pos.x /= s; pos.y /= s;
pos.y = window->h() - pos.y;
- NSInteger btn = [theEvent buttonNumber] + 1;
+ NSInteger btn = [theEvent buttonNumber] + 1;
NSUInteger mods = [theEvent modifierFlags];
int sendEvent = 0;
@@ -1070,13 +1070,17 @@ static void cocoaMouseHandler(NSEvent *theEvent)
if (btn == 1) Fl::e_state |= FL_BUTTON1;
else if (btn == 3) Fl::e_state |= FL_BUTTON2;
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 == NSEventTypeLeftMouseUp || etype == NSEventTypeRightMouseUp ||
etype == NSEventTypeOtherMouseUp) {
if (btn == 1) Fl::e_state &= ~FL_BUTTON1;
else if (btn == 3) Fl::e_state &= ~FL_BUTTON2;
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 ) {
case NSEventTypeLeftMouseDown:
diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx
index 36e77a99b..dec95241e 100644
--- a/src/Fl_win32.cxx
+++ b/src/Fl_win32.cxx
@@ -1044,9 +1044,12 @@ static int mouse_event(Fl_Window *window, int what, int button,
if (wParam & MK_SHIFT) state |= FL_SHIFT;
if (wParam & MK_CONTROL) state |= FL_CTRL;
#endif
- if (wParam & MK_LBUTTON) state |= FL_BUTTON1;
- if (wParam & MK_MBUTTON) state |= FL_BUTTON2;
- if (wParam & MK_RBUTTON) state |= FL_BUTTON3;
+ if (wParam & MK_LBUTTON) state |= FL_BUTTON1; // left
+ if (wParam & MK_MBUTTON) state |= FL_BUTTON2; // right
+ 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;
switch (what) {
@@ -1348,6 +1351,21 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
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:
#ifdef USE_TRACK_MOUSE
diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx
index dcae946ab..681014486 100644
--- a/src/Fl_x.cxx
+++ b/src/Fl_x.cxx
@@ -987,12 +987,25 @@ static ulong ptime;
// Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask // 1<<8 .. 1<<12
// };
//
-// Note: some more (undefined?) state bits *can* be set if the user uses a keyboard
+// Note: some more (undefined?) state bits *may* be set if the user uses a keyboard
// other than the primary one (the top-most in keyboard settings). Therefore we must
-// take care not to use these undefined bits. These undefined bits will be set in
-// Fl::event_state() though: for backwards compatibility and transparency.
-// See definition of FL_BUTTONS in FL/Enumerations.H: only three "sticky" mouse
-// buttons as of July 2024.
+// take care not to use these undefined bits (found by accident).
+// These undefined bits are ignored and not set in Fl::event_state(), otherwise we
+// might overwrite other valid bits (since FLTK 1.4.0, Sep 2024 or later).
+// See definition of FL_BUTTONS in FL/Enumerations.H:
+// there are only five "sticky" mouse buttons as of Sep 27, 2024.
+
+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(Fl_Window *win) {
# if FLTK_CONSOLIDATE_MOTION
@@ -1006,7 +1019,7 @@ static void set_event_xy(Fl_Window *win) {
Fl::e_x = fl_xevent->xbutton.x/s;
Fl::e_y_root = fl_xevent->xbutton.y_root/s;
Fl::e_y = fl_xevent->xbutton.y/s;
- 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;
# ifdef __sgi
// get the meta key off PC keyboards:
@@ -2015,26 +2028,56 @@ int fl_handle(const XEvent& thisevent)
Fl::e_is_click = 0; }
break;
- case ButtonPress:
- Fl::e_keysym = FL_Button + xevent.xbutton.button;
+ // Mouse button "press" event:
+ // ---------------------------
+ // 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(window);
Fl::e_dx = Fl::e_dy = 0;
- if (xevent.xbutton.button == Button4 && !Fl::event_shift()) {
- Fl::e_dy = -1; // Up
+
+ if (mb == Button4 && !Fl::event_shift()) {
+ Fl::e_dy = -1; // up
event = FL_MOUSEWHEEL;
- } else if (xevent.xbutton.button == Button5 && !Fl::event_shift()) {
- Fl::e_dy = +1; // Down
+ } else if (mb == Button5 && !Fl::event_shift()) {
+ Fl::e_dy = +1; // down
event = FL_MOUSEWHEEL;
- } else if (xevent.xbutton.button == 6 || (xevent.xbutton.button == Button4 && Fl::event_shift())) {
- Fl::e_dx = -1; // Left
+ } else if (mb == 6 || (mb == Button4 && Fl::event_shift())) {
+ Fl::e_dx = -1; // left
event = FL_MOUSEWHEEL;
- } else if (xevent.xbutton.button == 7 || (xevent.xbutton.button == Button5 && Fl::event_shift())) {
- Fl::e_dx = +1; // Right
+ } else if (mb == 7 || (mb == Button5 && Fl::event_shift())) {
+ Fl::e_dx = +1; // right
event = FL_MOUSEWHEEL;
- } else {
- Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
- event = FL_PUSH;
- checkdouble();
+ } else if (mb < 4 || mb > 7) { // real mouse *buttons*, not scroll wheel
+ 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;
+ checkdouble();
+ } else { // unknown button or shift combination
+ return 0;
}
#if FLTK_CONSOLIDATE_MOTION
@@ -2042,6 +2085,38 @@ int fl_handle(const XEvent& thisevent)
#endif // FLTK_CONSOLIDATE_MOTION
in_a_window = true;
break;
+ } // ButtonPress
+
+ // Mouse button release event: for details see ButtonPress above
+
+ 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(window);
+
+ Fl::e_state &= ~(FL_BUTTON1 << (mb-1));
+ 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;
+
+#if FLTK_CONSOLIDATE_MOTION
+ fl_xmousewin = window;
+#endif // FLTK_CONSOLIDATE_MOTION
+ in_a_window = true;
+ break;
+ } // ButtonRelease
case PropertyNotify:
if (xevent.xproperty.atom == fl_NET_WM_STATE) {
@@ -2085,21 +2160,6 @@ int fl_handle(const XEvent& thisevent)
break;
# endif
- case ButtonRelease:
- Fl::e_keysym = FL_Button + xevent.xbutton.button;
- set_event_xy(window);
- Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
- if (xevent.xbutton.button > Button3) { // "buttons" 4-7 = mousewheel events: don't send FL_RELEASE
- return 0;
- }
- event = FL_RELEASE;
-
-#if FLTK_CONSOLIDATE_MOTION
- fl_xmousewin = window;
-#endif // FLTK_CONSOLIDATE_MOTION
- in_a_window = true;
- break;
-
case EnterNotify:
if (xevent.xcrossing.detail == NotifyInferior) break;
// XInstallColormap(fl_display, Fl_X::flx(window)->colormap);
diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
index 01fd46536..4a7ace768 100644
--- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
@@ -300,13 +300,21 @@ static void pointer_button(void *data,
int b = 0;
// Fl::e_state &= ~FL_BUTTONS; // DO NOT reset the mouse button state!
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
- if (button == BTN_LEFT) { Fl::e_state |= FL_BUTTON1; b = 1; }
- else if (button == BTN_RIGHT) { Fl::e_state |= FL_BUTTON3; b = 3; }
- else if (button == BTN_MIDDLE) { Fl::e_state |= FL_BUTTON2; b = 2; }
+ if (button == BTN_LEFT) { Fl::e_state |= FL_BUTTON1; b = 1; }
+ else if (button == BTN_RIGHT) { Fl::e_state |= FL_BUTTON3; b = 3; }
+ else if (button == BTN_MIDDLE) { Fl::e_state |= FL_BUTTON2; b = 2; }
+ else if (button == BTN_BACK) { Fl::e_state |= FL_BUTTON4; b = 4; } // ?
+ else if (button == BTN_SIDE) { Fl::e_state |= FL_BUTTON4; b = 4; } // OK: Debian 12
+ else if (button == BTN_FORWARD) { Fl::e_state |= FL_BUTTON5; b = 5; } // ?
+ else if (button == BTN_EXTRA) { Fl::e_state |= FL_BUTTON5; b = 5; } // OK: Debian 12
} else { // must be WL_POINTER_BUTTON_STATE_RELEASED
- if (button == BTN_LEFT) { Fl::e_state &= ~FL_BUTTON1; b = 1; }
- else if (button == BTN_RIGHT) { Fl::e_state &= ~FL_BUTTON3; b = 3; }
- else if (button == BTN_MIDDLE) { Fl::e_state &= ~FL_BUTTON2; b = 2; }
+ if (button == BTN_LEFT) { Fl::e_state &= ~FL_BUTTON1; b = 1; }
+ else if (button == BTN_RIGHT) { Fl::e_state &= ~FL_BUTTON3; b = 3; }
+ else if (button == BTN_MIDDLE) { Fl::e_state &= ~FL_BUTTON2; b = 2; }
+ else if (button == BTN_BACK) { Fl::e_state &= ~FL_BUTTON4; b = 4; } // ?
+ else if (button == BTN_SIDE) { Fl::e_state &= ~FL_BUTTON4; b = 4; } // OK: Debian 12
+ else if (button == BTN_FORWARD) { Fl::e_state &= ~FL_BUTTON5; b = 5; } // ?
+ else if (button == BTN_EXTRA) { Fl::e_state &= ~FL_BUTTON5; b = 5; } // OK: Debian 12
}
Fl::e_keysym = FL_Button + b;
Fl::e_dx = Fl::e_dy = 0;