ref: 8b91de196c8cb21c57763fe80010edb889a7a8f6
parent: 8ae25046d843667ce0ec671ed8c72d58aaef7000
author: Simon Tatham <anakin@pobox.com>
date: Tue Jan 25 09:07:46 EST 2005
Include code in midend.c to sanitise streams of mouse events so that back ends can be assured of always receiving them in a sensible sequence (button-down, followed by zero or more drags, followed by button-up, and never changing button in the middle of such a sequence). I have a suspicion this issue was the cause of the mysterious Pattern grid updates seen by Dan during testing last night. [originally from svn r5208]
--- a/midend.c
+++ b/midend.c
@@ -32,6 +32,8 @@
float anim_time, anim_pos;
float flash_time, flash_pos;
int dir;
+
+ int pressed_mouse_button;
};
#define ensure(me) do { \
@@ -66,6 +68,7 @@
me->flash_time = me->flash_pos = 0.0F;
me->dir = 0;
me->ui = NULL;
+ me->pressed_mouse_button = 0;
sfree(randseed);
@@ -114,6 +117,7 @@
if (me->ui)
me->ourgame->free_ui(me->ui);
me->ui = me->ourgame->new_ui(me->states[0]);
+ me->pressed_mouse_button = 0;
}
void midend_restart_game(midend_data *me)
@@ -180,7 +184,7 @@
}
}
-int midend_process_key(midend_data *me, int x, int y, int button)
+static int midend_really_process_key(midend_data *me, int x, int y, int button)
{
game_state *oldstate = me->ourgame->dup_game(me->states[me->statepos - 1]);
float anim_time;
@@ -253,6 +257,99 @@
activate_timer(me->frontend);
return 1;
+}
+
+int midend_process_key(midend_data *me, int x, int y, int button)
+{
+ int ret = 1;
+
+ /*
+ * Harmonise mouse drag and release messages.
+ *
+ * Some front ends might accidentally switch from sending, say,
+ * RIGHT_DRAG messages to sending LEFT_DRAG, half way through a
+ * drag. (This can happen on the Mac, for example, since
+ * RIGHT_DRAG is usually done using Command+drag, and if the
+ * user accidentally releases Command half way through the drag
+ * then there will be trouble.)
+ *
+ * It would be an O(number of front ends) annoyance to fix this
+ * in the front ends, but an O(number of back ends) annoyance
+ * to have each game capable of dealing with it. Therefore, we
+ * fix it _here_ in the common midend code so that it only has
+ * to be done once.
+ *
+ * Semantics:
+ *
+ * - when we receive a button down message, we remember which
+ * button went down.
+ *
+ * - if we receive a drag or release message for a button we
+ * don't currently think is pressed, we fabricate a
+ * button-down for it before sending it.
+ *
+ * - if we receive (or fabricate) a button down message
+ * without having seen a button up for the previously
+ * pressed button, we invent the button up before sending
+ * the button down.
+ *
+ * Therefore, front ends can just send whatever data they
+ * happen to be conveniently able to get, and back ends can be
+ * guaranteed of always receiving a down, zero or more drags
+ * and an up for a single button at a time.
+ */
+ if (IS_MOUSE_DRAG(button) || IS_MOUSE_RELEASE(button)) {
+ int which;
+
+ if (IS_MOUSE_DRAG(button))
+ which = button + (LEFT_BUTTON - LEFT_DRAG);
+ else
+ which = button + (LEFT_BUTTON - LEFT_RELEASE);
+
+ if (which != me->pressed_mouse_button) {
+ /*
+ * Fabricate a button-up for the currently pressed
+ * button, if any.
+ */
+ if (me->pressed_mouse_button) {
+ ret = ret && midend_really_process_key
+ (me, x, y, (me->pressed_mouse_button +
+ (LEFT_RELEASE - LEFT_BUTTON)));
+ }
+
+ /*
+ * Now fabricate a button-down for this one.
+ */
+ ret = ret && midend_really_process_key(me, x, y, which);
+
+ /*
+ * And set the currently pressed button to this one.
+ */
+ me->pressed_mouse_button = which;
+ }
+ } else if (IS_MOUSE_DOWN(button) && me->pressed_mouse_button) {
+ /*
+ * Fabricate a button-up for the previously pressed button.
+ */
+ ret = ret && midend_really_process_key
+ (me, x, y, (me->pressed_mouse_button +
+ (LEFT_RELEASE - LEFT_BUTTON)));
+ }
+
+ /*
+ * Now send on the event we originally received.
+ */
+ ret = ret && midend_really_process_key(me, x, y, button);
+
+ /*
+ * And update the currently pressed button.
+ */
+ if (IS_MOUSE_RELEASE(button))
+ me->pressed_mouse_button = 0;
+ else if (IS_MOUSE_DOWN(button))
+ me->pressed_mouse_button = button;
+
+ return ret;
}
void midend_redraw(midend_data *me)
--- a/puzzles.h
+++ b/puzzles.h
@@ -34,6 +34,13 @@
CURSOR_DOWN_RIGHT
};
+#define IS_MOUSE_DOWN(m) ( (unsigned)((m) - LEFT_BUTTON) <= \
+ (unsigned)(RIGHT_BUTTON - LEFT_BUTTON))
+#define IS_MOUSE_DRAG(m) ( (unsigned)((m) - LEFT_DRAG) <= \
+ (unsigned)(RIGHT_DRAG - LEFT_DRAG))
+#define IS_MOUSE_RELEASE(m) ( (unsigned)((m) - LEFT_RELEASE) <= \
+ (unsigned)(RIGHT_RELEASE - LEFT_RELEASE))
+
#define IGNOREARG(x) ( (x) = (x) )
typedef struct frontend frontend;