Keyboard control patch for Twiddle, from James H.

[originally from svn r8438]
This commit is contained in:
Simon Tatham
2009-01-28 18:27:10 +00:00
parent cd3a7d89ce
commit 8a3f525a54
2 changed files with 99 additions and 21 deletions

View File

@ -707,6 +707,11 @@ the centre tile of the square you want to rotate.
Clicking with the left mouse button rotates the group anticlockwise. Clicking with the left mouse button rotates the group anticlockwise.
Clicking with the right button rotates it clockwise. Clicking with the right button rotates it clockwise.
You can also move an outline square around the grid with the cursor
keys; the square is the size above (2\by\.2 by default, or larger).
Pressing the return key or space bar will rotate the current square
anticlockwise or clockwise respectively.
(All the actions described in \k{common-actions} are also available.) (All the actions described in \k{common-actions} are also available.)
\H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters \H{twiddle-parameters} \I{parameters, for Twiddle}Twiddle parameters

115
twiddle.c
View File

@ -31,6 +31,7 @@ enum {
COL_HIGHLIGHT_GENTLE, COL_HIGHLIGHT_GENTLE,
COL_LOWLIGHT, COL_LOWLIGHT,
COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE,
COL_HIGHCURSOR, COL_LOWCURSOR,
NCOLOURS NCOLOURS
}; };
@ -597,13 +598,25 @@ static char *game_text_format(game_state *state)
return ret; return ret;
} }
struct game_ui {
int cur_x, cur_y;
int cur_visible;
};
static game_ui *new_ui(game_state *state) static game_ui *new_ui(game_state *state)
{ {
return NULL; game_ui *ui = snew(game_ui);
ui->cur_x = 0;
ui->cur_y = 0;
ui->cur_visible = FALSE;
return ui;
} }
static void free_ui(game_ui *ui) static void free_ui(game_ui *ui)
{ {
sfree(ui);
} }
static char *encode_ui(game_ui *ui) static char *encode_ui(game_ui *ui)
@ -625,6 +638,7 @@ struct game_drawstate {
int w, h, bgcolour; int w, h, bgcolour;
int *grid; int *grid;
int tilesize; int tilesize;
int cur_x, cur_y;
}; };
static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds, static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
@ -636,6 +650,19 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
button = button & (~MOD_MASK | MOD_NUM_KEYPAD); button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
if (IS_CURSOR_MOVE(button)) {
if (button == CURSOR_LEFT && ui->cur_x > 0)
ui->cur_x--;
if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w))
ui->cur_x++;
if (button == CURSOR_UP && ui->cur_y > 0)
ui->cur_y--;
if (button == CURSOR_DOWN && (ui->cur_y+n) < (h))
ui->cur_y++;
ui->cur_visible = 1;
return "";
}
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
/* /*
* Determine the coordinates of the click. We offset by n-1 * Determine the coordinates of the click. We offset by n-1
@ -649,6 +676,16 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
dir = (button == LEFT_BUTTON ? 1 : -1); dir = (button == LEFT_BUTTON ? 1 : -1);
if (x < 0 || x > w-n || y < 0 || y > h-n) if (x < 0 || x > w-n || y < 0 || y > h-n)
return NULL; return NULL;
ui->cur_visible = 0;
} else if (IS_CURSOR_SELECT(button)) {
if (ui->cur_visible) {
x = ui->cur_x;
y = ui->cur_y;
dir = (button == CURSOR_SELECT2) ? -1 : +1;
} else {
ui->cur_visible = 1;
return "";
}
} else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') { } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') {
x = y = 0; x = y = 0;
dir = (button == 'A' ? -1 : +1); dir = (button == 'A' ? -1 : +1);
@ -770,10 +807,16 @@ static float *game_colours(frontend *fe, int *ncolours)
game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT); game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
/* cursor is light-background with a red tinge. */
ret[COL_HIGHCURSOR * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.0F;
ret[COL_HIGHCURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.5F;
ret[COL_HIGHCURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.5F;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F; ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F;
ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F; ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F;
ret[COL_TEXT * 3 + i] = 0.0; ret[COL_TEXT * 3 + i] = 0.0;
ret[COL_LOWCURSOR * 3 + i] = ret[COL_HIGHCURSOR * 3 + i] * 0.6F;
} }
*ncolours = NCOLOURS; *ncolours = NCOLOURS;
@ -793,6 +836,7 @@ static game_drawstate *game_new_drawstate(drawing *dr, game_state *state)
ds->tilesize = 0; /* haven't decided yet */ ds->tilesize = 0; /* haven't decided yet */
for (i = 0; i < ds->w*ds->h; i++) for (i = 0; i < ds->w*ds->h; i++)
ds->grid[i] = -1; ds->grid[i] = -1;
ds->cur_x = ds->cur_y = -state->n;
return ds; return ds;
} }
@ -813,20 +857,25 @@ struct rotation {
static void rotate(int *xy, struct rotation *rot) static void rotate(int *xy, struct rotation *rot)
{ {
if (rot) { if (rot) {
float xf = xy[0] - rot->ox, yf = xy[1] - rot->oy; float xf = (float)xy[0] - rot->ox, yf = (float)xy[1] - rot->oy;
float xf2, yf2; float xf2, yf2;
xf2 = rot->c * xf + rot->s * yf; xf2 = rot->c * xf + rot->s * yf;
yf2 = - rot->s * xf + rot->c * yf; yf2 = - rot->s * xf + rot->c * yf;
xy[0] = xf2 + rot->ox + 0.5; /* round to nearest */ xy[0] = (int)(xf2 + rot->ox + 0.5); /* round to nearest */
xy[1] = yf2 + rot->oy + 0.5; /* round to nearest */ xy[1] = (int)(yf2 + rot->oy + 0.5); /* round to nearest */
} }
} }
#define CUR_TOP 1
#define CUR_RIGHT 2
#define CUR_BOTTOM 4
#define CUR_LEFT 8
static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state, static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
int x, int y, int tile, int flash_colour, int x, int y, int tile, int flash_colour,
struct rotation *rot) struct rotation *rot, unsigned cedges)
{ {
int coords[8]; int coords[8];
char str[40]; char str[40];
@ -863,28 +912,28 @@ static void draw_tile(drawing *dr, game_drawstate *ds, game_state *state,
coords[3] = y; coords[3] = y;
rotate(coords+2, rot); rotate(coords+2, rot);
draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT, draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT,
rot ? rot->rc : COL_LOWLIGHT); rot ? rot->rc : (cedges & CUR_RIGHT) ? COL_LOWCURSOR : COL_LOWLIGHT);
/* Bottom side. */ /* Bottom side. */
coords[2] = x; coords[2] = x;
coords[3] = y + TILE_SIZE - 1; coords[3] = y + TILE_SIZE - 1;
rotate(coords+2, rot); rotate(coords+2, rot);
draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT, draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT,
rot ? rot->bc : COL_LOWLIGHT); rot ? rot->bc : (cedges & CUR_BOTTOM) ? COL_LOWCURSOR : COL_LOWLIGHT);
/* Left side. */ /* Left side. */
coords[0] = x; coords[0] = x;
coords[1] = y; coords[1] = y;
rotate(coords+0, rot); rotate(coords+0, rot);
draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT, draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT,
rot ? rot->lc : COL_HIGHLIGHT); rot ? rot->lc : (cedges & CUR_LEFT) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
/* Top side. */ /* Top side. */
coords[2] = x + TILE_SIZE - 1; coords[2] = x + TILE_SIZE - 1;
coords[3] = y; coords[3] = y;
rotate(coords+2, rot); rotate(coords+2, rot);
draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT, draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT,
rot ? rot->tc : COL_HIGHLIGHT); rot ? rot->tc : (cedges & CUR_TOP) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
/* /*
* Now the main blank area in the centre of the tile. * Now the main blank area in the centre of the tile.
@ -1008,7 +1057,7 @@ static int highlight_colour(float angle)
static float game_anim_length(game_state *oldstate, game_state *newstate, static float game_anim_length(game_state *oldstate, game_state *newstate,
int dir, game_ui *ui) int dir, game_ui *ui)
{ {
return ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1); return (float)(ANIM_PER_RADIUS_UNIT * sqrt(newstate->n-1));
} }
static float game_flash_length(game_state *oldstate, game_state *newstate, static float game_flash_length(game_state *oldstate, game_state *newstate,
@ -1028,6 +1077,12 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
int i, bgcolour; int i, bgcolour;
struct rotation srot, *rot; struct rotation srot, *rot;
int lastx = -1, lasty = -1, lastr = -1; int lastx = -1, lasty = -1, lastr = -1;
int cx, cy, cmoved = 0, n = state->n;
cx = ui->cur_visible ? ui->cur_x : -state->n;
cy = ui->cur_visible ? ui->cur_y : -state->n;
if (cx != ds->cur_x || cy != ds->cur_y)
cmoved = 1;
if (flashtime > 0) { if (flashtime > 0) {
int frame = (int)(flashtime / FLASH_FRAME); int frame = (int)(flashtime / FLASH_FRAME);
@ -1092,17 +1147,17 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
rot->cw = rot->ch = TILE_SIZE * state->n; rot->cw = rot->ch = TILE_SIZE * state->n;
rot->ox = rot->cx + rot->cw/2; rot->ox = rot->cx + rot->cw/2;
rot->oy = rot->cy + rot->ch/2; rot->oy = rot->cy + rot->ch/2;
angle = (-PI/2 * lastr) * (1.0 - animtime / anim_max); angle = (float)((-PI/2 * lastr) * (1.0 - animtime / anim_max));
rot->c = cos(angle); rot->c = (float)cos(angle);
rot->s = sin(angle); rot->s = (float)sin(angle);
/* /*
* Sort out the colours of the various sides of the tile. * Sort out the colours of the various sides of the tile.
*/ */
rot->lc = highlight_colour(PI + angle); rot->lc = highlight_colour((float)PI + angle);
rot->rc = highlight_colour(angle); rot->rc = highlight_colour(angle);
rot->tc = highlight_colour(PI/2 + angle); rot->tc = highlight_colour((float)(PI/2.0) + angle);
rot->bc = highlight_colour(-PI/2 + angle); rot->bc = highlight_colour((float)(-PI/2.0) + angle);
draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour); draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour);
} else } else
@ -1112,7 +1167,7 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
* Now draw each tile. * Now draw each tile.
*/ */
for (i = 0; i < state->w * state->h; i++) { for (i = 0; i < state->w * state->h; i++) {
int t; int t, cc = 0;
int tx = i % state->w, ty = i / state->w; int tx = i % state->w, ty = i / state->w;
/* /*
@ -1129,15 +1184,31 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
else else
t = state->grid[i]; t = state->grid[i];
if (ds->bgcolour != bgcolour || /* always redraw when flashing */ if (cmoved) {
ds->grid[i] != t || ds->grid[i] == -1 || t == -1) { /* cursor has moved (or changed visibility)... */
int x = COORD(tx), y = COORD(ty); if (tx == cx || tx == cx+n-1 || ty == cy || ty == cy+n-1)
cc = 1; /* ...we're on new cursor, redraw */
if (tx == ds->cur_x || tx == ds->cur_x+n-1 ||
ty == ds->cur_y || ty == ds->cur_y+n-1)
cc = 1; /* ...we were on old cursor, redraw */
}
draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot); if (ds->bgcolour != bgcolour || /* always redraw when flashing */
ds->grid[i] != t || ds->grid[i] == -1 || t == -1 || cc) {
int x = COORD(tx), y = COORD(ty);
unsigned cedges = 0;
if (tx == cx && ty >= cy && ty <= cy+n-1) cedges |= CUR_LEFT;
if (ty == cy && tx >= cx && tx <= cx+n-1) cedges |= CUR_TOP;
if (tx == cx+n-1 && ty >= cy && ty <= cy+n-1) cedges |= CUR_RIGHT;
if (ty == cy+n-1 && tx >= cx && tx <= cx+n-1) cedges |= CUR_BOTTOM;
draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot, cedges);
ds->grid[i] = t; ds->grid[i] = t;
} }
} }
ds->bgcolour = bgcolour; ds->bgcolour = bgcolour;
ds->cur_x = cx; ds->cur_y = cy;
/* /*
* Update the status bar. * Update the status bar.
@ -1221,3 +1292,5 @@ const struct game thegame = {
FALSE, game_timing_state, FALSE, game_timing_state,
0, /* flags */ 0, /* flags */
}; };
/* vim: set shiftwidth=4 tabstop=8: */