From b0accb87dfdc9331aa8cbc9f2a4d8a99e6a9485c Mon Sep 17 00:00:00 2001
From: deuce <>
Date: Mon, 4 May 2020 18:32:54 +0000
Subject: [PATCH] Fix error where SDL mode would not get button up events. Add
 support for scroll "buttons" (buttons 4 and 5).

---
 src/conio/ciolib.h   | 34 ++++++++++++++++++++++------
 src/conio/curs_cio.c | 16 ++++++++++++-
 src/conio/mouse.c    | 50 +++++++++++++++++++++++------------------
 src/conio/sdl_con.c  | 12 ++++++++++
 src/conio/win32cio.c | 53 ++++++++++++++++++++++++++------------------
 src/conio/x_events.c |  2 +-
 6 files changed, 116 insertions(+), 51 deletions(-)

diff --git a/src/conio/ciolib.h b/src/conio/ciolib.h
index b876748d06..bb77f4f912 100644
--- a/src/conio/ciolib.h
+++ b/src/conio/ciolib.h
@@ -568,11 +568,13 @@ CIOLIBEXPORT void CIOLIBCALL ansi_ciolib_setdoorway(int enable);
 #define CIOLIB_BUTTON_1	1
 #define CIOLIB_BUTTON_2	2
 #define CIOLIB_BUTTON_3	4
+#define CIOLIB_BUTTON_4	8
+#define CIOLIB_BUTTON_5	16
 
 #define CIOLIB_BUTTON(x)	(1<<(x-1))
 
 enum {
-	 CIOLIB_MOUSE_MOVE				/* 0 */
+	 CIOLIB_MOUSE_MOVE			/* 0 */
 	,CIOLIB_BUTTON_1_PRESS
 	,CIOLIB_BUTTON_1_RELEASE
 	,CIOLIB_BUTTON_1_CLICK
@@ -599,7 +601,25 @@ enum {
 	,CIOLIB_BUTTON_3_QUAD_CLICK
 	,CIOLIB_BUTTON_3_DRAG_START
 	,CIOLIB_BUTTON_3_DRAG_MOVE
-	,CIOLIB_BUTTON_3_DRAG_END		/* 27 */
+	,CIOLIB_BUTTON_3_DRAG_END
+	,CIOLIB_BUTTON_4_PRESS
+	,CIOLIB_BUTTON_4_RELEASE
+	,CIOLIB_BUTTON_4_CLICK			/* 30 */
+	,CIOLIB_BUTTON_4_DBL_CLICK
+	,CIOLIB_BUTTON_4_TRPL_CLICK
+	,CIOLIB_BUTTON_4_QUAD_CLICK
+	,CIOLIB_BUTTON_4_DRAG_START
+	,CIOLIB_BUTTON_4_DRAG_MOVE
+	,CIOLIB_BUTTON_4_DRAG_END
+	,CIOLIB_BUTTON_5_PRESS
+	,CIOLIB_BUTTON_5_RELEASE
+	,CIOLIB_BUTTON_5_CLICK
+	,CIOLIB_BUTTON_5_DBL_CLICK		/* 40 */
+	,CIOLIB_BUTTON_5_TRPL_CLICK
+	,CIOLIB_BUTTON_5_QUAD_CLICK
+	,CIOLIB_BUTTON_5_DRAG_START
+	,CIOLIB_BUTTON_5_DRAG_MOVE
+	,CIOLIB_BUTTON_5_DRAG_END		/* 45 */
 };
 
 #define CIOLIB_BUTTON_PRESS(x)		((x-1)*9+1)
@@ -628,11 +648,11 @@ CIOLIBEXPORT int CIOLIBCALL mouse_pending(void);
 CIOLIBEXPORT int CIOLIBCALL ciolib_getmouse(struct mouse_event *mevent);
 CIOLIBEXPORT int CIOLIBCALL ciolib_ungetmouse(struct mouse_event *mevent);
 CIOLIBEXPORT void ciolib_mouse_thread(void *data);
-CIOLIBEXPORT int CIOLIBCALL ciomouse_setevents(int events);
-CIOLIBEXPORT int CIOLIBCALL ciomouse_addevents(int events);
-CIOLIBEXPORT int CIOLIBCALL ciomouse_delevents(int events);
-CIOLIBEXPORT int CIOLIBCALL ciomouse_addevent(int event);
-CIOLIBEXPORT int CIOLIBCALL ciomouse_delevent(int event);
+CIOLIBEXPORT uint64_t CIOLIBCALL ciomouse_setevents(uint64_t events);
+CIOLIBEXPORT uint64_t CIOLIBCALL ciomouse_addevents(uint64_t events);
+CIOLIBEXPORT uint64_t CIOLIBCALL ciomouse_delevents(uint64_t events);
+CIOLIBEXPORT uint64_t CIOLIBCALL ciomouse_addevent(uint64_t event);
+CIOLIBEXPORT uint64_t CIOLIBCALL ciomouse_delevent(uint64_t event);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/conio/curs_cio.c b/src/conio/curs_cio.c
index 4bef3f5abe..ab5b1f9409 100644
--- a/src/conio/curs_cio.c
+++ b/src/conio/curs_cio.c
@@ -894,13 +894,19 @@ int curs_initciolib(long inmode)
 	}
 	mode = inmode;
 #ifdef NCURSES_VERSION_MAJOR
