mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 16:05:44 -07:00
Patch from Jonas Koelker to add keyboard control support to Pearl.
[originally from svn r9411]
This commit is contained in:
134
pearl.c
134
pearl.c
@ -5,10 +5,17 @@
|
|||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
*
|
*
|
||||||
* - Keyboard-control cursor. (Would probably have to address both
|
* - The current keyboard cursor mechanism works well on ordinary PC
|
||||||
* square centres, for laying multiple edges at a time in a
|
* keyboards, but for platforms with only arrow keys and a select
|
||||||
* drag-like style, and grid edges for marking particular line
|
* button or two, we may at some point need a simpler one which can
|
||||||
* segments as no-go.)
|
* handle 'x' markings without needing shift keys. For instance, a
|
||||||
|
* cursor with twice the grid resolution, so that it can range
|
||||||
|
* across face centres, edge centres and vertices; 'clicks' on face
|
||||||
|
* centres begin a drag as currently, clicks on edges toggle
|
||||||
|
* markings, and clicks on vertices are ignored (but it would be
|
||||||
|
* too confusing not to let the cursor rest on them). But I'm
|
||||||
|
* pretty sure that would be less pleasant to play on a full
|
||||||
|
* keyboard, so probably a #ifdef would be the thing.
|
||||||
*
|
*
|
||||||
* - Generation is still pretty slow, due to difficulty coming up in
|
* - Generation is still pretty slow, due to difficulty coming up in
|
||||||
* the first place with a loop that makes a soluble puzzle even
|
* the first place with a loop that makes a soluble puzzle even
|
||||||
@ -83,6 +90,7 @@
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
|
COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT,
|
||||||
|
COL_CURSOR_BACKGROUND = COL_LOWLIGHT,
|
||||||
COL_BLACK, COL_WHITE,
|
COL_BLACK, COL_WHITE,
|
||||||
COL_ERROR, COL_GRID, COL_FLASH,
|
COL_ERROR, COL_GRID, COL_FLASH,
|
||||||
COL_DRAGON, COL_DRAGOFF,
|
COL_DRAGON, COL_DRAGOFF,
|
||||||
@ -1720,6 +1728,9 @@ struct game_ui {
|
|||||||
int ndragcoords; /* number of entries in dragcoords.
|
int ndragcoords; /* number of entries in dragcoords.
|
||||||
* 0 = click but no drag yet. -1 = no drag at all */
|
* 0 = click but no drag yet. -1 = no drag at all */
|
||||||
int clickx, clicky; /* pixel position of initial click */
|
int clickx, clicky; /* pixel position of initial click */
|
||||||
|
|
||||||
|
int curx, cury; /* grid position of keyboard cursor */
|
||||||
|
int cursor_active; /* TRUE iff cursor is shown */
|
||||||
};
|
};
|
||||||
|
|
||||||
static game_ui *new_ui(game_state *state)
|
static game_ui *new_ui(game_state *state)
|
||||||
@ -1729,6 +1740,8 @@ static game_ui *new_ui(game_state *state)
|
|||||||
|
|
||||||
ui->ndragcoords = -1;
|
ui->ndragcoords = -1;
|
||||||
ui->dragcoords = snewn(sz, int);
|
ui->dragcoords = snewn(sz, int);
|
||||||
|
ui->cursor_active = FALSE;
|
||||||
|
ui->curx = ui->cury = 0;
|
||||||
|
|
||||||
return ui;
|
return ui;
|
||||||
}
|
}
|
||||||
@ -1762,6 +1775,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
|
|||||||
#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
|
#define BORDER_WIDTH (max(TILE_SIZE / 32, 1))
|
||||||
|
|
||||||
#define COORD(x) ( (x) * TILE_SIZE + BORDER )
|
#define COORD(x) ( (x) * TILE_SIZE + BORDER )
|
||||||
|
#define CENTERED_COORD(x) ( COORD(x) + TILE_SIZE/2 )
|
||||||
#define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) )
|
#define FROMCOORD(x) ( ((x) < BORDER) ? -1 : ( ((x) - BORDER) / TILE_SIZE) )
|
||||||
|
|
||||||
#define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */
|
#define DS_ESHIFT 4 /* R/U/L/D shift, for error flags */
|
||||||
@ -1770,6 +1784,7 @@ static void game_changed_state(game_ui *ui, game_state *oldstate,
|
|||||||
|
|
||||||
#define DS_ERROR_CLUE (1 << 20)
|
#define DS_ERROR_CLUE (1 << 20)
|
||||||
#define DS_FLASH (1 << 21)
|
#define DS_FLASH (1 << 21)
|
||||||
|
#define DS_CURSOR (1 << 22)
|
||||||
|
|
||||||
enum { GUI_MASYU, GUI_LOOPY };
|
enum { GUI_MASYU, GUI_LOOPY };
|
||||||
|
|
||||||
@ -1912,14 +1927,44 @@ static void interpret_ui_drag(game_state *state, game_ui *ui, int *clearing,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *mark_in_direction(game_state *state, int x, int y, int dir,
|
||||||
|
int ismark, char *buf)
|
||||||
|
{
|
||||||
|
int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */;
|
||||||
|
int x2 = x + DX(dir);
|
||||||
|
int y2 = y + DY(dir);
|
||||||
|
int dir2 = F(dir);
|
||||||
|
char ch = ismark ? 'M' : 'F';
|
||||||
|
|
||||||
|
if (!INGRID(state, x, y) || !INGRID(state, x2, y2)) return "";
|
||||||
|
/* disallow laying a mark over a line, or vice versa. */
|
||||||
|
if (ismark) {
|
||||||
|
if ((state->lines[y*w+x] & dir) || (state->lines[y2*w+x2] & dir2))
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
if ((state->marks[y*w+x] & dir) || (state->marks[y2*w+x2] & dir2))
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "%c%d,%d,%d;%c%d,%d,%d", ch, dir, x, y, ch, dir2, x2, y2);
|
||||||
|
return dupstr(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KEY_DIRECTION(btn) (\
|
||||||
|
(btn) == CURSOR_DOWN ? D : (btn) == CURSOR_UP ? U :\
|
||||||
|
(btn) == CURSOR_LEFT ? L : R)
|
||||||
|
|
||||||
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,
|
||||||
int x, int y, int button)
|
int x, int y, int button)
|
||||||
{
|
{
|
||||||
int w = state->shared->w /*, h = state->shared->h, sz = state->shared->sz */;
|
int w = state->shared->w, h = state->shared->h /*, sz = state->shared->sz */;
|
||||||
int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
|
int gx = FROMCOORD(x), gy = FROMCOORD(y), i;
|
||||||
|
int release = FALSE;
|
||||||
char tmpbuf[80];
|
char tmpbuf[80];
|
||||||
|
|
||||||
if (IS_MOUSE_DOWN(button)) {
|
if (IS_MOUSE_DOWN(button)) {
|
||||||
|
ui->cursor_active = FALSE;
|
||||||
|
|
||||||
if (!INGRID(state, gx, gy)) {
|
if (!INGRID(state, gx, gy)) {
|
||||||
ui->ndragcoords = -1;
|
ui->ndragcoords = -1;
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1937,7 +1982,44 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_MOUSE_RELEASE(button)) {
|
if (IS_MOUSE_RELEASE(button)) release = TRUE;
|
||||||
|
|
||||||
|
if (IS_CURSOR_MOVE(button & ~MOD_MASK)) {
|
||||||
|
if (!ui->cursor_active) {
|
||||||
|
ui->cursor_active = TRUE;
|
||||||
|
} else if (button & (MOD_SHFT | MOD_CTRL)) {
|
||||||
|
if (ui->ndragcoords > 0) return NULL;
|
||||||
|
ui->ndragcoords = -1;
|
||||||
|
return mark_in_direction(state, ui->curx, ui->cury,
|
||||||
|
KEY_DIRECTION(button & ~MOD_MASK),
|
||||||
|
(button & MOD_SHFT), tmpbuf);
|
||||||
|
} else {
|
||||||
|
move_cursor(button, &ui->curx, &ui->cury, w, h, FALSE);
|
||||||
|
if (ui->ndragcoords >= 0)
|
||||||
|
update_ui_drag(state, ui, ui->curx, ui->cury);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_CURSOR_SELECT(button & ~MOD_MASK)) {
|
||||||
|
if (!ui->cursor_active) {
|
||||||
|
ui->cursor_active = TRUE;
|
||||||
|
return "";
|
||||||
|
} else if (button == CURSOR_SELECT) {
|
||||||
|
if (ui->ndragcoords == -1) {
|
||||||
|
ui->ndragcoords = 0;
|
||||||
|
ui->dragcoords[0] = ui->cury * w + ui->curx;
|
||||||
|
ui->clickx = CENTERED_COORD(ui->curx);
|
||||||
|
ui->clicky = CENTERED_COORD(ui->cury);
|
||||||
|
return "";
|
||||||
|
} else release = TRUE;
|
||||||
|
} else if (button == CURSOR_SELECT2 && ui->ndragcoords >= 0) {
|
||||||
|
ui->ndragcoords = -1;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release) {
|
||||||
if (ui->ndragcoords > 0) {
|
if (ui->ndragcoords > 0) {
|
||||||
/* End of a drag: process the cached line data. */
|
/* End of a drag: process the cached line data. */
|
||||||
int buflen = 0, bufsize = 256, tmplen;
|
int buflen = 0, bufsize = 256, tmplen;
|
||||||
@ -1971,8 +2053,6 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
|
|||||||
/* Click (or tiny drag). Work out which edge we were
|
/* Click (or tiny drag). Work out which edge we were
|
||||||
* closest to. */
|
* closest to. */
|
||||||
int cx, cy;
|
int cx, cy;
|
||||||
int gx2, gy2, l1, l2, ismark = (button == RIGHT_RELEASE);
|
|
||||||
char movec = ismark ? 'M' : 'F';
|
|
||||||
|
|
||||||
ui->ndragcoords = -1;
|
ui->ndragcoords = -1;
|
||||||
|
|
||||||
@ -1986,8 +2066,8 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
|
|||||||
|
|
||||||
gx = FROMCOORD(x);
|
gx = FROMCOORD(x);
|
||||||
gy = FROMCOORD(y);
|
gy = FROMCOORD(y);
|
||||||
cx = COORD(gx) + TILE_SIZE/2;
|
cx = CENTERED_COORD(gx);
|
||||||
cy = COORD(gy) + TILE_SIZE/2;
|
cy = CENTERED_COORD(gy);
|
||||||
|
|
||||||
if (!INGRID(state, gx, gy)) return "";
|
if (!INGRID(state, gx, gy)) return "";
|
||||||
|
|
||||||
@ -1996,30 +2076,16 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
|
|||||||
|
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
|
int direction;
|
||||||
if (abs(x-cx) < abs(y-cy)) {
|
if (abs(x-cx) < abs(y-cy)) {
|
||||||
/* Closest to top/bottom edge. */
|
/* Closest to top/bottom edge. */
|
||||||
l1 = (y < cy) ? U : D;
|
direction = (y < cy) ? U : D;
|
||||||
} else {
|
} else {
|
||||||
/* Closest to left/right edge. */
|
/* Closest to left/right edge. */
|
||||||
l1 = (x < cx) ? L : R;
|
direction = (x < cx) ? L : R;
|
||||||
}
|
}
|
||||||
gx2 = gx + DX(l1); gy2 = gy + DY(l1);
|
return mark_in_direction(state, gx, gy, direction,
|
||||||
l2 = F(l1);
|
(button == RIGHT_RELEASE), tmpbuf);
|
||||||
|
|
||||||
if (!INGRID(state, gx, gy) || !INGRID(state, gx2, gy2)) return "";
|
|
||||||
|
|
||||||
/* disallow laying a mark over a line, or vice versa. */
|
|
||||||
if (ismark) {
|
|
||||||
if ((state->lines[gy*w+gx] & l1) || (state->lines[gy2*w+gx2] & l2))
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
if ((state->marks[gy*w+gx] & l1) || (state->marks[gy2*w+gx2] & l2))
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
sprintf(tmpbuf, "%c%d,%d,%d;%c%d,%d,%d",
|
|
||||||
movec, l1, gx, gy, movec, l2, gx2, gy2);
|
|
||||||
return dupstr(tmpbuf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2027,8 +2093,6 @@ static char *interpret_move(game_state *state, game_ui *ui, game_drawstate *ds,
|
|||||||
if (button == 'H' || button == 'h')
|
if (button == 'H' || button == 'h')
|
||||||
return dupstr("H");
|
return dupstr("H");
|
||||||
|
|
||||||
/* TODO cursor */
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2231,7 +2295,10 @@ static void draw_square(drawing *dr, game_drawstate *ds, game_ui *ui,
|
|||||||
clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
|
clip(dr, ox, oy, TILE_SIZE, TILE_SIZE);
|
||||||
|
|
||||||
/* Clear the square. */
|
/* Clear the square. */
|
||||||
draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE, COL_BACKGROUND);
|
draw_rect(dr, ox, oy, TILE_SIZE, TILE_SIZE,
|
||||||
|
(lflags & DS_CURSOR) ?
|
||||||
|
COL_CURSOR_BACKGROUND : COL_BACKGROUND);
|
||||||
|
|
||||||
|
|
||||||
if (get_gui_style() == GUI_LOOPY) {
|
if (get_gui_style() == GUI_LOOPY) {
|
||||||
/* Draw small dot, underneath any lines. */
|
/* Draw small dot, underneath any lines. */
|
||||||
@ -2355,6 +2422,9 @@ static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
|
|||||||
|
|
||||||
f |= flashing;
|
f |= flashing;
|
||||||
|
|
||||||
|
if (ui->cursor_active && x == ui->curx && y == ui->cury)
|
||||||
|
f |= DS_CURSOR;
|
||||||
|
|
||||||
if (f != ds->lflags[y*w+x] || force) {
|
if (f != ds->lflags[y*w+x] || force) {
|
||||||
ds->lflags[y*w+x] = f;
|
ds->lflags[y*w+x] = f;
|
||||||
draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]);
|
draw_square(dr, ds, ui, x, y, f, state->shared->clues[y*w+x]);
|
||||||
|
@ -2958,6 +2958,12 @@ indicating that you are sure the loop does not go through that edge.
|
|||||||
white clue has to be a corner, but don't yet know which way the corner
|
white clue has to be a corner, but don't yet know which way the corner
|
||||||
turns, you might mark the one way it \e{can't} go with a cross.)
|
turns, you might mark the one way it \e{can't} go with a cross.)
|
||||||
|
|
||||||
|
Alternatively, use the cursor keys to move the cursor. Use the Enter
|
||||||
|
key to begin and end keyboard `drag' operations. Use the Space key to
|
||||||
|
cancel the drag. Use Ctrl-arrowkey and Shift-arrowkey to simulate a
|
||||||
|
left or right click, respectively, on the edge in the given direction
|
||||||
|
relative to the cursor, i.e. to draw a segment or a cross.
|
||||||
|
|
||||||
(All the actions described in \k{common-actions} are also available.)
|
(All the actions described in \k{common-actions} are also available.)
|
||||||
|
|
||||||
\H{pearl-parameters} \I{parameters, for Pearl}Pearl parameters
|
\H{pearl-parameters} \I{parameters, for Pearl}Pearl parameters
|
||||||
|
Reference in New Issue
Block a user