Expose the NO_EFFECT/UNUSED distinction through midend_process_key()

This removed the "handled" pointer and instead extends the existing
boolean return value (quit or don't quit) into an enumeration.  One of
the values still quits the program, but now there are different values
for keys that had an effect, had no effect, and are not used by the
puzzle at all.  The mapping from interpret_move results to process_key
results is roughly:

move string    -> PKR_SOME_EFFECT
MOVE_UI_UPDATE -> PKR_SOME_EFFECT
MOVE_NO_EFFECT -> PKR_NO_EFFECT
MOVE_UNUSED    -> PKR_UNUSED

The mid-end can also generate results internally, and is the only place
that PKR_QUIT can arise.

For compatibility, PKR_QUIT is zero, so anything expecting a false
return value to mean quit will be unsurprised.  The other values are
ordered so that lower values indicate a greater amount of handling of
the key.
This commit is contained in:
Ben Harris
2023-06-07 23:03:30 +01:00
parent 87e98e6715
commit 1547154efb
8 changed files with 97 additions and 71 deletions

View File

@ -3291,8 +3291,7 @@ call to this function. Some back ends require that \cw{midend_size()}
\H{midend-process-key} \cw{midend_process_key()} \H{midend-process-key} \cw{midend_process_key()}
\c bool midend_process_key(midend *me, int x, int y, int button, \c int midend_process_key(midend *me, int x, int y, int button)
\c bool *handled);
The front end calls this function to report a mouse or keyboard event. The front end calls this function to report a mouse or keyboard event.
The parameters \c{x} and \c{y} are identical to the ones passed to the The parameters \c{x} and \c{y} are identical to the ones passed to the
@ -3330,16 +3329,33 @@ Calling this function is very likely to result in calls back to the
front end's drawing API and/or \cw{activate_timer()} front end's drawing API and/or \cw{activate_timer()}
(\k{frontend-activate-timer}). (\k{frontend-activate-timer}).
The return value from \cw{midend_process_key()} is \cw{true} unless The return value from \cw{midend_process_key()} is one of the
the effect of the keypress was to request termination of the program. following constants:
A front end should shut down the puzzle in response to a \cw{false}
return.
If the front end passes in a non-NULL pointer in \c{handled}, the \dt \cw{PKR_QUIT}
mid-end will set \cw{*handled} to \cw{true} if it or the backend does
something in response to the keypress. A front end can use this to \dd Means that the effect of the keypress was to request termination
decide whether to pass the keypress on to anything else that might of the program. A front end should shut down the puzzle in response
want to do something in response to it. to a \cw{PKR_QUIT} return.
\dt \cw{PKR_SOME_EFFECT}
\dd The keypress had some other effect, either in the mid-end or in
the puzzle itself.
\dt \cw{PKR_NO_EFFECT}
\dd The keypress had no effect, but might have had an effect in
slightly different circumstances. For instance it requested a move
that wasn't possible.
\dt \cw{PKR_UNUSED}
\dd The key was one that neither the mid-end nor the back-end has any
use for at all.
A front end might respond to the last value by passing the key on to
something else that might be interested in it.
The following additional values of \c{button} are permitted to be The following additional values of \c{button} are permitted to be
passed to this function by the front end, but are never passed on to passed to this function by the front end, but are never passed on to

25
emcc.c
View File

@ -295,7 +295,7 @@ bool mousedown(int x, int y, int button)
button = (button == 0 ? LEFT_BUTTON : button = (button == 0 ? LEFT_BUTTON :
button == 1 ? MIDDLE_BUTTON : RIGHT_BUTTON); button == 1 ? MIDDLE_BUTTON : RIGHT_BUTTON);
midend_process_key(me, x, y, button, &handled); handled = midend_process_key(me, x, y, button) != PKR_UNUSED;
post_move(); post_move();
return handled; return handled;
} }
@ -306,7 +306,7 @@ bool mouseup(int x, int y, int button)
button = (button == 0 ? LEFT_RELEASE : button = (button == 0 ? LEFT_RELEASE :
button == 1 ? MIDDLE_RELEASE : RIGHT_RELEASE); button == 1 ? MIDDLE_RELEASE : RIGHT_RELEASE);
midend_process_key(me, x, y, button, &handled); handled = midend_process_key(me, x, y, button) != PKR_UNUSED;
post_move(); post_move();
return handled; return handled;
} }
@ -317,7 +317,7 @@ bool mousemove(int x, int y, int buttons)
buttons & 4 ? RIGHT_DRAG : LEFT_DRAG); buttons & 4 ? RIGHT_DRAG : LEFT_DRAG);
bool handled; bool handled;
midend_process_key(me, x, y, button, &handled); handled = midend_process_key(me, x, y, button) != PKR_UNUSED;
post_move(); post_move();
return handled; return handled;
} }
@ -335,7 +335,7 @@ bool key(int keycode, const char *key, const char *chr, int location,
#define DOM_KEY_LOCATION_RIGHT 2 #define DOM_KEY_LOCATION_RIGHT 2
#define DOM_KEY_LOCATION_NUMPAD 3 #define DOM_KEY_LOCATION_NUMPAD 3
int keyevent = -1; int keyevent = -1;
bool handled; int process_key_result;
if (!strnullcmp(key, "Backspace") || !strnullcmp(key, "Delete") || if (!strnullcmp(key, "Backspace") || !strnullcmp(key, "Delete") ||
!strnullcmp(key, "Del")) !strnullcmp(key, "Del"))
@ -422,9 +422,16 @@ bool key(int keycode, const char *key, const char *chr, int location,
if (ctrl) keyevent |= MOD_CTRL; if (ctrl) keyevent |= MOD_CTRL;
if (location == DOM_KEY_LOCATION_NUMPAD) keyevent |= MOD_NUM_KEYPAD; if (location == DOM_KEY_LOCATION_NUMPAD) keyevent |= MOD_NUM_KEYPAD;
midend_process_key(me, 0, 0, keyevent, &handled); process_key_result = midend_process_key(me, 0, 0, keyevent);
post_move(); post_move();
return handled; /*
* Treat Backspace specially because that's expected on KaiOS.
* https://developer.kaiostech.com/docs/design-guide/key
*/
if (process_key_result == PKR_NO_EFFECT &&
!strnullcmp(key, "Backspace"))
return false;
return process_key_result != PKR_UNUSED;
} }
return false; /* Event not handled, because we don't even recognise it. */ return false; /* Event not handled, because we don't even recognise it. */
} }
@ -839,7 +846,7 @@ void command(int n)
post_move(); post_move();
break; break;
case 5: /* New Game */ case 5: /* New Game */
midend_process_key(me, 0, 0, UI_NEWGAME, NULL); midend_process_key(me, 0, 0, UI_NEWGAME);
post_move(); post_move();
js_focus_canvas(); js_focus_canvas();
break; break;
@ -849,12 +856,12 @@ void command(int n)
js_focus_canvas(); js_focus_canvas();
break; break;
case 7: /* Undo */ case 7: /* Undo */
midend_process_key(me, 0, 0, UI_UNDO, NULL); midend_process_key(me, 0, 0, UI_UNDO);
post_move(); post_move();
js_focus_canvas(); js_focus_canvas();
break; break;
case 8: /* Redo */ case 8: /* Redo */
midend_process_key(me, 0, 0, UI_REDO, NULL); midend_process_key(me, 0, 0, UI_REDO);
post_move(); post_move();
js_focus_canvas(); js_focus_canvas();
break; break;