-		if(mousemask(BUTTON1_PRESSED|BUTTON1_RELEASED|BUTTON2_PRESSED|BUTTON2_RELEASED|BUTTON3_PRESSED|BUTTON3_RELEASED|REPORT_MOUSE_POSITION,NULL)==(BUTTON1_PRESSED|BUTTON1_RELEASED|BUTTON2_PRESSED|BUTTON2_RELEASED|BUTTON3_PRESSED|BUTTON3_RELEASED|REPORT_MOUSE_POSITION)) {
+	{
+		mmask_t msk = BUTTON1_PRESSED|BUTTON1_RELEASED|BUTTON2_PRESSED|BUTTON2_RELEASED|BUTTON3_PRESSED|BUTTON3_RELEASED|REPORT_MOUSE_POSITION;
+#ifdef BUTTON5_PRESSED
+		msk |= BUTTON4_PRESSED|BUTTON5_PRESSED;
+#endif
+		if (mousemask(msk, NULL) == msk) {
 			mouseinterval(0);
 			cio_api.mouse=1;
 		}
 		else {
 			mousemask(0,NULL);
 		}
+	}
 #endif
 
 	if (COLORS >= 16)
@@ -1139,6 +1145,14 @@ int curs_getch(void)
 						case BUTTON3_RELEASED:
 							evnt=CIOLIB_BUTTON_3_RELEASE;
 							break;
+#ifdef BUTTON5_PRESSED
+						case BUTTON4_PRESSED:
+							evnt=CIOLIB_BUTTON_4_PRESS;
+							break;
+						case BUTTON5_PRESSED:
+							evnt=CIOLIB_BUTTON_5_PRESS;
+							break;
+#endif
 						case REPORT_MOUSE_POSITION:
 							evnt=CIOLIB_MOUSE_MOVE;
 							break;
diff --git a/src/conio/mouse.c b/src/conio/mouse.c
index 9b1d782b68..5554c03bb5 100644
--- a/src/conio/mouse.c
+++ b/src/conio/mouse.c
@@ -80,10 +80,10 @@ struct mouse_state {
 	int	knownbuttonstatemask;	/* Mask of buttons that have done something since
 								 * We started watching... the rest are actually in
 								 * an unknown state */
-	int	button_state[3];		/* Expanded state of each button */
-	int	button_x[3];			/* Start X/Y position of the current state */
-	int	button_y[3];
-	clock_t	timeout[3];	/* Button event timeouts (timespecs ie: time of expiry) */
+	int	button_state[5];		/* Expanded state of each button */
+	int	button_x[5];			/* Start X/Y position of the current state */
+	int	button_y[5];
+	clock_t	timeout[5];	/* Button event timeouts (timespecs ie: time of expiry) */
 	int	curx;					/* Current X position */
 	int	cury;					/* Current Y position */
 	int	events;					/* Currently enabled events */
@@ -95,7 +95,7 @@ struct mouse_state {
 };
 
 struct mouse_state state;
-int mouse_events=0;
+uint64_t mouse_events=0;
 int ciolib_mouse_initialized=0;
 static int ungot=0;
 pthread_mutex_t unget_mutex;
@@ -111,33 +111,33 @@ void CIOLIBCALL init_mouse(void)
 	ciolib_mouse_initialized=1;
 }
 
-int CIOLIBCALL ciomouse_setevents(int events)
+uint64_t CIOLIBCALL ciomouse_setevents(uint64_t events)
 {
 	mouse_events=events;
 	return mouse_events;
 }
 
-int CIOLIBCALL ciomouse_addevents(int events)
+uint64_t CIOLIBCALL ciomouse_addevents(uint64_t events)
 {
 	mouse_events |= events;
 	return mouse_events;
 }
 
-int CIOLIBCALL ciomouse_delevents(int events)
+uint64_t CIOLIBCALL ciomouse_delevents(uint64_t events)
 {
 	mouse_events &= ~events;
 	return mouse_events;
 }
 
-int CIOLIBCALL ciomouse_addevent(int event)
+uint64_t CIOLIBCALL ciomouse_addevent(uint64_t event)
 {
-	mouse_events |= (1<<event);
+	mouse_events |= (UINT64_C(1)<<event);
 	return mouse_events;
 }
 
-int CIOLIBCALL ciomouse_delevent(int event)
+uint64_t CIOLIBCALL ciomouse_delevent(uint64_t event)
 {
-	mouse_events &= ~(1<<event);
+	mouse_events &= ~(UINT64_C(1)<<event);
 	return mouse_events;
 }
 
@@ -164,7 +164,7 @@ void CIOLIBCALL add_outevent(int event, int x, int y)
 	struct out_mouse_event *ome;
 	int	but;
 
-	if(!(mouse_events & 1<<event))
+	if(!(mouse_events & UINT64_C(1)<<event))
 		return;
 	ome=(struct out_mouse_event *)malloc(sizeof(struct out_mouse_event));
 
@@ -187,19 +187,19 @@ int CIOLIBCALL more_multies(int button, int clicks)
 {
 	switch(clicks) {
 		case 0:
-			if(mouse_events & (1<<CIOLIB_BUTTON_CLICK(button)))
+			if(mouse_events & (UINT64_C(1)<<CIOLIB_BUTTON_CLICK(button)))
 				return(1);
 			/* Fall-through */
 		case 1:
-			if(mouse_events & (1<<CIOLIB_BUTTON_DBL_CLICK(button)))
+			if(mouse_events & (UINT64_C(1)<<CIOLIB_BUTTON_DBL_CLICK(button)))
 				return(1);
 			/* Fall-through */
 		case 2:
-			if(mouse_events & (1<<CIOLIB_BUTTON_TRPL_CLICK(button)))
+			if(mouse_events & (UINT64_C(1)<<CIOLIB_BUTTON_TRPL_CLICK(button)))
 				return(1);
 			/* Fall-through */
 		case 3:
-			if(mouse_events & (1<<CIOLIB_BUTTON_QUAD_CLICK(button)))
+			if(mouse_events & (UINT64_C(1)<<CIOLIB_BUTTON_QUAD_CLICK(button)))
 				return(1);
 			/* Fall-through */
 	}
@@ -280,13 +280,18 @@ void ciolib_mouse_thread(void *data)
 				continue;
 			}
 			but=CIOLIB_BUTTON_NUMBER(in->event);
+			if (in->x < 0)
+				in->x = state.curx;
+			if (in->y < 0)
+				in->y = state.curx;
+
 			switch(CIOLIB_BUTTON_BASE(in->event)) {
 				case CIOLIB_MOUSE_MOVE:
 					if(in->x==state.curx
 							&& in->y==state.cury)
 						break;
 					add_outevent(CIOLIB_MOUSE_MOVE,in->x,in->y);
-					for(but=1;but<=3;but++) {
+					for(but=1;but<=5;but++) {
 						switch(state.button_state[but-1]) {
 							case MOUSE_NOSTATE:
 								if(state.buttonstate & CIOLIB_BUTTON(but)) {
@@ -354,6 +359,9 @@ void ciolib_mouse_thread(void *data)
 								state.button_state[but-1]=MOUSE_NOSTATE;
 								state.timeout[but-1]=0;
 							}
+							// Scroll "buttons"...
+							if (but > 3)
+								state.button_state[but-1] = MOUSE_NOSTATE;
 							break;
 						case MOUSE_CLICKED:
 							state.button_state[but-1]=MOUSE_DOUBLEPRESSED;
@@ -427,13 +435,13 @@ void ciolib_mouse_thread(void *data)
 		}
 
 		timeout_button=0;
-		for(but=1;but<=3;but++) {
+		for(but=1;but<=5;but++) {
 			if(state.button_state[but-1]==MOUSE_DRAGSTARTED &&
-			    (mouse_events & ((1<<CIOLIB_BUTTON_DRAG_START(but)) | (1<<CIOLIB_BUTTON_DRAG_MOVE(but)) | (1<<CIOLIB_BUTTON_DRAG_END(but)))) == 0)
+			    (mouse_events & ((UINT64_C(1)<<CIOLIB_BUTTON_DRAG_START(but)) | (UINT64_C(1)<<CIOLIB_BUTTON_DRAG_MOVE(but)) | (UINT64_C(1)<<CIOLIB_BUTTON_DRAG_END(but)))) == 0)
 				state.button_state[but-1] = MOUSE_NOSTATE;
 		}
 
-		for(but=1;but<=3;but++) {
+		for(but=1;but<=5;but++) {
 			if(state.button_state[but-1]!=MOUSE_NOSTATE 
 					&& state.button_state[but-1]!=MOUSE_DRAGSTARTED 
 					&& state.timeout[but-1]!=0
diff --git a/src/conio/sdl_con.c b/src/conio/sdl_con.c
index 9957a716c8..382e9e6d13 100644
--- a/src/conio/sdl_con.c
+++ b/src/conio/sdl_con.c
@@ -863,6 +863,18 @@ void sdl_video_event_thread(void *data)
 							break;
 					}
 					break;
+				case SDL_MOUSEWHEEL:
+					if (!ciolib_mouse_initialized)
+						break;
+					if (ev.wheel.y) {
+						if (ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
+							ev.wheel.y = 0 - ev.wheel.y;
+						if (ev.wheel.y > 0)
+							ciomouse_gotevent(CIOLIB_BUTTON_PRESS(4), -1, -1);
+						if (ev.wheel.y < 0)
+							ciomouse_gotevent(CIOLIB_BUTTON_PRESS(5), -1, -1);
+					}
+					break;
 				case SDL_MOUSEBUTTONUP:
 					if(!ciolib_mouse_initialized)
 						break;
diff --git a/src/conio/win32cio.c b/src/conio/win32cio.c
index a51a172398..2fe63d0e41 100644
--- a/src/conio/win32cio.c
+++ b/src/conio/win32cio.c
@@ -334,28 +334,39 @@ static int win32_keyboardio(int isgetch)
 						LastY=input.Event.MouseEvent.dwMousePosition.Y+1;
 						ciomouse_gotevent(CIOLIB_MOUSE_MOVE,LastX,LastY);
 					}
-					if(last_state != input.Event.MouseEvent.dwButtonState) {
-						switch(input.Event.MouseEvent.dwButtonState ^ last_state) {
-							case FROM_LEFT_1ST_BUTTON_PRESSED:
-								if(input.Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED)
-									ciomouse_gotevent(CIOLIB_BUTTON_1_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
-								else
-									ciomouse_gotevent(CIOLIB_BUTTON_1_RELEASE,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
-								break;
-							case FROM_LEFT_2ND_BUTTON_PRESSED:
-								if(input.Event.MouseEvent.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED)
-									ciomouse_gotevent(CIOLIB_BUTTON_2_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
-								else
-									ciomouse_gotevent(CIOLIB_BUTTON_2_RELEASE,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
-								break;
-							case RIGHTMOST_BUTTON_PRESSED:
-								if(input.Event.MouseEvent.dwButtonState & RIGHTMOST_BUTTON_PRESSED)
-									ciomouse_gotevent(CIOLIB_BUTTON_3_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
-								else
-									ciomouse_gotevent(CIOLIB_BUTTON_3_RELEASE,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
-								break;
+					if (input.Event.MouseEvent.dwEventFlags == 0) {
+						if(last_state != input.Event.MouseEvent.dwButtonState) {
+							switch(input.Event.MouseEvent.dwButtonState ^ last_state) {
+								case FROM_LEFT_1ST_BUTTON_PRESSED:
+									if(input.Event.MouseEvent.dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED)
+										ciomouse_gotevent(CIOLIB_BUTTON_1_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+									else
+										ciomouse_gotevent(CIOLIB_BUTTON_1_RELEASE,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+									break;
+								case FROM_LEFT_2ND_BUTTON_PRESSED:
+									if(input.Event.MouseEvent.dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED)
+										ciomouse_gotevent(CIOLIB_BUTTON_2_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+									else
+										ciomouse_gotevent(CIOLIB_BUTTON_2_RELEASE,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+									break;
+								case RIGHTMOST_BUTTON_PRESSED:
+									if(input.Event.MouseEvent.dwButtonState & RIGHTMOST_BUTTON_PRESSED)
+										ciomouse_gotevent(CIOLIB_BUTTON_3_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+									else
+										ciomouse_gotevent(CIOLIB_BUTTON_3_RELEASE,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+									break;
+							}
+							last_state=input.Event.MouseEvent.dwButtonState;
+						}
+					}
+					else if (input.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED) {
+						// If the high word of the dwButtonState member contains a positive value... ARGH!
+						if (input.Event.MouseEvent.dwButtonState & 0x80000000) {
+							ciomouse_gotevent(CIOLIB_BUTTON_5_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
+						}
+						else {
+							ciomouse_gotevent(CIOLIB_BUTTON_4_PRESS,input.Event.MouseEvent.dwMousePosition.X+1,input.Event.MouseEvent.dwMousePosition.Y+1);
 						}
-						last_state=input.Event.MouseEvent.dwButtonState;
 					}
 				}
 		}
diff --git a/src/conio/x_events.c b/src/conio/x_events.c
index 2e7af59797..425866d12f 100644
--- a/src/conio/x_events.c
+++ b/src/conio/x_events.c
@@ -818,7 +818,7 @@ static int x11_event(XEvent *ev)
 					be->x=x_cvstat.cols;
 				if(be->y>x_cvstat.rows+1)
 					be->y=x_cvstat.rows+1;
-				if (be->button <= 3) {
+				if (be->button <= 5) {
 					ciomouse_gotevent(CIOLIB_BUTTON_PRESS(be->button),be->x,be->y);
 				}
 	    	}
-- 
GitLab