diff --git a/common/board.c b/common/board.c index 568ad8df4..16b89e64b 100644 --- a/common/board.c +++ b/common/board.c @@ -1,4 +1,4 @@ -/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +E/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ /* * Copyright 1997 - 2002 by Eric House (xwords@eehouse.org). All rights reserved. * @@ -113,8 +113,7 @@ static XP_Bool advanceArrow( BoardCtxt* board ); #ifdef KEY_SUPPORT static XP_Bool getArrow( BoardCtxt* board, XP_U16* col, XP_U16* row ); -static XP_Bool board_moveArrow( BoardCtxt* board, XP_Key cursorKey, - XP_Bool canCycle ); +static XP_Bool board_moveArrow( BoardCtxt* board, XP_Key cursorKey ); static XP_Bool setArrowVisibleFor( BoardCtxt* board, XP_U16 player, XP_Bool visible ); @@ -1921,6 +1920,9 @@ invalCell( BoardCtxt* board, XP_U16 col, XP_U16 row ) { board->redrawFlags[row] |= 1 << col; + XP_ASSERT( col < MAX_ROWS ); + XP_ASSERT( row < MAX_ROWS ); + /* if the trade window is up and this cell intersects it, set up to draw it again */ if ( (board->trayVisState != TRAY_HIDDEN) && TRADE_IN_PROGRESS(board) ) { @@ -1936,29 +1938,6 @@ invalCell( BoardCtxt* board, XP_U16 col, XP_U16 row ) board->needsDrawing = XP_TRUE; } /* invalCell */ -#ifdef KEYBOARD_NAV -static XP_Bool -focusNext( BoardCtxt* board ) -{ - BoardObjectType typ; - switch ( board->focussed ) { - case OBJ_SCORE: - typ = OBJ_BOARD; - break; - case OBJ_BOARD: - typ = OBJ_TRAY; - break; - case OBJ_TRAY: - case OBJ_NONE: - typ = OBJ_SCORE; - break; - } - - board->focussed = typ; - return XP_TRUE; -} /* focusNext */ -#endif - #ifdef POINTER_SUPPORT static XP_Bool pointOnSomething( BoardCtxt* board, XP_U16 x, XP_U16 y, BoardObjectType* wp ) @@ -2738,6 +2717,10 @@ board_handleKey( BoardCtxt* board, XP_Key key ) } else if ( board->focussed == OBJ_TRAY ) { result = tray_moveCursor( board, key ); } + } else { + invalFocusOwner( board ); + shiftFocusUp( board, key ); + result = XP_TRUE; } break; #endif @@ -2751,12 +2734,7 @@ board_handleKey( BoardCtxt* board, XP_Key key ) #ifdef KEYBOARD_NAV case XP_FOCUSCHANGE_KEY: - invalFocusOwner( board ); - if ( board->focusHasDived ) { - board->focusHasDived = XP_FALSE; /* come back up */ - } else if ( focusNext( board ) ) { - invalFocusOwner( board ); - } + shiftFocusUp( board, XP_CURSOR_KEY_RIGHT ); result = XP_TRUE; break; @@ -2868,7 +2846,8 @@ board_focusChanged( BoardCtxt* board, BoardObjectType typ, XP_Bool gained ) draw = invalFocusOwner( board ) || draw; } board->focussed = typ; - XP_LOGF( "%s: set focussed to %d", __FUNCTION__, (int)typ ); + XP_LOGF( "%s: set focussed to %s", __FUNCTION__, + BoardObjectType_2str(typ) ); draw = invalFocusOwner( board ) || draw; board->focusHasDived = XP_FALSE; } else { @@ -2897,12 +2876,27 @@ board_toggle_arrowDir( BoardCtxt* board ) } } /* board_toggle_cursorDir */ +void +shiftFocusUp( BoardCtxt* board, XP_Key key ) +{ + BoardObjectType next = OBJ_NONE; + util_notifyFocusChange( board->util, board->focussed, key, &next ); + + if ( board->focussed != next ) { + (void)board_focusChanged( board, board->focussed, XP_FALSE ); + + board->focusHasDived = XP_FALSE; + + (void)board_focusChanged( board, next, XP_TRUE ); + } +} + static XP_Bool moveScoreCursor( BoardCtxt* board, XP_Key key ) { XP_Bool result = XP_TRUE; XP_U16 nPlayers = board->gi->nPlayers; - XP_U16 scoreCursorLoc = board->scoreCursorLoc + nPlayers; + XP_S16 scoreCursorLoc = board->scoreCursorLoc; switch ( key ) { case XP_CURSOR_KEY_DOWN: @@ -2916,7 +2910,11 @@ moveScoreCursor( BoardCtxt* board, XP_Key key ) default: result = XP_FALSE; } - board->scoreCursorLoc = scoreCursorLoc % nPlayers; + if ( scoreCursorLoc < 0 || scoreCursorLoc >= nPlayers ) { + shiftFocusUp( board, key ); + } else { + board->scoreCursorLoc = scoreCursorLoc; + } board->scoreBoardInvalid = XP_TRUE; return result; @@ -2931,17 +2929,16 @@ advanceArrow( BoardCtxt* board ) XP_ASSERT( board->trayVisState == TRAY_REVEALED ); - return board_moveArrow( board, key, XP_FALSE ); + return board_moveArrow( board, key ); } /* advanceArrow */ static XP_Bool -figureNextLoc( BoardCtxt* board, XP_Key cursorKey, XP_Bool canCycle, +figureNextLoc( BoardCtxt* board, XP_Key cursorKey, XP_Bool canShiftFocus, XP_Bool avoidOccupied, XP_U16* colP, XP_U16* rowP ) { XP_S16 max; XP_S16* useWhat; XP_S16 end = 0; - XP_U16 counter = 0; XP_S16 incr = 0; XP_U16 numCols, numRows; XP_Bool result = XP_FALSE; @@ -2958,25 +2955,25 @@ figureNextLoc( BoardCtxt* board, XP_Key cursorKey, XP_Bool canCycle, case XP_CURSOR_KEY_DOWN: incr = 1; useWhat = (XP_S16*)rowP; - max = numRows; + max = numRows - 1; end = max; break; case XP_CURSOR_KEY_UP: incr = -1; useWhat = (XP_S16*)rowP; - max = numRows; - end = -1; + max = numRows - 1; + end = 0; break; case XP_CURSOR_KEY_LEFT: incr = -1; useWhat = (XP_S16*)colP; - max = numCols; - end = -1; + max = numCols - 1; + end = 0; break; case XP_CURSOR_KEY_RIGHT: incr = 1; useWhat = (XP_S16*)colP; - max = numCols; + max = numCols - 1; end = max; break; default: @@ -2985,19 +2982,18 @@ figureNextLoc( BoardCtxt* board, XP_Key cursorKey, XP_Bool canCycle, XP_ASSERT( incr != 0 ); - for ( counter = max; ; --counter ) { - - *useWhat += incr; - - if ( (counter == 0) || (!canCycle && (*useWhat == end)) ) { + for ( ; ; ) { + if ( *useWhat == end ) { + if ( canShiftFocus ) { + shiftFocusUp( board, cursorKey ); + result = XP_TRUE; + } break; } - - *useWhat = (*useWhat + max) % max; - + result = XP_TRUE; + *useWhat += incr; if ( !avoidOccupied - || !cellOccupied( board, *colP, *rowP, XP_TRUE ) ) { - result = XP_TRUE; + || !cellOccupied( board, *colP, *rowP, XP_TRUE ) ) { break; } } @@ -3007,14 +3003,14 @@ figureNextLoc( BoardCtxt* board, XP_Key cursorKey, XP_Bool canCycle, } /* figureNextLoc */ static XP_Bool -board_moveArrow( BoardCtxt* board, XP_Key cursorKey, XP_Bool canCycle ) +board_moveArrow( BoardCtxt* board, XP_Key cursorKey ) { XP_U16 col, row; XP_Bool changed; setArrowVisible( board, XP_TRUE ); (void)getArrow( board, &col, &row ); - changed = figureNextLoc( board, cursorKey, canCycle, XP_TRUE, &col, &row ); + changed = figureNextLoc( board, cursorKey, XP_FALSE, XP_TRUE, &col, &row ); if ( changed ) { (void)setArrow( board, col, row ); } diff --git a/common/boardp.h b/common/boardp.h index c0c052ce3..78bee9803 100644 --- a/common/boardp.h +++ b/common/boardp.h @@ -199,6 +199,7 @@ XP_Bool rectsIntersect( XP_Rect* rect1, XP_Rect* rect2 ); XP_Bool tray_moveCursor( BoardCtxt* board, XP_Key cursorKey ); XP_Bool tray_keyAction( BoardCtxt* board ); DrawFocusState dfsFor( BoardCtxt* board, BoardObjectType obj ); +void shiftFocusUp( BoardCtxt* board, XP_Key key ); #else # define dfsFor( board, obj ) DFS_NONE #endif diff --git a/common/tray.c b/common/tray.c index e46d70db0..8478e0e6d 100644 --- a/common/tray.c +++ b/common/tray.c @@ -617,33 +617,26 @@ tray_moveCursor( BoardCtxt* board, XP_Key cursorKey ) { XP_Bool result; XP_U16 selPlayer = board->selPlayer; - XP_U16 numTrayTiles = model_getNumTilesInTray( board->model, - selPlayer ); - XP_U16 pos; - TileBit newSel; - TileBit oldSel = 1 << board->trayCursorLoc[selPlayer]; - - numTrayTiles = MAX_TRAY_TILES; if ( cursorKey == XP_CURSOR_KEY_UP ) { result = board_moveDivider( board, XP_FALSE ); } else if ( cursorKey == XP_CURSOR_KEY_DOWN ) { result = board_moveDivider( board, XP_TRUE ); } else { - pos = indexForBits( oldSel ); + XP_S16 pos; - pos += numTrayTiles; /* add what we'll mod by below: makes circular */ - if ( cursorKey == XP_CURSOR_KEY_LEFT ) { - --pos; - } else if ( cursorKey == XP_CURSOR_KEY_RIGHT ) { - ++pos; + board_invalTrayTiles( board, 1 << board->trayCursorLoc[selPlayer] ); + + pos = board->trayCursorLoc[selPlayer]; + pos += cursorKey == XP_CURSOR_KEY_RIGHT ? 1 : -1; + if ( pos < 0 || pos >= MAX_TRAY_TILES ) { + shiftFocusUp( board, cursorKey ); + } else { + board->trayCursorLoc[selPlayer] = pos; + board_invalTrayTiles( board, 1 << pos ); } - - pos %= numTrayTiles; - board->trayCursorLoc[selPlayer] = pos; - newSel = 1 << pos; - board_invalTrayTiles( board, newSel | oldSel ); + board_invalTrayTiles( board, 1 << board->trayCursorLoc[selPlayer] ); result = XP_TRUE; } diff --git a/common/util.h b/common/util.h index e6181794e..6583df8e0 100644 --- a/common/util.h +++ b/common/util.h @@ -155,6 +155,11 @@ typedef struct UtilVtable { void (*m_util_engineStarting)( XW_UtilCtxt* uc, XP_U16 nBlanks ); void (*m_util_engineStopping)( XW_UtilCtxt* uc ); #endif + +#ifdef KEYBOARD_NAV + void (*m_util_notifyFocusChange)( XW_UtilCtxt* uc, BoardObjectType cur, + XP_Key key, BoardObjectType* next ); +#endif } UtilVtable; @@ -241,4 +246,8 @@ struct XW_UtilCtxt { # define util_engineStopping( uc ) # endif +# ifdef KEYBOARD_NAV +# define util_notifyFocusChange( uc, c, k, n ) \ + (uc)->vtable->m_util_notifyFocusChange((uc),(c),(k),(n)) +# endif #endif diff --git a/linux/cursesmain.c b/linux/cursesmain.c index 2461b1533..aa93b6f76 100644 --- a/linux/cursesmain.c +++ b/linux/cursesmain.c @@ -1,4 +1,4 @@ -/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ /* * Copyright 2000 by Eric House (xwords@eehouse.org). All rights reserved. * @@ -53,13 +53,20 @@ #include "server.h" #include "memstream.h" #include "util.h" +#include "dbgutil.h" #define MENU_WINDOW_HEIGHT 5 /* three lines plus borders */ #define INFINITE_TIMEOUT -1 CursesAppGlobals globals; /* must be global b/c of SIGWINCH_handler */ -static void changeFocus( CursesAppGlobals* globals ); +static void changeMenuForFocus( CursesAppGlobals* globals, + BoardObjectType obj ); +static XP_Bool handleLeft( CursesAppGlobals* globals ); +static XP_Bool handleRight( CursesAppGlobals* globals ); +static XP_Bool handleUp( CursesAppGlobals* globals ); +static XP_Bool handleDown( CursesAppGlobals* globals ); + #ifdef MEM_DEBUG # define MEMPOOL params->util->mpool, @@ -220,6 +227,30 @@ curses_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc) ) return XP_TRUE; } /* curses_util_engineProgressCallback */ +static void +curses_util_notifyFocusChange( XW_UtilCtxt* uc, BoardObjectType cur, + XP_Key key, BoardObjectType* nextP ) +{ + BoardObjectType nxt; + CursesAppGlobals* globals; + + XP_LOGF( "%s(%s)", __FUNCTION__, BoardObjectType_2str(cur) ); + XP_Bool forward = key == XP_CURSOR_KEY_DOWN + || key == XP_CURSOR_KEY_RIGHT; + switch( cur ) { + case OBJ_SCORE: nxt = forward? OBJ_TRAY : OBJ_BOARD; break; + case OBJ_BOARD: nxt = forward? OBJ_SCORE : OBJ_TRAY; break; + case OBJ_TRAY: nxt = forward? OBJ_BOARD : OBJ_SCORE; break; + case OBJ_NONE: nxt = OBJ_BOARD; + } + + globals = (CursesAppGlobals*)uc->closure; + changeMenuForFocus( globals, nxt ); + + *nextP = nxt; + XP_LOGF( "%s()=>%s", __FUNCTION__, BoardObjectType_2str(*nextP) ); +} + #ifdef XWFEATURE_RELAY static void curses_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when, @@ -322,7 +353,6 @@ handleTab( CursesAppGlobals* globals ) { globals->doDraw = board_handleKey( globals->cGlobals.game.board, XP_FOCUSCHANGE_KEY ); - changeFocus( globals ); return XP_TRUE; } /* handleTab */ @@ -416,6 +446,14 @@ MenuList sharedMenuList[] = { { handleTab, "Change focus", "", '\t' }, { handleRet, "Click/tap", "", '\r' }, { handleHint, "Hint", "?", '?' }, + +#ifdef KEYBOARD_NAV + { handleLeft, "Left", "H", 'H' }, + { handleRight, "Right", "L", 'L' }, + { handleUp, "Up", "J", 'J' }, + { handleDown, "Down", "K", 'K' }, +#endif + { handleCommit, "Commit move", "C", 'C' }, { handleFlip, "Flip", "F", 'F' }, { handleToggleValues, "Show values", "V", 'V' }, @@ -436,22 +474,6 @@ handleLeft( CursesAppGlobals* globals ) return XP_TRUE; } /* handleLeft */ -static XP_Bool -handleDivLeft( CursesAppGlobals* globals ) -{ - globals->doDraw = board_moveDivider( globals->cGlobals.game.board, - XP_FALSE ); - return XP_TRUE; -} /* handleDivLeft */ - -static XP_Bool -handleDivRight( CursesAppGlobals* globals ) -{ - globals->doDraw = board_moveDivider( globals->cGlobals.game.board, - XP_TRUE ); - return XP_TRUE; -} /* handleDivRight */ - static XP_Bool handleRight( CursesAppGlobals* globals ) { @@ -478,31 +500,17 @@ handleDown( CursesAppGlobals* globals ) #endif MenuList boardMenuList[] = { -#ifdef KEYBOARD_NAV - { handleLeft, "Left", "H", 'H' }, - { handleRight, "Right", "L", 'L' }, - { handleUp, "Up", "J", 'J' }, - { handleDown, "Down", "K", 'K' }, -#endif { NULL, NULL, NULL, '\0'} }; MenuList scoreMenuList[] = { #ifdef KEYBOARD_NAV - { handleUp, "Up", "J", 'J' }, - { handleDown, "Down", "K", 'K' }, #endif { NULL, NULL, NULL, '\0'} }; MenuList trayMenuList[] = { -#ifdef KEYBOARD_NAV - { handleLeft, "Left", "H", 'H' }, - { handleRight, "Right", "L", 'L' }, - { handleDivLeft, "Div left", "{", '{' }, - { handleDivRight, "Div right", "}", '}' }, -#endif - { handleJuggle, "Juggle", "J", 'J' }, + { handleJuggle, "Juggle", "G", 'G' }, { handleHide, "[un]hIde", "I", 'I' }, { NULL, NULL, NULL, '\0'} @@ -556,6 +564,9 @@ drawMenuFromList( CursesAppGlobals* globals, MenuList* menuList ) if ( !isShared ) { done = XP_TRUE; break; + } else if ( menuList->handler == NULL ) { + done = XP_TRUE; + break; } else { isShared = XP_FALSE; entry = menuList; @@ -563,6 +574,7 @@ drawMenuFromList( CursesAppGlobals* globals, MenuList* menuList ) } } + XP_ASSERT( nLines > 0 ); if ( line % nLines == 0 ) { line = 0; col += maxKey + maxCmd + 2; @@ -802,11 +814,9 @@ blocking_gotEvent( CursesAppGlobals* globals, int* ch ) } /* blocking_gotEvent */ static void -changeFocus( CursesAppGlobals* globals ) +changeMenuForFocus( CursesAppGlobals* globals, BoardObjectType focussed ) { #ifdef KEYBOARD_NAV - BoardObjectType focussed = - board_getFocusOwner( globals->cGlobals.game.board ); if ( focussed == OBJ_TRAY ) { globals->menuList = trayMenuList; drawMenuFromList( globals, trayMenuList ); @@ -820,7 +830,7 @@ changeFocus( CursesAppGlobals* globals ) XP_ASSERT(0); } #endif -} /* changeFocus */ +} /* changeMenuForFocus */ #if 0 static void @@ -916,7 +926,12 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util ) util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver; util->vtable->m_util_hiliteCell = curses_util_hiliteCell; util->vtable->m_util_engineProgressCallback = - curses_util_engineProgressCallback; + curses_util_engineProgressCallback; + +#ifdef KEYBOARD_NAV + util->vtable->m_util_notifyFocusChange = curses_util_notifyFocusChange; +#endif + #ifdef XWFEATURE_RELAY util->vtable->m_util_setTimer = curses_util_setTimer; #endif