14
gtk.c
View File

@ -1547,7 +1547,7 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
keyval = -1; keyval = -1;
if (keyval >= 0 && if (keyval >= 0 &&
!midend_process_key(fe->me, 0, 0, keyval, NULL)) midend_process_key(fe->me, 0, 0, keyval) == PKR_QUIT)
gtk_widget_destroy(fe->window); gtk_widget_destroy(fe->window);
return true; return true;
@ -1581,8 +1581,8 @@ static gint button_event(GtkWidget *widget, GdkEventButton *event,
if (event->type == GDK_BUTTON_RELEASE && button >= LEFT_BUTTON) if (event->type == GDK_BUTTON_RELEASE && button >= LEFT_BUTTON)
button += LEFT_RELEASE - LEFT_BUTTON; button += LEFT_RELEASE - LEFT_BUTTON;
if (!midend_process_key(fe->me, event->x - fe->ox, if (midend_process_key(fe->me, event->x - fe->ox,
event->y - fe->oy, button, NULL)) event->y - fe->oy, button) == PKR_QUIT)
gtk_widget_destroy(fe->window); gtk_widget_destroy(fe->window);
return true; return true;
@ -1606,8 +1606,8 @@ static gint motion_event(GtkWidget *widget, GdkEventMotion *event,
else else
return false; /* don't even know what button! */ return false; /* don't even know what button! */
if (!midend_process_key(fe->me, event->x - fe->ox, if (midend_process_key(fe->me, event->x - fe->ox,
event->y - fe->oy, button, NULL)) event->y - fe->oy, button) == PKR_QUIT)
gtk_widget_destroy(fe->window); gtk_widget_destroy(fe->window);
#if GTK_CHECK_VERSION(2,12,0) #if GTK_CHECK_VERSION(2,12,0)
gdk_event_request_motions(event); gdk_event_request_motions(event);
@ -2210,7 +2210,7 @@ static void menu_key_event(GtkMenuItem *menuitem, gpointer data)
frontend *fe = (frontend *)data; frontend *fe = (frontend *)data;
int key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), int key = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
"user-data")); "user-data"));
if (!midend_process_key(fe->me, 0, 0, key, NULL)) if (midend_process_key(fe->me, 0, 0, key) == PKR_QUIT)
gtk_widget_destroy(fe->window); gtk_widget_destroy(fe->window);
} }
@ -4377,7 +4377,7 @@ int main(int argc, char **argv)
if (redo_proportion) { if (redo_proportion) {
/* Start a redo. */ /* Start a redo. */
midend_process_key(fe->me, 0, 0, 'r', NULL); midend_process_key(fe->me, 0, 0, 'r');
/* And freeze the timer at the specified position. */ /* And freeze the timer at the specified position. */
midend_freeze_timer(fe->me, redo_proportion); midend_freeze_timer(fe->me, redo_proportion);
} }

View File

@ -973,13 +973,13 @@ void midend_restart_game(midend *me)
midend_set_timer(me); midend_set_timer(me);
} }
static bool midend_really_process_key(midend *me, int x, int y, int button, static int midend_really_process_key(midend *me, int x, int y, int button)
bool *handled)
{ {
game_state *oldstate = game_state *oldstate =
me->ourgame->dup_game(me->states[me->statepos - 1].state); me->ourgame->dup_game(me->states[me->statepos - 1].state);
int type = MOVE; int type = MOVE;
bool gottype = false, ret = true; bool gottype = false;
int ret = PKR_NO_EFFECT;
float anim_time; float anim_time;
game_state *s; game_state *s;
char *movestr = NULL; char *movestr = NULL;
@ -995,7 +995,7 @@ static bool midend_really_process_key(midend *me, int x, int y, int button,
button == '\x0E' || button == UI_NEWGAME) { button == '\x0E' || button == UI_NEWGAME) {
midend_new_game(me); midend_new_game(me);
midend_redraw(me); midend_redraw(me);
*handled = true; ret = PKR_SOME_EFFECT;
goto done; /* never animate */ goto done; /* never animate */
} else if ((me->one_key_shortcuts && (button=='u' || button=='U')) || } else if ((me->one_key_shortcuts && (button=='u' || button=='U')) ||
button == '*' || button == '\x1A' || button == '\x1F' || button == '*' || button == '\x1A' || button == '\x1F' ||
@ -1005,30 +1005,32 @@ static bool midend_really_process_key(midend *me, int x, int y, int button,
gottype = true; gottype = true;
if (!midend_undo(me)) if (!midend_undo(me))
goto done; goto done;
*handled = true; ret = PKR_SOME_EFFECT;
} else if ((me->one_key_shortcuts && (button=='r' || button=='R')) || } else if ((me->one_key_shortcuts && (button=='r' || button=='R')) ||
button == '#' || button == '\x12' || button == '\x19' || button == '#' || button == '\x12' || button == '\x19' ||
button == UI_REDO) { button == UI_REDO) {
midend_stop_anim(me); midend_stop_anim(me);
if (!midend_redo(me)) if (!midend_redo(me))
goto done; goto done;
*handled = true; ret = PKR_SOME_EFFECT;
} else if ((button == '\x13' || button == UI_SOLVE) && } else if ((button == '\x13' || button == UI_SOLVE) &&
me->ourgame->can_solve) { me->ourgame->can_solve) {
*handled = true; ret = PKR_SOME_EFFECT;
if (midend_solve(me)) if (midend_solve(me))
goto done; goto done;
} else if ((me->one_key_shortcuts && (button=='q' || button=='Q')) || } else if ((me->one_key_shortcuts && (button=='q' || button=='Q')) ||
button == '\x11' || button == UI_QUIT) { button == '\x11' || button == UI_QUIT) {
ret = false; ret = PKR_QUIT;
*handled = true;
goto done; goto done;
} else } else {
ret = PKR_UNUSED;
goto done; goto done;
}
} else if (movestr == MOVE_NO_EFFECT) { } else if (movestr == MOVE_NO_EFFECT) {
ret = PKR_NO_EFFECT;
goto done; goto done;
} else { } else {
*handled = true; ret = PKR_SOME_EFFECT;
if (movestr == MOVE_UI_UPDATE) if (movestr == MOVE_UI_UPDATE)
s = me->states[me->statepos-1].state; s = me->states[me->statepos-1].state;
else { else {
@ -1099,12 +1101,10 @@ static bool midend_really_process_key(midend *me, int x, int y, int button,
return ret; return ret;
} }
bool midend_process_key(midend *me, int x, int y, int button, bool *handled) int midend_process_key(midend *me, int x, int y, int button)
{ {
bool ret = true, dummy_handled; int ret = PKR_UNUSED, ret2;
if (handled == NULL) handled = &dummy_handled;
*handled = false;
/* /*
* Harmonise mouse drag and release messages. * Harmonise mouse drag and release messages.
* *
@ -1206,9 +1206,10 @@ bool midend_process_key(midend *me, int x, int y, int button, bool *handled)
/* /*
* Fabricate a button-up for the previously pressed button. * Fabricate a button-up for the previously pressed button.
*/ */
ret = ret && midend_really_process_key ret2 = midend_really_process_key
(me, x, y, (me->pressed_mouse_button + (me, x, y, (me->pressed_mouse_button +
(LEFT_RELEASE - LEFT_BUTTON)), handled); (LEFT_RELEASE - LEFT_BUTTON)));
ret = min(ret, ret2);
} }
/* Canonicalise CTRL+ASCII. */ /* Canonicalise CTRL+ASCII. */
@ -1243,7 +1244,8 @@ bool midend_process_key(midend *me, int x, int y, int button, bool *handled)
/* /*
* Now send on the event we originally received. * Now send on the event we originally received.
*/ */
ret = ret && midend_really_process_key(me, x, y, button, handled); ret2 = midend_really_process_key(me, x, y, button);
ret = min(ret, ret2);
/* /*
* And update the currently pressed button. * And update the currently pressed button.

View File

@ -206,7 +206,7 @@ int jcallback_key_event(int x, int y, int keyval)
if (fe->ox == -1) if (fe->ox == -1)
return 1; return 1;
if (keyval >= 0 && if (keyval >= 0 &&
!midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval, NULL)) midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval) == PKR_QUIT)
return 42; return 42;
return 1; return 1;
} }
@ -323,7 +323,7 @@ static bool get_config(frontend *fe, int which)
int jcallback_newgame_event(void) int jcallback_newgame_event(void)
{ {
frontend *fe = (frontend *)_fe; frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME, NULL)) if (midend_process_key(fe->me, 0, 0, UI_NEWGAME) == PKR_QUIT)
return 42; return 42;
return 0; return 0;
} }
@ -331,7 +331,7 @@ int jcallback_newgame_event(void)
int jcallback_undo_event(void) int jcallback_undo_event(void)
{ {
frontend *fe = (frontend *)_fe; frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_UNDO, NULL)) if (midend_process_key(fe->me, 0, 0, UI_UNDO) == PKR_QUIT)
return 42; return 42;
return 0; return 0;
} }
@ -339,7 +339,7 @@ int jcallback_undo_event(void)
int jcallback_redo_event(void) int jcallback_redo_event(void)
{ {
frontend *fe = (frontend *)_fe; frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_REDO, NULL)) if (midend_process_key(fe->me, 0, 0, UI_REDO) == PKR_QUIT)
return 42; return 42;
return 0; return 0;
} }
@ -347,7 +347,7 @@ int jcallback_redo_event(void)
int jcallback_quit_event(void) int jcallback_quit_event(void)
{ {
frontend *fe = (frontend *)_fe; frontend *fe = (frontend *)_fe;
if (!midend_process_key(fe->me, 0, 0, UI_QUIT, NULL)) if (midend_process_key(fe->me, 0, 0, UI_QUIT) == PKR_QUIT)
return 42; return 42;
return 0; return 0;
} }

4
osx.m
View File

@ -767,13 +767,13 @@ struct frontend {
- (void)processButton:(int)b x:(int)x y:(int)y - (void)processButton:(int)b x:(int)x y:(int)y
{ {
if (!midend_process_key(me, x, fe.h - 1 - y, b, NULL)) if (midend_process_key(me, x, fe.h - 1 - y, b) == PKR_QUIT)
[self close]; [self close];
} }
- (void)processKey:(int)b - (void)processKey:(int)b
{ {
if (!midend_process_key(me, -1, -1, b, NULL)) if (midend_process_key(me, -1, -1, b) == PKR_QUIT)
[self close]; [self close];
} }

View File

@ -320,7 +320,8 @@ void midend_reset_tilesize(midend *me);
void midend_new_game(midend *me); void midend_new_game(midend *me);
void midend_restart_game(midend *me); void midend_restart_game(midend *me);
void midend_stop_anim(midend *me); void midend_stop_anim(midend *me);
bool midend_process_key(midend *me, int x, int y, int button, bool *handled); enum { PKR_QUIT = 0, PKR_SOME_EFFECT, PKR_NO_EFFECT, PKR_UNUSED };
int midend_process_key(midend *me, int x, int y, int button);
key_label *midend_request_keys(midend *me, int *nkeys); key_label *midend_request_keys(midend *me, int *nkeys);
const char *midend_current_key_label(midend *me, int button); const char *midend_current_key_label(midend *me, int button);
void midend_force_redraw(midend *me); void midend_force_redraw(midend *me);

View File

@ -2678,18 +2678,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */ cmd = wParam & ~0xF; /* low 4 bits reserved to Windows */
switch (cmd) { switch (cmd) {
case IDM_NEW: case IDM_NEW:
if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME, NULL)) if (midend_process_key(fe->me, 0, 0, UI_NEWGAME) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
break; break;
case IDM_RESTART: case IDM_RESTART:
midend_restart_game(fe->me); midend_restart_game(fe->me);
break; break;
case IDM_UNDO: case IDM_UNDO:
if (!midend_process_key(fe->me, 0, 0, UI_UNDO, NULL)) if (midend_process_key(fe->me, 0, 0, UI_UNDO) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
break; break;
case IDM_REDO: case IDM_REDO:
if (!midend_process_key(fe->me, 0, 0, UI_REDO, NULL)) if (midend_process_key(fe->me, 0, 0, UI_REDO) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
break; break;
case IDM_COPY: case IDM_COPY:
@ -2711,7 +2711,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} }
break; break;
case IDM_QUIT: case IDM_QUIT:
if (!midend_process_key(fe->me, 0, 0, UI_QUIT, NULL)) if (midend_process_key(fe->me, 0, 0, UI_QUIT) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
break; break;
case IDM_CONFIG: case IDM_CONFIG:
@ -2998,7 +2998,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
} }
if (key != -1) { if (key != -1) {
if (!midend_process_key(fe->me, 0, 0, key, NULL)) if (midend_process_key(fe->me, 0, 0, key) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
} else { } else {
MSG m; MSG m;
@ -3028,10 +3028,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
else else
button = LEFT_BUTTON; button = LEFT_BUTTON;
if (!midend_process_key(fe->me, if (midend_process_key(fe->me,
(signed short)LOWORD(lParam) - fe->bitmapPosition.left, (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
(signed short)HIWORD(lParam) - fe->bitmapPosition.top, (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
button, NULL)) button) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
SetCapture(hwnd); SetCapture(hwnd);
@ -3055,10 +3055,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
else else
button = LEFT_RELEASE; button = LEFT_RELEASE;
if (!midend_process_key(fe->me, if (midend_process_key(fe->me,
(signed short)LOWORD(lParam) - fe->bitmapPosition.left, (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
(signed short)HIWORD(lParam) - fe->bitmapPosition.top, (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
button, NULL)) button) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
ReleaseCapture(); ReleaseCapture();
@ -3075,10 +3075,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
else else
button = LEFT_DRAG; button = LEFT_DRAG;
if (!midend_process_key(fe->me, if (midend_process_key(fe->me,
(signed short)LOWORD(lParam) - fe->bitmapPosition.left, (signed short)LOWORD(lParam) - fe->bitmapPosition.left,
(signed short)HIWORD(lParam) - fe->bitmapPosition.top, (signed short)HIWORD(lParam) - fe->bitmapPosition.top,
button, NULL)) button) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
} }
break; break;
@ -3092,7 +3092,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
(keystate[VK_CONTROL] & 0x80)) (keystate[VK_CONTROL] & 0x80))
key = UI_REDO; key = UI_REDO;
} }
if (!midend_process_key(fe->me, 0, 0, key, NULL)) if (midend_process_key(fe->me, 0, 0, key) == PKR_QUIT)
PostQuitMessage(0); PostQuitMessage(0);
} }
return 0; return 0;