mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-30 10:26:58 +01:00
3697 lines
114 KiB
C
Executable file
3697 lines
114 KiB
C
Executable file
/* -*- fill-column: 77; compile-command: "make -j2 TARGET_OS=wince DEBUG=TRUE" -*- */
|
|
/*
|
|
* Copyright 2002-2009 by Eric House (xwords@eehouse.org). All rights
|
|
* reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Derived from code generated by M$'s eVC++.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "xwords4.h"
|
|
#include <commctrl.h>
|
|
#include <commdlg.h>
|
|
#include <stdio.h>
|
|
#include <time.h> /* time() */
|
|
#include <winuser.h>
|
|
#ifdef _WIN32_WCE
|
|
# include <aygshell.h>
|
|
#endif
|
|
/* #include <imm.h> */
|
|
#include "strutils.h"
|
|
|
|
#include "memstream.h"
|
|
|
|
#include "cemain.h"
|
|
#include "cedefines.h"
|
|
|
|
#include "ceginfo.h"
|
|
#include "cestrbx.h"
|
|
#include "cedict.h"
|
|
#include "ceblank.h"
|
|
#include "ceprefs.h"
|
|
#include "ceaskpwd.h"
|
|
#include "ceutil.h"
|
|
#include "ceir.h"
|
|
#include "ceclrsel.h"
|
|
#include "cehntlim.h"
|
|
#include "cedebug.h"
|
|
#include "LocalizedStrIncludes.h"
|
|
#include "debhacks.h"
|
|
#include "cesvdgms.h"
|
|
#include "cedraw.h"
|
|
#include "cesms.h"
|
|
#include "cesockwr.h"
|
|
#include "ceresstr.h"
|
|
|
|
#include "dbgutil.h"
|
|
|
|
#define MAX_LOADSTRING 100
|
|
|
|
#define MAX_SCROLLBAR_WIDTH 12
|
|
#define MIN_SCROLLBAR_WIDTH 6
|
|
#define SCROLLBARID 0x4321 /* needs to be unique! */
|
|
|
|
#ifdef MEM_DEBUG
|
|
# define MEMPOOL globals->mpool,
|
|
#else
|
|
# define MEMPOOL
|
|
#endif
|
|
|
|
typedef struct FileWriteState {
|
|
CEAppGlobals* globals;
|
|
XP_UCHAR* path;
|
|
} FileWriteState;
|
|
|
|
/* forward util function decls */
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static XP_S16 ce_send_proc( const XP_U8* buf, XP_U16 len,
|
|
const CommsAddrRec* addr,
|
|
void* closure );
|
|
static void ce_relay_status( void* closure,
|
|
CommsRelayState newState );
|
|
|
|
# ifdef COMMS_HEARTBEAT
|
|
static void ce_reset_proc( void* closure );
|
|
# endif
|
|
#endif
|
|
|
|
static VTableMgr* ce_util_getVTManager( XW_UtilCtxt* uc );
|
|
static void ce_util_userError( XW_UtilCtxt* uc, UtilErrID id );
|
|
static XP_Bool ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id,
|
|
XWStreamCtxt* stream );
|
|
static XWBonusType ce_util_getSquareBonus( XW_UtilCtxt* uc,
|
|
const ModelCtxt* model,
|
|
XP_U16 col, XP_U16 row );
|
|
static XP_S16 ce_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
|
|
XP_U16 playerNum,
|
|
const XP_UCHAR** texts, XP_U16 nTiles );
|
|
static XP_Bool ce_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name,
|
|
XP_UCHAR* buf, XP_U16* len );
|
|
static void ce_util_trayHiddenChange( XW_UtilCtxt* uc,
|
|
XW_TrayVisState newState,
|
|
XP_U16 nVisibleRows );
|
|
static void ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 oldOffset,
|
|
XP_U16 newOffset );
|
|
static void ce_util_turnChanged( XW_UtilCtxt* uc );
|
|
static void ce_util_notifyGameOver( XW_UtilCtxt* uc );
|
|
static XP_Bool ce_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col,
|
|
XP_U16 row );
|
|
static XP_Bool ce_util_engineProgressCallback( XW_UtilCtxt* uc );
|
|
static void ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
|
|
XWTimerProc proc, void* closure);
|
|
static XP_Bool ce_util_altKeyDown( XW_UtilCtxt* uc );
|
|
static void ce_util_requestTime( XW_UtilCtxt* uc );
|
|
static XP_U32 ce_util_getCurSeconds( XW_UtilCtxt* uc );
|
|
static DictionaryCtxt* ce_util_makeEmptyDict( XW_UtilCtxt* uc );
|
|
#ifdef XWFEATURE_RELAY
|
|
static XWStreamCtxt* ce_util_makeStreamFromAddr( XW_UtilCtxt* uc,
|
|
XP_PlayerAddr channelNo );
|
|
#endif
|
|
static const XP_UCHAR* ce_util_getUserString( XW_UtilCtxt* uc,
|
|
XP_U16 stringCode );
|
|
static XP_Bool ce_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
|
XP_U16 turn, XP_Bool turnLost );
|
|
static void ce_util_remSelected( XW_UtilCtxt* uc );
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static void ce_util_addrChange( XW_UtilCtxt* uc, const CommsAddrRec* oldAddr,
|
|
const CommsAddrRec* newAddr );
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
static XP_Bool ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min,
|
|
XP_U16* max );
|
|
#endif
|
|
#ifdef SHOW_PROGRESS
|
|
static void ce_util_engineStarting( XW_UtilCtxt* uc );
|
|
static void ce_util_engineStopping( XW_UtilCtxt* uc );
|
|
#endif
|
|
|
|
static XP_Bool ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons,
|
|
XP_Bool destroy );
|
|
static void RECTtoXPR( XP_Rect* dest, const RECT* src );
|
|
static XP_Bool ceDoNewGame( CEAppGlobals* globals );
|
|
static XP_Bool ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave );
|
|
static void closeGame( CEAppGlobals* globals );
|
|
static void ceInitPrefs( CEAppGlobals* globals, CEAppPrefs* prefs );
|
|
static void updateForColors( CEAppGlobals* globals );
|
|
static XWStreamCtxt* make_generic_stream( const CEAppGlobals* globals );
|
|
#ifdef XWFEATURE_RELAY
|
|
static void ce_send_on_close( XWStreamCtxt* stream, void* closure );
|
|
#endif
|
|
static XP_Bool ceSetDictName( const wchar_t* wPath, XP_U16 index, void* ctxt );
|
|
static int messageBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons );
|
|
static XP_Bool ceQueryFromStream( CEAppGlobals* globals, XWStreamCtxt* stream);
|
|
static int ceOopsId( CEAppGlobals* globals, XP_U16 strId );
|
|
static int ceOops( CEAppGlobals* globals, const XP_UCHAR* str );
|
|
static XP_Bool isDefaultName( CEAppGlobals* globals, const XP_UCHAR* name );
|
|
static void ceSetTitleFromName( CEAppGlobals* globals );
|
|
static void removeScrollbar( CEAppGlobals* globals );
|
|
|
|
|
|
#ifndef _WIN32_WCE
|
|
/* Very basic cmdline args meant at first to let me vary the size of the
|
|
* screen. Form is of arg=digits, with no spaces and digits having to be an
|
|
* integer. Right now only width and height work: e.g.
|
|
* "wine obj_win32_dbg/xwords4.exe height=240 width=320"
|
|
*/
|
|
static XP_Bool
|
|
tryIntParam( const char* buf, const char* param, XP_U16* value )
|
|
{
|
|
XP_U16 len = strlen( param );
|
|
XP_Bool found = 0 == strncmp( buf, param, len );
|
|
if ( found ) {
|
|
*value = atoi( &buf[len] );
|
|
}
|
|
return found;
|
|
}
|
|
|
|
static XP_Bool
|
|
tryCharParam( const char* buf, const char* param, char* out )
|
|
{
|
|
XP_U16 len = strlen( param );
|
|
XP_Bool found = 0 == strncmp( buf, param, len );
|
|
if ( found ) {
|
|
int ii;
|
|
for ( ii = len; buf[ii] != ' ' && buf[ii] != 0 ; ++ii ) {
|
|
}
|
|
memcpy( out, &buf[len], ii - len );
|
|
out[ii-len] = 0;
|
|
XP_LOGF( "%s: \"%s\"", __func__, out );
|
|
}
|
|
return found;
|
|
}
|
|
|
|
static void
|
|
parseCmdLine( const char* cmdline, XP_U16* width, XP_U16* height,
|
|
char* dll )
|
|
{
|
|
XP_U16 ii;
|
|
for ( ii = 0; ; ++ii ) {
|
|
const char* cmd;
|
|
char ch;
|
|
char buf[64];
|
|
int len;
|
|
for ( cmd = cmdline ; ; ++cmd ) {
|
|
ch = *cmd;
|
|
if ( ch == '\0' || ch == ' ' ) {
|
|
break;
|
|
}
|
|
}
|
|
len = cmd - cmdline;
|
|
if ( len < sizeof(buf) ) {
|
|
memcpy( buf, cmdline, cmd - cmdline );
|
|
buf[len] = '\0';
|
|
if ( ii > 0 ) { /* skip argv[0] */
|
|
if ( tryIntParam( buf, "width=", width ) ) {
|
|
} else if ( tryIntParam( buf, "height=", height ) ) {
|
|
} else if ( tryCharParam( buf, "dll=", dll ) ) {
|
|
} else {
|
|
XP_LOGF( "failed to match cmdline arg \"%s\"", buf );
|
|
}
|
|
}
|
|
}
|
|
if ( ch == '\0' ) {
|
|
break;
|
|
}
|
|
cmdline = ++cmd;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Forward declarations of functions included in this code module:
|
|
ATOM MyRegisterClass (HINSTANCE, LPTSTR);
|
|
BOOL InitInstance (HINSTANCE, int
|
|
#ifndef _WIN32_WCE
|
|
, XP_U16, XP_U16, const char*
|
|
#endif
|
|
);
|
|
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
|
|
LRESULT CALLBACK ceAbout (HWND, UINT, WPARAM, LPARAM);
|
|
|
|
int WINAPI
|
|
WinMain( HINSTANCE hInstance,
|
|
HINSTANCE XP_UNUSED(hPrevInstance),
|
|
#ifdef _WIN32_WCE
|
|
LPWSTR XP_UNUSED_CE(lpCmdLine),
|
|
#else
|
|
LPSTR lpCmdLine,
|
|
#endif
|
|
int nCmdShow)
|
|
{
|
|
MSG msg;
|
|
HACCEL hAccelTable;
|
|
|
|
#ifndef _WIN32_WCE
|
|
XP_U16 width = 320, height = 320;
|
|
char dll[MAX_PATH] = {0};
|
|
parseCmdLine( lpCmdLine, &width, &height, dll );
|
|
#endif
|
|
|
|
// Perform application initialization:
|
|
if (!InitInstance (hInstance, nCmdShow
|
|
#ifndef _WIN32_WCE
|
|
, width, height, dll
|
|
#endif
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
|
|
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_XWORDS4);
|
|
|
|
// Main message loop. Return of 0 indicates quit message. Return of -1
|
|
// indicates major error (so we just bail.)
|
|
while ( 0 < GetMessage(&msg, NULL, 0, 0) ) {
|
|
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
}
|
|
|
|
/* This would be a good place to free up memory, close sockets, etc. */
|
|
|
|
LOG_RETURNF( "%d", msg.wParam );
|
|
return msg.wParam;
|
|
}
|
|
|
|
#ifdef __GNUC__
|
|
int
|
|
main()
|
|
{
|
|
LOG_FUNC();
|
|
|
|
return WinMain( GetModuleHandle(NULL), 0,
|
|
#ifdef _WIN32_WCE
|
|
GetCommandLineW(),
|
|
#else
|
|
GetCommandLineA(),
|
|
#endif
|
|
SW_SHOWDEFAULT );
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// FUNCTION: MyRegisterClass()
|
|
//
|
|
// PURPOSE: Registers the window class.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// It is important to call this function so that the application
|
|
// will get 'well formed' small icons associated with it.
|
|
//
|
|
ATOM
|
|
MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
XP_MEMSET( &wc, 0, sizeof(wc) );
|
|
|
|
wc.style = CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = (WNDPROC) WndProc;
|
|
wc.cbWndExtra = sizeof(CEAppGlobals*);
|
|
wc.hInstance = hInstance;
|
|
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_XWORDS4));
|
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|
#ifndef _WIN32_WCE
|
|
wc.lpszMenuName = (LPCTSTR)IDM_MENU;
|
|
#endif
|
|
wc.lpszClassName = szWindowClass;
|
|
|
|
return RegisterClass(&wc);
|
|
}
|
|
|
|
static void
|
|
ceInitUtilFuncs( CEAppGlobals* globals )
|
|
{
|
|
UtilVtable* vtable = globals->util.vtable =
|
|
XP_MALLOC( globals->mpool, sizeof( UtilVtable ) );
|
|
globals->util.closure = (void*)globals;
|
|
globals->util.gameInfo = &globals->gameInfo;
|
|
|
|
MPASSIGN( globals->util.mpool, globals->mpool );
|
|
|
|
vtable->m_util_getVTManager = ce_util_getVTManager;
|
|
vtable->m_util_userError = ce_util_userError;
|
|
vtable->m_util_getSquareBonus = ce_util_getSquareBonus;
|
|
vtable->m_util_userQuery = ce_util_userQuery;
|
|
vtable->m_util_userPickTile = ce_util_userPickTile;
|
|
vtable->m_util_askPassword = ce_util_askPassword;
|
|
vtable->m_util_trayHiddenChange = ce_util_trayHiddenChange;
|
|
vtable->m_util_yOffsetChange = ce_util_yOffsetChange;
|
|
vtable->m_util_turnChanged = ce_util_turnChanged;
|
|
vtable->m_util_notifyGameOver = ce_util_notifyGameOver;
|
|
vtable->m_util_hiliteCell = ce_util_hiliteCell;
|
|
vtable->m_util_engineProgressCallback = ce_util_engineProgressCallback;
|
|
vtable->m_util_setTimer = ce_util_setTimer;
|
|
vtable->m_util_altKeyDown = ce_util_altKeyDown;
|
|
vtable->m_util_requestTime = ce_util_requestTime;
|
|
vtable->m_util_getCurSeconds = ce_util_getCurSeconds;
|
|
vtable->m_util_makeEmptyDict = ce_util_makeEmptyDict;
|
|
vtable->m_util_getUserString = ce_util_getUserString;
|
|
vtable->m_util_warnIllegalWord = ce_util_warnIllegalWord;
|
|
vtable->m_util_remSelected = ce_util_remSelected;
|
|
#ifdef XWFEATURE_RELAY
|
|
vtable->m_util_addrChange = ce_util_addrChange;
|
|
vtable->m_util_makeStreamFromAddr = ce_util_makeStreamFromAddr;
|
|
#endif
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
vtable->m_util_getTraySearchLimits = ce_util_getTraySearchLimits;
|
|
#endif
|
|
#ifdef SHOW_PROGRESS
|
|
vtable->m_util_engineStarting = ce_util_engineStarting;
|
|
vtable->m_util_engineStopping = ce_util_engineStopping;
|
|
#endif
|
|
|
|
} /* ceInitUtilFuncs */
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
# define SCROLL_SHRINK 1
|
|
|
|
static void
|
|
updateScrollInfo( CEAppGlobals* globals, XP_U16 nHidden )
|
|
{
|
|
SCROLLINFO sinfo;
|
|
|
|
XP_MEMSET( &sinfo, 0, sizeof(sinfo) );
|
|
sinfo.cbSize = sizeof(sinfo);
|
|
sinfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
|
|
sinfo.nMax = model_numRows( globals->game.model );
|
|
sinfo.nPage = sinfo.nMax - nHidden + 1;
|
|
|
|
(void)SetScrollInfo( globals->scrollHandle, SB_CTL, &sinfo, TRUE );
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
scrollWindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)GetWindowLongPtr( hWnd, GWL_USERDATA );
|
|
LRESULT result = 1;
|
|
|
|
/* XP_LOGF( "%s: event=%s (%d)", __func__, messageToStr(message), message ); */
|
|
|
|
/* Trap key events. Left and right always shift the focus off. Up and
|
|
down shift focus off IFF they're going to be no-ops on the theory that
|
|
the user needs to get some visual feedback and on some devices the
|
|
scrollbar isn't even drawn differently when focussed. */
|
|
if ( WM_KEYDOWN == message ) {
|
|
XP_Bool setFocus = XP_FALSE;
|
|
|
|
if ( (VK_RIGHT == wParam) ||
|
|
(VK_LEFT == wParam) ||
|
|
(VK_TAB == wParam) ) {
|
|
setFocus = XP_TRUE;
|
|
} else if ( (VK_UP == wParam) || (VK_DOWN == wParam) ) {
|
|
SCROLLINFO sinfo;
|
|
sinfo.cbSize = sizeof(sinfo);
|
|
sinfo.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
|
|
GetScrollInfo( globals->scrollHandle, SB_CTL, &sinfo );
|
|
|
|
if ( (VK_UP == wParam) && (sinfo.nPos <= sinfo.nMin) ) {
|
|
setFocus = XP_TRUE;
|
|
} else if ( (VK_DOWN == wParam)
|
|
&& (sinfo.nPos > (sinfo.nMax - sinfo.nPage)) ) {
|
|
setFocus = XP_TRUE;
|
|
}
|
|
}
|
|
|
|
if ( setFocus ) {
|
|
SetFocus( globals->hWnd );
|
|
result = 0;
|
|
globals->keyDown = XP_TRUE;
|
|
}
|
|
}
|
|
if ( 0 != result ) {
|
|
result = CallWindowProc( globals->oldScrollProc, hWnd, message,
|
|
wParam, lParam );
|
|
}
|
|
return result;
|
|
} /* scrollWindowProc */
|
|
|
|
static void
|
|
makeScrollbar( CEAppGlobals* globals, XP_U16 nHidden, XP_U16 xx, XP_U16 yy,
|
|
XP_U16 width, XP_U16 height )
|
|
{
|
|
HWND hwndSB;
|
|
#ifdef _WIN32_WCE
|
|
RECT tmp;
|
|
XP_U16 rectHt;
|
|
#endif
|
|
|
|
++xx;
|
|
width -= 2; /* make narrower: on CE will erase cell border */
|
|
|
|
#ifdef _WIN32_WCE
|
|
tmp.left = xx;
|
|
tmp.right = xx + width;
|
|
rectHt = height / 10; /* each focus rect to be 1/10th height */
|
|
|
|
tmp.top = yy;
|
|
tmp.bottom = yy + rectHt;
|
|
XP_MEMCPY( &globals->scrollRects[0], &tmp, sizeof(globals->scrollRects[0]) );
|
|
|
|
tmp.bottom = yy + height;
|
|
tmp.top = tmp.bottom - rectHt;
|
|
XP_MEMCPY( &globals->scrollRects[1], &tmp, sizeof(globals->scrollRects[1]) );
|
|
|
|
yy += rectHt;
|
|
height -= rectHt * 2; /* above and below */
|
|
#endif
|
|
|
|
/* Need to destroy it, or resize it, because board size may be changing
|
|
in case where portrait display's been flipped. */
|
|
removeScrollbar( globals );
|
|
|
|
hwndSB = CreateWindow( TEXT("SCROLLBAR"), // Class name
|
|
NULL, // Window text
|
|
// Window style
|
|
SBS_VERT|WS_VISIBLE|WS_CHILD,
|
|
xx, yy, width, height, globals->hWnd,
|
|
(HMENU)SCROLLBARID, // The control identifier
|
|
globals->hInst, // The instance handle
|
|
NULL ); // s'pposed to be NULL
|
|
|
|
globals->scrollHandle = hwndSB;
|
|
|
|
globals->oldScrollProc = (WNDPROC) GetWindowLongPtr( hwndSB,
|
|
GWL_WNDPROC );
|
|
XP_ASSERT( 0 == GetWindowLongPtr( hwndSB, GWL_USERDATA ) );
|
|
SetWindowLongPtr( hwndSB, GWL_WNDPROC, (LPARAM)scrollWindowProc );
|
|
SetWindowLongPtr( hwndSB, GWL_USERDATA, (LPARAM)globals );
|
|
|
|
updateScrollInfo( globals, nHidden );
|
|
EnableWindow( hwndSB, nHidden > 0 );
|
|
|
|
ShowWindow( globals->scrollHandle, SW_SHOW );
|
|
} /* makeScrollbar */
|
|
|
|
static void
|
|
removeScrollbar( CEAppGlobals* globals )
|
|
{
|
|
if ( !!globals->scrollHandle ) {
|
|
DestroyWindow( globals->scrollHandle );
|
|
globals->scrollHandle = NULL;
|
|
globals->scrollerHasFocus = XP_FALSE;
|
|
}
|
|
} /* removeScrollbar */
|
|
#endif
|
|
|
|
typedef struct CEBoardParms {
|
|
XP_U16 scrnWidth;
|
|
XP_U16 scrnHeight;
|
|
XP_U16 adjLeft;
|
|
XP_U16 adjTop;
|
|
|
|
XP_U16 boardHScale;
|
|
XP_U16 boardVScale;
|
|
XP_U16 boardTop;
|
|
XP_U16 trayTop;
|
|
|
|
XP_U16 trayHeight;
|
|
XP_U16 trayWidth;
|
|
|
|
XP_U16 timerLeft, timerTop, timerWidth, timerHeight;
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
XP_U16 netstatLeft, netstatTop, netstatWidth, netstatHeight;
|
|
#endif
|
|
|
|
XP_U16 boardLeft, trayLeft;
|
|
XP_U16 scoreWidth;
|
|
XP_U16 scoreHeight;
|
|
XP_U16 scrollWidth;
|
|
XP_Bool horiz;
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
XP_Bool needsScroller;
|
|
#endif
|
|
} CEBoardParms;
|
|
|
|
static void
|
|
figureBoardParms( CEAppGlobals* globals, const XP_U16 nRows,
|
|
CEBoardParms* bparms )
|
|
{
|
|
RECT rc;
|
|
XP_U16 scrnWidth, scrnHeight;
|
|
XP_U16 trayHeight, scoreWidth, scoreHeight;
|
|
XP_U16 hScale, vScale, nVisibleRows;
|
|
XP_U16 tmp;
|
|
XP_Bool horiz;
|
|
XP_U16 scrollWidth = 0;
|
|
XP_U16 adjLeft, adjTop;
|
|
|
|
XP_MEMSET( bparms, 0, sizeof(*bparms) );
|
|
|
|
GetClientRect( globals->hWnd, &rc );
|
|
#ifndef _WIN32_WCE
|
|
# if defined FORCE_HEIGHT && defined FORCE_WIDTH
|
|
rc.right = rc.left + FORCE_WIDTH;
|
|
rc.bottom = rc.top + FORCE_HEIGHT;
|
|
# else
|
|
if ( !globals->appPrefs.fullScreen ) {
|
|
if ( globals->dbWidth != 0 ) {
|
|
rc.right = rc.left + globals->dbWidth;
|
|
}
|
|
if ( globals->dbHeight != 0 ) {
|
|
rc.bottom = rc.top + globals->dbHeight;
|
|
}
|
|
}
|
|
# endif
|
|
#endif /* #ifndef _WIN32_WCE */
|
|
|
|
scrnWidth = (XP_U16)(rc.right - rc.left);
|
|
scrnHeight = (XP_U16)(rc.bottom - rc.top);
|
|
|
|
XP_LOGF( "%s: scrnWidth: %d, scrnHeight: %d", __func__,
|
|
scrnWidth, scrnHeight );
|
|
|
|
/* Figure layout style based on proportion UNLESS there's just no room
|
|
for anything but 15 columns on the board -- because vertically is the
|
|
only dimension in which we can scroll. */
|
|
if ( scrnWidth <= (MIN_CELL_WIDTH * (nRows+2)) ) {
|
|
horiz = XP_TRUE;
|
|
} else {
|
|
horiz = scrnHeight >= scrnWidth;
|
|
}
|
|
|
|
/* vScale. Tray must be a few pixels taller than cells to use same
|
|
sized, minimum-sized font. Subtract out that difference here and it's
|
|
guaranteed that tray will wind up at least that much taller later
|
|
though for the rest of the calculation we reserve only a cell's height
|
|
for it. */
|
|
vScale = (scrnHeight - (MIN_TRAY_HEIGHT-MIN_CELL_HEIGHT))
|
|
/ (nRows + (horiz?2:1)); /* horiz means scoreboard *and* tray */
|
|
if ( vScale >= MIN_CELL_HEIGHT ) {
|
|
nVisibleRows = nRows; /* no scrolling needed */
|
|
} else {
|
|
XP_U16 nRowsPossible = (scrnHeight-MIN_TRAY_HEIGHT) / MIN_CELL_HEIGHT;
|
|
XP_S16 num2Scroll;
|
|
num2Scroll = (nRows + (horiz?1:0)) - nRowsPossible; /* 1: scoreboard */
|
|
XP_ASSERT( num2Scroll > 0 );
|
|
if ( num2Scroll < 0 ) { /* no-scroll case should be caught above */
|
|
num2Scroll = 0;
|
|
}
|
|
nVisibleRows = nRows - num2Scroll;
|
|
vScale = (scrnHeight-MIN_TRAY_HEIGHT) / (nVisibleRows + (horiz? 1:0));
|
|
}
|
|
|
|
tmp = nRows + (horiz ? 0 : 2);
|
|
hScale = scrnWidth / tmp;
|
|
|
|
if ( vScale > hScale ) {
|
|
vScale = XP_MAX( MIN_CELL_HEIGHT, hScale );
|
|
} else if ( hScale > vScale ) {
|
|
hScale = XP_MAX( MIN_CELL_WIDTH, vScale );
|
|
}
|
|
|
|
/* Figure out tray size */
|
|
tmp = vScale * (nVisibleRows + (horiz? 1:0));
|
|
trayHeight = XP_MIN( vScale * 2, scrnHeight - tmp );
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
/* Does this need to be in a loop? */
|
|
while ( nVisibleRows < nRows && hScale > MIN_CELL_WIDTH ) { /* need scroller? (while allows break) */
|
|
scrollWidth = scrnWidth - (tmp * hScale);
|
|
if ( scrollWidth >= MIN_SCROLLBAR_WIDTH ) {
|
|
break;
|
|
}
|
|
--hScale;
|
|
}
|
|
if ( scrollWidth > MAX_SCROLLBAR_WIDTH ) {
|
|
scrollWidth = MAX_SCROLLBAR_WIDTH;
|
|
}
|
|
#endif
|
|
|
|
if ( horiz ) {
|
|
scoreWidth = scrollWidth + (hScale * nRows);
|
|
scoreHeight = vScale - SCORE_TWEAK;
|
|
trayHeight += SCORE_TWEAK;
|
|
} else {
|
|
scoreWidth = XP_MIN( 2*hScale, scrnWidth - (hScale * nRows) );
|
|
scoreHeight = (nVisibleRows * vScale) + trayHeight;
|
|
}
|
|
/* XP_LOGF( "hScale=%d; vScale=%d; trayHeight=%d", hScale, vScale, trayHeight ); */
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->game.comms ) {
|
|
/* If we're horizontal, steal from the right of the scoreboard. If
|
|
vertical, from the bottom. */
|
|
if ( horiz ) {
|
|
bparms->netstatWidth = hScale;
|
|
scoreWidth -= bparms->netstatWidth;
|
|
bparms->netstatLeft = scoreWidth;
|
|
bparms->netstatTop = 0;
|
|
bparms->netstatHeight = scoreHeight;
|
|
} else {
|
|
bparms->netstatLeft = 0;
|
|
bparms->netstatHeight = vScale;
|
|
scoreHeight -= bparms->netstatHeight;
|
|
bparms->netstatTop = scoreHeight;
|
|
bparms->netstatWidth = scoreWidth;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( globals->gameInfo.timerEnabled ) {
|
|
if ( horiz ) {
|
|
bparms->timerWidth = scoreWidth / 6; /* arbitrarily, one sixth */
|
|
scoreWidth -= bparms->timerWidth;
|
|
bparms->timerLeft = scoreWidth;
|
|
bparms->timerTop = 0;
|
|
bparms->timerHeight = scoreHeight;
|
|
} else {
|
|
bparms->timerLeft = 0;
|
|
bparms->timerHeight = vScale * 2;
|
|
bparms->timerTop = scoreHeight - bparms->timerHeight;
|
|
bparms->timerWidth = scoreWidth;
|
|
|
|
scoreHeight -= bparms->timerHeight;
|
|
}
|
|
}
|
|
|
|
globals->cellHt = vScale;
|
|
|
|
/* figure actual width and height */
|
|
tmp = scrollWidth + (hScale * nRows) + (horiz ? 0 : scoreWidth);
|
|
adjLeft = (scrnWidth - tmp)/2;
|
|
tmp = trayHeight + (vScale * nVisibleRows) + (horiz?scoreHeight:0);
|
|
adjTop = (scrnHeight - tmp)/2;
|
|
|
|
bparms->scrnWidth = scrnWidth;
|
|
bparms->scrnHeight = scrnHeight;
|
|
bparms->adjLeft = adjLeft;
|
|
bparms->adjTop = adjTop;
|
|
bparms->boardHScale = hScale;
|
|
bparms->boardVScale = vScale;
|
|
bparms->boardTop = adjTop + (horiz? scoreHeight : 0);
|
|
bparms->trayTop = bparms->boardTop + (nVisibleRows * vScale) + 1;
|
|
bparms->trayHeight = trayHeight - 1;
|
|
bparms->trayWidth = (hScale * nRows) + scrollWidth;
|
|
bparms->boardLeft = adjLeft + (horiz ? 0 : scoreWidth);
|
|
bparms->trayLeft = bparms->boardLeft;//horiz? 0 : scoreWidth;
|
|
bparms->scoreWidth = scoreWidth;
|
|
bparms->scoreHeight = scoreHeight;
|
|
bparms->scrollWidth = scrollWidth;
|
|
bparms->horiz = horiz;
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
bparms->needsScroller = scrollWidth > 0;
|
|
if ( bparms->needsScroller ) {
|
|
XP_U16 boardRight;
|
|
boardRight = bparms->boardLeft + (nRows * hScale);
|
|
makeScrollbar( globals, nRows - nVisibleRows,
|
|
boardRight, bparms->boardTop,
|
|
scrollWidth,
|
|
vScale * nVisibleRows );
|
|
} else {
|
|
removeScrollbar( globals );
|
|
}
|
|
#endif
|
|
} /* figureBoardParms */
|
|
|
|
static void
|
|
setOwnedRects( CEAppGlobals* globals, XP_U16 nRows,
|
|
const CEBoardParms* bparms )
|
|
{
|
|
RECT tmp;
|
|
XP_U16 scrollWidth = bparms->scrollWidth;
|
|
|
|
XP_MEMSET( &globals->ownedRects, 0, sizeof(globals->ownedRects) );
|
|
|
|
tmp.top = bparms->adjTop; /* Same for both */
|
|
tmp.bottom = bparms->trayTop + bparms->trayHeight; /* Same for both */
|
|
|
|
tmp.left = 0;
|
|
tmp.right = bparms->adjLeft;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_LEFT], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_LEFT]) );
|
|
|
|
tmp.left = tmp.right + (bparms->boardHScale * nRows) + scrollWidth;
|
|
tmp.right = bparms->scrnWidth;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_RIGHT], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_RIGHT]) );
|
|
|
|
tmp.left = 0;
|
|
tmp.top = 0;
|
|
tmp.right = bparms->scrnWidth;
|
|
tmp.bottom = bparms->adjTop;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_TOP], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_TOP]) );
|
|
|
|
tmp.top = bparms->trayTop + bparms->trayHeight;
|
|
tmp.bottom = bparms->scrnHeight;
|
|
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_BOTTOM], &tmp,
|
|
sizeof(globals->ownedRects[OWNED_RECT_BOTTOM]) );
|
|
} /* setOwnedRects */
|
|
|
|
|
|
/* PENDING cePositionBoard gets called a lot when the screen size hasn't
|
|
changed. It'd be better to cache the size used to do layout and not
|
|
repeat those steps (including possibly nuking and rebuilding a
|
|
scrollbar). */
|
|
static XP_Bool
|
|
cePositionBoard( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool erase = XP_FALSE;
|
|
XP_U16 nCols;
|
|
CEBoardParms bparms;
|
|
|
|
XP_ASSERT( !!globals->game.model );
|
|
nCols = model_numCols( globals->game.model );
|
|
XP_ASSERT( nCols <= CE_MAX_ROWS );
|
|
|
|
figureBoardParms( globals, nCols, &bparms );
|
|
setOwnedRects( globals, nCols, &bparms );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->game.comms ) {
|
|
globals->relayStatusR.left = bparms.adjLeft + bparms.netstatLeft;
|
|
globals->relayStatusR.top = bparms.adjTop + bparms.netstatTop;
|
|
globals->relayStatusR.right = globals->relayStatusR.left
|
|
+ bparms.netstatWidth;
|
|
globals->relayStatusR.bottom = globals->relayStatusR.top
|
|
+ bparms.netstatHeight;
|
|
}
|
|
#endif
|
|
|
|
if ( globals->gameInfo.timerEnabled ) {
|
|
board_setTimerLoc( globals->game.board,
|
|
bparms.adjLeft + bparms.timerLeft,
|
|
bparms.adjTop + bparms.timerTop, bparms.timerWidth,
|
|
bparms.timerHeight );
|
|
}
|
|
|
|
board_setPos( globals->game.board, bparms.boardLeft,
|
|
bparms.boardTop, XP_FALSE );
|
|
board_setScale( globals->game.board, bparms.boardHScale,
|
|
bparms.boardVScale );
|
|
|
|
board_setScoreboardLoc( globals->game.board, bparms.adjLeft, bparms.adjTop,
|
|
bparms.scoreWidth,
|
|
bparms.scoreHeight, bparms.horiz );
|
|
board_setShowColors( globals->game.board, globals->appPrefs.showColors );
|
|
board_setYOffset( globals->game.board, 0 );
|
|
|
|
board_prefsChanged( globals->game.board, &globals->appPrefs.cp );
|
|
|
|
board_setTrayLoc( globals->game.board, bparms.trayLeft, bparms.trayTop,
|
|
bparms.trayWidth, bparms.trayHeight,
|
|
bparms.trayWidth/40 ); /* 1/8 of a tile width, roughly */
|
|
|
|
server_prefsChanged( globals->game.server, &globals->appPrefs.cp );
|
|
|
|
#if ! defined _WIN32_WCE && defined DEBUG
|
|
ceSetTitleFromName( globals );
|
|
#endif
|
|
ceCheckMenus( globals );
|
|
return erase;
|
|
} /* cePositionBoard */
|
|
|
|
/* Set the title. If there's a game name, replace the window title with that
|
|
* in case both won't fit. If there's no name yet, install the app name as
|
|
* title.
|
|
*/
|
|
static void
|
|
ceSetTitleFromName( CEAppGlobals* globals )
|
|
{
|
|
wchar_t widebuf[64];
|
|
const XP_UCHAR* gameName = globals->curGameName;
|
|
|
|
/* if default name, remove any current name */
|
|
if ( !gameName || isDefaultName( globals, gameName ) ) {
|
|
LoadString( globals->locInst, IDS_APP_TITLE, widebuf, VSIZE(widebuf) );
|
|
} else {
|
|
wchar_t* dotPos;
|
|
XP_UCHAR* baseStart = 1 + strrchr( gameName, '\\' );
|
|
XP_U16 len = (XP_U16)XP_STRLEN( baseStart );
|
|
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, baseStart, len + 1,
|
|
widebuf, len + 1 );
|
|
|
|
/* now get rid of the ".xwg" */
|
|
dotPos = wcsrchr( widebuf, '.' );
|
|
if ( dotPos != NULL ) {
|
|
*dotPos = 0;
|
|
}
|
|
}
|
|
|
|
#if ! defined _WIN32_WCE && defined DEBUG
|
|
swprintf( &widebuf[wcslen(widebuf)], L":%dx%d",
|
|
globals->dbWidth, globals->dbHeight );
|
|
#endif
|
|
SendMessage( globals->hWnd, WM_SETTEXT, 0, (long)widebuf );
|
|
} /* ceSetTitleFromName */
|
|
|
|
static void
|
|
ceInitTProcs( CEAppGlobals* globals, TransportProcs* procs )
|
|
{
|
|
XP_MEMSET( procs, 0, sizeof(*procs) );
|
|
procs->send = ce_send_proc;
|
|
#ifdef COMMS_HEARTBEAT
|
|
procs->reset = ce_reset_proc;
|
|
#endif
|
|
procs->rstatus = ce_relay_status;
|
|
procs->closure = globals;
|
|
}
|
|
|
|
static void
|
|
ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame,
|
|
const CommsAddrRec* XP_UNUSED_STANDALONE(addr) )
|
|
{
|
|
DictionaryCtxt* dict;
|
|
DictionaryCtxt* toBeDestroyed = NULL;
|
|
XP_UCHAR* newDictName = globals->gameInfo.dictName;
|
|
|
|
/* This needs to happen even when !newGame because it's how the dict
|
|
slots in PlayerInfo get loaded. That really ought to happen earlier,
|
|
though. */
|
|
XP_ASSERT( !!globals->game.model );
|
|
dict = model_getDictionary( globals->game.model );
|
|
|
|
if ( !!dict ) {
|
|
const XP_UCHAR* curDictName = dict_getName( dict );
|
|
if ( !!newDictName &&
|
|
( !curDictName || 0 != strcmp( curDictName, newDictName ) ) ) {
|
|
toBeDestroyed = dict;
|
|
dict = NULL;
|
|
} else {
|
|
replaceStringIfDifferent( globals->mpool,
|
|
&globals->gameInfo.dictName,
|
|
curDictName );
|
|
}
|
|
}
|
|
|
|
if ( !dict ) {
|
|
#ifdef STUBBED_DICT
|
|
dict = make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) );
|
|
#else
|
|
XP_ASSERT( !!newDictName );
|
|
XP_LOGF( "calling ce_dictionary_make" );
|
|
dict = ce_dictionary_make( globals, newDictName);
|
|
#endif
|
|
XP_ASSERT( !!dict );
|
|
model_setDictionary( globals->game.model, dict );
|
|
}
|
|
|
|
if ( newGame ) {
|
|
TransportProcs procs;
|
|
ceInitTProcs( globals, &procs );
|
|
game_reset( MEMPOOL &globals->game, &globals->gameInfo, &globals->util,
|
|
0, &globals->appPrefs.cp, &procs );
|
|
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
if ( !!addr ) {
|
|
XP_ASSERT( globals->game.comms != NULL );
|
|
comms_setAddr( globals->game.comms, addr );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
XP_ASSERT( !!globals->game.board );
|
|
ceSizeIfFullscreen( globals, globals->hWnd );
|
|
(void)cePositionBoard( globals );
|
|
|
|
board_invalAll( globals->game.board );
|
|
InvalidateRect( globals->hWnd, NULL, TRUE /* erase */ );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->game.comms ) {
|
|
comms_start( globals->game.comms );
|
|
}
|
|
if ( newGame && globals->gameInfo.serverRole == SERVER_ISCLIENT ) {
|
|
XWStreamCtxt* stream;
|
|
XP_ASSERT( !!globals->game.comms );
|
|
stream = make_generic_stream( globals );
|
|
stream_setOnCloseProc( stream, ce_send_on_close );
|
|
server_initClientConnection( globals->game.server, stream );
|
|
}
|
|
#endif
|
|
|
|
ceSetLeftSoftkey( globals, ID_MOVE_TURNDONE );
|
|
|
|
server_do( globals->game.server );
|
|
|
|
globals->isNewGame = FALSE;
|
|
|
|
if ( !!toBeDestroyed ) {
|
|
dict_destroy( toBeDestroyed );
|
|
}
|
|
} /* ceInitAndStartBoard */
|
|
|
|
static XP_UCHAR*
|
|
ceReadString( const CEAppGlobals* XP_UNUSED_DBG(globals), HANDLE fileH )
|
|
{
|
|
XP_U16 nameLen;
|
|
XP_UCHAR* name = NULL;
|
|
XP_U32 nRead;
|
|
|
|
if ( ReadFile( fileH, &nameLen, sizeof(nameLen), &nRead, NULL )
|
|
&& nameLen > 0 ) {
|
|
name = XP_MALLOC( globals->mpool, nameLen + 1 );
|
|
ReadFile( fileH, name, nameLen, &nRead, NULL );
|
|
name[nameLen] = '\0';
|
|
}
|
|
|
|
return name;
|
|
} /* ceReadString */
|
|
|
|
static void
|
|
ceWriteString( const XP_UCHAR* str, HANDLE fileH )
|
|
{
|
|
XP_U32 nWritten;
|
|
XP_U16 len = !!str? XP_STRLEN( str ) : 0;
|
|
WriteFile( fileH, &len, sizeof(len), &nWritten, NULL );
|
|
if ( 0 < len ) {
|
|
WriteFile( fileH, str, len, &nWritten, NULL );
|
|
}
|
|
}
|
|
|
|
static void
|
|
ceSavePrefs( CEAppGlobals* globals )
|
|
{
|
|
HANDLE fileH;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
|
|
(void)ceGetPath( globals, PREFS_FILE_PATH_L, path, VSIZE(path) );
|
|
fileH = CreateFile( path, GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL );
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_U32 nWritten;
|
|
|
|
SetFilePointer( fileH, 0, 0, FILE_BEGIN );
|
|
/* write prefs, including version num */
|
|
WriteFile( fileH, &globals->appPrefs, sizeof(globals->appPrefs),
|
|
&nWritten, NULL );
|
|
|
|
ceWriteString( globals->curGameName, fileH );
|
|
|
|
WriteFile( fileH, &globals->flags, sizeof(globals->flags), &nWritten,
|
|
NULL );
|
|
|
|
ceWriteString( globals->langFileName, fileH );
|
|
|
|
SetEndOfFile( fileH ); /* truncate anything previously there */
|
|
|
|
CloseHandle( fileH ); /* am I not supposed to do this? PENDING */
|
|
XP_LOGW( "prefs file written", path );
|
|
} else {
|
|
logLastError( "failed to create prefs file" );
|
|
}
|
|
} /* ceSavePrefs */
|
|
|
|
static XP_Bool
|
|
peekVersion( HANDLE fileH, XP_U16* version )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
XP_U32 nRead;
|
|
success = ReadFile( fileH, version, sizeof(*version), &nRead, NULL );
|
|
if ( success ) {
|
|
SetFilePointer( fileH, -nRead, 0, FILE_CURRENT );
|
|
}
|
|
return success;
|
|
} /* peekVersion */
|
|
|
|
static XP_Bool
|
|
canUpdatePrefs( CEAppGlobals* globals, HANDLE fileH, XP_U16 curVersion,
|
|
CEAppPrefs* prefs )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
LOG_FUNC();
|
|
if ( (curVersion == 0x0002) && (CUR_CE_PREFS_FLAGS == 0x0003) ) {
|
|
CEAppPrefs0002 oldPrefs;
|
|
XP_U32 nRead;
|
|
if ( ReadFile( fileH, &oldPrefs, sizeof(oldPrefs), &nRead, NULL ) ) {
|
|
ceInitPrefs( globals, prefs );
|
|
|
|
XP_MEMCPY( &prefs->cp, &oldPrefs.cp, sizeof(prefs->cp) );
|
|
prefs->showColors = oldPrefs.showColors;
|
|
|
|
XP_MEMCPY( &prefs->colors[0], &oldPrefs.colors[0],
|
|
CE_FOCUS_COLOR*sizeof(prefs->colors[0]));
|
|
XP_ASSERT( CE_PLAYER0_COLOR - 1 == CE_FOCUS_COLOR );
|
|
XP_MEMCPY( &prefs->colors[CE_PLAYER0_COLOR],
|
|
&oldPrefs.colors[CE_FOCUS_COLOR],
|
|
(CE_NUM_COLORS-CE_PLAYER0_COLOR)
|
|
* sizeof(prefs->colors[0]));
|
|
success = XP_TRUE;
|
|
} else {
|
|
XP_LOGF( "%s: ReadFile bad", __func__ );
|
|
}
|
|
} else {
|
|
XP_LOGF( "%s: can't convert from %d to %d", __func__,
|
|
curVersion, CUR_CE_PREFS_FLAGS );
|
|
}
|
|
LOG_RETURNF( "%d", (int)success );
|
|
return success;
|
|
} /* canUpdatePrefs */
|
|
|
|
static XP_Bool
|
|
ceLoadPrefs( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
HANDLE fileH;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
|
|
(void)ceGetPath( globals, PREFS_FILE_PATH_L, path, VSIZE(path) );
|
|
fileH = CreateFile( path, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_HIDDEN, NULL );
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_U32 fileSize = GetFileSize( fileH, NULL );
|
|
XP_U16 curVersion;
|
|
if ( fileSize >= sizeof(curVersion) && peekVersion( fileH,
|
|
&curVersion ) ) {
|
|
CEAppPrefs tmpPrefs;
|
|
if ( curVersion == CUR_CE_PREFS_FLAGS ) {
|
|
if ( fileSize >= sizeof( CEAppPrefs ) ) {
|
|
XP_U32 bytesRead;
|
|
if ( ReadFile( fileH, &tmpPrefs, sizeof(tmpPrefs),
|
|
&bytesRead, NULL ) ) {
|
|
XP_ASSERT( tmpPrefs.versionFlags == CUR_CE_PREFS_FLAGS );
|
|
result = XP_TRUE;
|
|
}
|
|
}
|
|
} else if ( canUpdatePrefs( globals, fileH, curVersion,
|
|
&tmpPrefs ) ) {
|
|
result = XP_TRUE;
|
|
} else {
|
|
XP_LOGF( "%s: old prefs; cannot read.", __func__ );
|
|
}
|
|
|
|
if ( result ) {
|
|
XP_U16 flags;
|
|
XP_U32 nRead;
|
|
|
|
XP_MEMCPY( &globals->appPrefs, &tmpPrefs,
|
|
sizeof(globals->appPrefs) );
|
|
|
|
globals->curGameName = ceReadString( globals, fileH );
|
|
|
|
if ( ReadFile( fileH, &flags, sizeof(flags), &nRead,
|
|
NULL )
|
|
&& nRead == sizeof(flags) ) {
|
|
} else {
|
|
flags = 0;
|
|
}
|
|
globals->flags = flags;
|
|
|
|
globals->langFileName = ceReadString( globals, fileH );
|
|
}
|
|
}
|
|
CloseHandle( fileH );
|
|
} else {
|
|
XP_LOGF( "ceLoadPrefs: prefs file not found" );
|
|
}
|
|
return result; /* none found */
|
|
} /* ceLoadPrefs */
|
|
|
|
static XWStreamCtxt*
|
|
make_generic_stream( const CEAppGlobals* globals )
|
|
{
|
|
return mem_stream_make( MPPARM(globals->mpool) globals->vtMgr,
|
|
(void*)globals, 0, NULL );
|
|
} /* make_generic_stream */
|
|
|
|
static XWStreamCtxt*
|
|
fileToStream( const CEAppGlobals* globals, const XP_UCHAR* path )
|
|
{
|
|
XWStreamCtxt* stream = NULL;
|
|
HANDLE fileH;
|
|
wchar_t widebuf[257];
|
|
XP_U16 len;
|
|
|
|
len = (XP_U16)XP_STRLEN( path );
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len + 1,
|
|
widebuf, len + 1 );
|
|
|
|
fileH = CreateFile( widebuf, GENERIC_READ, 0, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_U32 len = GetFileSize( fileH, NULL );
|
|
XP_U32 nRead;
|
|
XP_UCHAR* buf = XP_MALLOC( globals->mpool, len );
|
|
|
|
XP_DEBUGF( "fileSize = %ld", len );
|
|
|
|
ReadFile( fileH, buf, len, &nRead, NULL );
|
|
CloseHandle( fileH );
|
|
|
|
stream = make_generic_stream( globals );
|
|
stream_open( stream );
|
|
stream_putBytes( stream, buf, (XP_U16)nRead );
|
|
|
|
XP_FREE( globals->mpool, buf );
|
|
}
|
|
|
|
XP_DEBUGF( "fileToStream => 0x%lx", (unsigned long)stream );
|
|
|
|
return stream;
|
|
} /* fileToStream */
|
|
|
|
static XP_Bool
|
|
ceLoadSavedGame( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool success = XP_FALSE;
|
|
XWStreamCtxt* stream;
|
|
|
|
LOG_FUNC();
|
|
|
|
stream = fileToStream( globals, globals->curGameName );
|
|
success = stream != NULL;
|
|
if ( success ) {
|
|
DictionaryCtxt* dict;
|
|
XP_U8 flags = stream_getU8( stream );
|
|
XP_Bool hasDict = (flags & 0x01) != 0;
|
|
flags >>= 1;
|
|
|
|
if ( hasDict ) {
|
|
#ifdef STUBBED_DICT
|
|
XP_ASSERT(0); /* just don't do this!!!! */
|
|
#else
|
|
XP_UCHAR* dictName = stringFromStream( globals->mpool, stream );
|
|
dict = ce_dictionary_make( globals, dictName );
|
|
success = dict != NULL;
|
|
if ( !success ) {
|
|
XP_UCHAR buf[128];
|
|
snprintf( buf, VSIZE(buf),
|
|
ceGetResString( globals, IDS_CANNOTOPEN_DICT ),
|
|
dictName );
|
|
buf[VSIZE(buf)-1] = '\0';
|
|
ceOops( globals, buf );
|
|
|
|
}
|
|
XP_FREE( globals->mpool, dictName );
|
|
#endif
|
|
} else {
|
|
dict = NULL;
|
|
}
|
|
|
|
if ( success ) {
|
|
TransportProcs procs;
|
|
|
|
if ( flags >= CE_GAMEFILE_VERSION1 ) {
|
|
ce_draw_fromStream( globals->draw, stream, flags );
|
|
}
|
|
|
|
XP_DEBUGF( "calling game_makeFromStream" );
|
|
ceInitTProcs( globals, &procs );
|
|
success = game_makeFromStream( MEMPOOL stream, &globals->game,
|
|
&globals->gameInfo, dict,
|
|
&globals->util,
|
|
(DrawCtx*)globals->draw,
|
|
&globals->appPrefs.cp, &procs );
|
|
if ( success ) {
|
|
ceSetTitleFromName( globals );
|
|
} else {
|
|
if ( !!dict ) {
|
|
dict_destroy( dict );
|
|
}
|
|
ceOopsId( globals, IDS_CANNOTOPEN_GAME );
|
|
}
|
|
}
|
|
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
return success;
|
|
} /* ceLoadSavedGame */
|
|
|
|
static void
|
|
colorsFromRsrc( const CEAppGlobals* globals, CEAppPrefs* prefs )
|
|
{
|
|
XP_U16 i;
|
|
HGLOBAL globH;
|
|
HRSRC rsrcH;
|
|
XP_U16* ptr;
|
|
|
|
rsrcH = FindResource( globals->hInst, MAKEINTRESOURCE(ID_COLORS_RES),
|
|
TEXT("CLRS") );
|
|
globH = LoadResource( globals->hInst, rsrcH );
|
|
ptr = (XP_U16*)globH;
|
|
|
|
for ( i = 0; i < CE_NUM_COLORS; ++i ) {
|
|
XP_U8 r = (XP_U8)*ptr++;
|
|
XP_U8 g = (XP_U8)*ptr++;
|
|
XP_U8 b = (XP_U8)*ptr++;
|
|
prefs->colors[i] = RGB( r, g, b );
|
|
}
|
|
|
|
DeleteObject( globH );
|
|
} /* colorsFromRsrc */
|
|
|
|
static void
|
|
ceInitPrefs( CEAppGlobals* globals, CEAppPrefs* prefs )
|
|
{
|
|
prefs->versionFlags = CUR_CE_PREFS_FLAGS;
|
|
prefs->showColors = XP_TRUE;
|
|
prefs->fullScreen = XP_FALSE;
|
|
|
|
prefs->cp.showBoardArrow = XP_TRUE;
|
|
prefs->cp.showRobotScores = XP_FALSE;
|
|
|
|
colorsFromRsrc( globals, prefs );
|
|
} /* ceInitPrefs */
|
|
|
|
#ifdef _WIN32_WCE
|
|
static void
|
|
getOSInfo( CEAppGlobals* globals )
|
|
{
|
|
OSVERSIONINFO ver = {0};
|
|
TCHAR buf[128];
|
|
XW_WinceVersion winceVersion = WINCE_UNKNOWN;
|
|
|
|
if ( GetVersionEx( &ver )) {
|
|
XP_LOGF( "version = %ld.%ld", ver.dwMajorVersion, ver.dwMinorVersion );
|
|
} else {
|
|
XP_WARNF( "GetVersionEx failed" );
|
|
}
|
|
|
|
if ( SystemParametersInfo( SPI_GETPLATFORMTYPE, sizeof(buf), buf, FALSE ) ) {
|
|
if ( 0 == lstrcmp( buf, L"PocketPC") ) {
|
|
// We are on a Pocket PC, so check the OS version,
|
|
// Pocket PC 2003 used WinCE 4.2
|
|
if ( ver.dwMajorVersion < 4 ) {
|
|
winceVersion = WINCE_PPC_V1;
|
|
} else if ( ver.dwMajorVersion == 4 ) {
|
|
winceVersion = WINCE_PPC_2003;
|
|
} else if ( ver.dwMajorVersion > 4 ) {
|
|
winceVersion = WINCE_PPC_2005;
|
|
}
|
|
} else if ( 0 == lstrcmp( buf, L"SmartPhone") ) {
|
|
if ( ver.dwMajorVersion < 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_V1;
|
|
} else if ( ver.dwMajorVersion == 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_2003;
|
|
} else if ( ver.dwMajorVersion > 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_2005;
|
|
}
|
|
} else {
|
|
XP_LOGW( "unknown OS type", buf );
|
|
}
|
|
} else if ( GetLastError() == ERROR_ACCESS_DENIED ) {
|
|
if ( ver.dwMajorVersion < 4 ) {
|
|
winceVersion = WINCE_SMARTPHONE_V1;
|
|
} else {
|
|
winceVersion = WINCE_SMARTPHONE_2003;
|
|
}
|
|
}
|
|
|
|
XP_ASSERT( winceVersion != WINCE_UNKNOWN );
|
|
globals->winceVersion = winceVersion;
|
|
XP_LOGF( "%s: set version to %d", __func__, winceVersion );
|
|
} /* getOSInfo */
|
|
#else
|
|
#define getOSInfo( g )
|
|
#endif
|
|
|
|
//
|
|
// FUNCTION: InitInstance(HANDLE, int)
|
|
//
|
|
// PURPOSE: Saves instance handle and creates main window
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// In this function, we save the instance handle in a global variable and
|
|
// create and display the main program window.
|
|
//
|
|
BOOL
|
|
InitInstance(HINSTANCE hInstance, int nCmdShow
|
|
#ifndef _WIN32_WCE
|
|
,XP_U16 width, XP_U16 height, const char* dll
|
|
#endif
|
|
)
|
|
{
|
|
HWND hWnd;
|
|
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
|
|
TCHAR szWindowClass[MAX_LOADSTRING]; // The window class name
|
|
CEAppGlobals* globals;
|
|
BOOL result = FALSE;
|
|
XP_Bool oldGameLoaded;
|
|
XP_Bool prevStateExists;
|
|
XP_Bool newDone = XP_FALSE;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
MPSLOT;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
{
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
int err;
|
|
wVersionRequested = MAKEWORD( 2, 2 );
|
|
err = WSAStartup( wVersionRequested, &wsaData );
|
|
if ( err != 0 ) {
|
|
/* Tell the user that we could not find a usable */
|
|
/* WinSock DLL. */
|
|
XP_LOGF( "unable to load winsock" );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
|
|
LoadString(hInstance, IDC_XWORDS4, szWindowClass, MAX_LOADSTRING);
|
|
|
|
// If it is already running, then focus on the window. Skip title in
|
|
// search as we change title to include game name
|
|
hWnd = FindWindow( szWindowClass, NULL );
|
|
if ( hWnd ) {
|
|
SetForegroundWindow( (HWND)((ULONG) hWnd | 0x00000001) );
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef MEM_DEBUG
|
|
mpool = mpool_make();
|
|
#endif
|
|
|
|
globals = (CEAppGlobals*)XP_MALLOC( mpool, sizeof(*globals) );
|
|
XP_DEBUGF( "globals created: 0x%p", globals );
|
|
XP_MEMSET( globals, 0, sizeof(*globals) );
|
|
MPASSIGN( globals->mpool, mpool );
|
|
|
|
#ifndef _WIN32_WCE
|
|
globals->dbWidth = width;
|
|
globals->dbHeight = height;
|
|
#endif
|
|
|
|
(void)ceGetPath( globals, DEFAULT_DIR_PATH_L, path, VSIZE(path) );
|
|
(void)CreateDirectory( path, 0 );
|
|
|
|
getOSInfo( globals );
|
|
|
|
globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
|
|
|
|
globals->hInst = hInstance;
|
|
// Initialize global strings
|
|
MyRegisterClass(hInstance, szWindowClass);
|
|
|
|
prevStateExists = ceLoadPrefs( globals );
|
|
if ( !prevStateExists ) {
|
|
ceInitPrefs( globals, &globals->appPrefs );
|
|
}
|
|
|
|
#ifndef _WIN32_WCE
|
|
srand( time(NULL) );
|
|
|
|
/* Was a language file named in preferences? If so, and if none was
|
|
provided on the cmdline, load it (if it exists; if it doesn't, act as
|
|
if none set). */
|
|
if ( !!dll && !!dll[0] ) {
|
|
replaceStringIfDifferent( globals->mpool, &globals->langFileName, dll );
|
|
}
|
|
#endif
|
|
|
|
if ( !!globals->langFileName && !globals->locInst ) {
|
|
HINSTANCE inst = ceLoadResFile( globals->langFileName );
|
|
if ( !!inst ) {
|
|
globals->locInst = inst;
|
|
} else {
|
|
XP_FREE( globals->mpool, globals->langFileName );
|
|
globals->langFileName = NULL;
|
|
}
|
|
}
|
|
if ( !globals->locInst ) {
|
|
globals->locInst = globals->hInst;
|
|
}
|
|
|
|
hWnd = CreateWindow( szWindowClass, szTitle, WS_VISIBLE,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, NULL, NULL, hInstance, globals );
|
|
if (!hWnd) {
|
|
goto exit;
|
|
}
|
|
globals->hWnd = hWnd;
|
|
|
|
#ifdef _WIN32_WCE
|
|
if ( globals->hwndCB && !IS_SMARTPHONE(globals) ) {
|
|
RECT rc, rcmb;
|
|
|
|
GetWindowRect( hWnd, &rc );
|
|
GetWindowRect( globals->hwndCB, &rcmb );
|
|
rc.bottom -= (rcmb.bottom - rcmb.top);
|
|
|
|
MoveWindow( hWnd, rc.left, rc.top, rc.right-rc.left,
|
|
rc.bottom-rc.top, FALSE );
|
|
}
|
|
#endif
|
|
|
|
ceInitUtilFuncs( globals );
|
|
|
|
gi_initPlayerInfo( MPPARM(mpool) &globals->gameInfo, NULL );
|
|
|
|
|
|
#ifndef _WIN32_WCE
|
|
SetMenu( hWnd, LoadMenu( globals->locInst, MAKEINTRESOURCE(IDM_MENU) ) );
|
|
#endif
|
|
|
|
/* choose one. If none found it's an error. */
|
|
#ifndef STUBBED_DICT
|
|
result = 1 == ceLocateNDicts( globals, 1, ceSetDictName,
|
|
globals );
|
|
if ( !result ) {
|
|
wchar_t buf[512];
|
|
(void)LoadString( globals->locInst, (UINT)IDS_DICTLOC, buf, VSIZE(buf) );
|
|
assertOnTop( globals->hWnd );
|
|
MessageBox( globals->hWnd, buf,
|
|
ceGetResStringL( globals, IDS_NODICT_L ),
|
|
MB_OK | MB_ICONHAND );
|
|
goto exit;
|
|
}
|
|
#else
|
|
result = TRUE;
|
|
#endif
|
|
|
|
|
|
/* must load prefs before creating draw ctxt */
|
|
globals->draw = ce_drawctxt_make( MPPARM(globals->mpool)
|
|
hWnd, globals );
|
|
|
|
oldGameLoaded = prevStateExists && ceLoadSavedGame( globals );
|
|
|
|
if ( !oldGameLoaded ) {
|
|
TransportProcs procs;
|
|
ceInitTProcs( globals, &procs );
|
|
game_makeNewGame( MPPARM(mpool) &globals->game, &globals->gameInfo,
|
|
&globals->util, (DrawCtx*)globals->draw, 0,
|
|
&globals->appPrefs.cp, &procs );
|
|
|
|
newDone = ceDoNewGame( globals ); /* calls ceInitAndStartBoard */
|
|
if ( !newDone ) {
|
|
result = FALSE;
|
|
}
|
|
}
|
|
|
|
trapBackspaceKey( hWnd );
|
|
|
|
ShowWindow(hWnd, nCmdShow);
|
|
UpdateWindow(hWnd);
|
|
#ifdef _WIN32_WCE
|
|
if (globals->hwndCB) {
|
|
CommandBar_Show(globals->hwndCB, TRUE);
|
|
}
|
|
#endif
|
|
if ( result && !newDone ) {
|
|
ceInitAndStartBoard( globals, !oldGameLoaded, NULL );
|
|
}
|
|
|
|
exit:
|
|
return result;
|
|
} /* InitInstance */
|
|
|
|
static XP_Bool
|
|
ceSetDictName( const wchar_t* XP_UNUSED(wPath), XP_U16 XP_UNUSED_DBG(index),
|
|
void* XP_UNUSED(ctxt) )
|
|
{
|
|
/* CEAppGlobals* globals = (CEAppGlobals*)ctxt; */
|
|
/* XP_UCHAR* str; */
|
|
/* XP_UCHAR buf[CE_MAX_PATH_LEN]; */
|
|
XP_ASSERT( index == 0 ); /* we said one only! */
|
|
|
|
/* WideCharToMultiByte( CP_ACP, 0, wPath, -1, */
|
|
/* buf, sizeof(buf)-1, NULL, NULL ); */
|
|
|
|
/* XP_LOGF( "%s: got path \"%s\"", __func__, buf ); */
|
|
/* str = copyString( MPPARM(globals->mpool) buf ); */
|
|
/* XP_LOGF( "%s: got %p", __func__, str ); /\* this is leaking *\/ */
|
|
/* XP_ASSERT( NULL == globals->gameInfo.dictName ); */
|
|
/* globals->gameInfo.dictName = str; */
|
|
return XP_FALSE;
|
|
} /* ceSetDictName */
|
|
|
|
static XP_Bool
|
|
ceHandleHintRequest( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool notDone;
|
|
XP_Bool draw;
|
|
XP_ASSERT( !!globals->game.board );
|
|
|
|
draw = board_requestHint( globals->game.board,
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
globals->askTrayLimits,
|
|
#endif
|
|
¬Done );
|
|
globals->hintPending = notDone;
|
|
if ( draw ) { /* don't turn on if disallowed */
|
|
ceSetLeftSoftkey( globals, ID_MOVE_NEXTHINT );
|
|
}
|
|
return draw;
|
|
} /* ceHandleHintRequest */
|
|
|
|
static XP_Bool
|
|
handleTradeCmd( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool success = board_beginTrade( globals->game.board );
|
|
if ( success ) {
|
|
ceSetLeftSoftkey( globals, ID_MOVE_TURNDONE );
|
|
}
|
|
return success;
|
|
} /* handleTradeCmd */
|
|
|
|
static XP_Bool
|
|
handleJuggleCmd( CEAppGlobals* globals )
|
|
{
|
|
ceSetLeftSoftkey( globals, ID_MOVE_JUGGLE );
|
|
return board_juggleTray( globals->game.board );
|
|
} /* handleJuggleCmd */
|
|
|
|
static XP_Bool
|
|
handleHidetrayCmd( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool result;
|
|
XW_TrayVisState curState = board_getTrayVisState( globals->game.board );
|
|
|
|
if ( curState == TRAY_REVEALED ) {
|
|
result = board_hideTray( globals->game.board );
|
|
} else {
|
|
result = board_showTray( globals->game.board );
|
|
}
|
|
|
|
ceSetLeftSoftkey( globals, ID_MOVE_HIDETRAY );
|
|
return result;
|
|
} /* handleHidetrayCmd */
|
|
|
|
static XP_Bool
|
|
handleDoneCmd( CEAppGlobals* globals)
|
|
{
|
|
return board_commitTurn( globals->game.board );
|
|
} /* handleDoneCmd */
|
|
|
|
static void
|
|
ceCountsAndValues( CEAppGlobals* globals )
|
|
{
|
|
if ( !!globals->game.server ) {
|
|
XWStreamCtxt* stream = make_generic_stream( globals );
|
|
|
|
server_formatDictCounts( globals->game.server, stream, 3 );
|
|
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_COUNTSVALS_L ),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
}
|
|
} /* ceCountsAndValues */
|
|
|
|
static void
|
|
ceTilesLeft( CEAppGlobals* globals )
|
|
{
|
|
if ( !!globals->game.board ) {
|
|
XWStreamCtxt* stream = make_generic_stream( globals );
|
|
board_formatRemainingTiles( globals->game.board, stream );
|
|
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_REMTILES_L ),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
}
|
|
} /* ceTilesLeft */
|
|
|
|
static void
|
|
ceDoHistory( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool gameOver = server_getGameIsOver(globals->game.server);
|
|
XWStreamCtxt* stream;
|
|
|
|
stream = make_generic_stream( globals );
|
|
|
|
model_writeGameHistory( globals->game.model, stream,
|
|
globals->game.server, gameOver );
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_GAMEHIST_L ),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
} /* ceDoHistory */
|
|
|
|
static wchar_t
|
|
ceStateChar( const CEAppGlobals* globals )
|
|
{
|
|
/* Idea is to give user a clue how the network connection's coming.
|
|
Relay only matters if we have a socket open. So use that first. */
|
|
CommsRelayState relayState = globals->relayState;
|
|
CeConnState socketState = globals->socketState;
|
|
wchar_t ch = L'A'; /* keep compiler quiet */
|
|
|
|
if ( socketState == CE_IPST_CONNECTED ) {
|
|
switch( relayState ) {
|
|
case COMMS_RELAYSTATE_UNCONNECTED: ch = L'D'; break;
|
|
case COMMS_RELAYSTATE_CONNECT_PENDING: ch = L'C'; break;
|
|
case COMMS_RELAYSTATE_CONNECTED: ch = L'B'; break;
|
|
case COMMS_RELAYSTATE_ALLCONNECTED: /*ch = L'A'; */ break;
|
|
}
|
|
} else {
|
|
switch( socketState ) {
|
|
case CE_IPST_START:
|
|
case CE_IPST_RESOLVINGHOST: ch = L'4'; break;
|
|
case CE_IPST_HOSTRESOLVED: ch = L'3'; break;
|
|
case CE_IPST_CONNECTING: ch = L'2'; break;
|
|
case CE_IPST_CONNECTED: ch = L'1'; break;
|
|
}
|
|
}
|
|
return ch;
|
|
}
|
|
|
|
static void
|
|
drawInsidePaint( CEAppGlobals* globals, const RECT* invalR )
|
|
{
|
|
HDC hdc;
|
|
|
|
hdc = GetDC( globals->hWnd );
|
|
if ( !hdc ) {
|
|
logLastError( __func__ );
|
|
} else {
|
|
int oldMode = SetBkMode( hdc, TRANSPARENT );
|
|
HDC prevHDC = globals->hdc;
|
|
globals->hdc = hdc;
|
|
|
|
if ( !!invalR ) {
|
|
XP_U16 ii;
|
|
RECT interR;
|
|
for ( ii = 0; ii < N_OWNED_RECTS; ++ii ) {
|
|
if ( IntersectRect( &interR, invalR,
|
|
&globals->ownedRects[ii] ) ) {
|
|
ce_draw_erase( globals->draw, &interR );
|
|
}
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( IntersectRect( &interR, invalR, &globals->relayStatusR ) ) {
|
|
wchar_t ch = ceStateChar( globals );
|
|
ce_draw_status( globals->draw, &globals->relayStatusR, ch );
|
|
}
|
|
#endif
|
|
|
|
#ifdef _WIN32_WCE
|
|
for ( ii = 0; ii < VSIZE(globals->scrollRects); ++ii ) {
|
|
if ( IntersectRect( &interR, invalR,
|
|
&globals->scrollRects[ii] ) ) {
|
|
if ( globals->scrollerHasFocus ) {
|
|
ce_draw_focus( globals->draw, &interR );
|
|
} else {
|
|
ce_draw_erase( globals->draw, &interR );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
board_draw( globals->game.board );
|
|
|
|
(void)SetBkMode( hdc, oldMode );
|
|
globals->hdc = prevHDC;
|
|
}
|
|
} /* drawInsidePaint */
|
|
|
|
static void
|
|
ceDisplayFinalScores( CEAppGlobals* globals )
|
|
{
|
|
XWStreamCtxt* stream;
|
|
|
|
stream = make_generic_stream( globals );
|
|
server_writeFinalScores( globals->game.server, stream );
|
|
stream_putU8( stream, '\0' );
|
|
|
|
(void)ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_FINALSCORE_L),
|
|
MB_OK | MB_ICONINFORMATION, XP_TRUE );
|
|
} /* ceDisplayFinalScores */
|
|
|
|
static XP_Bool
|
|
ceDoNewGame( CEAppGlobals* globals )
|
|
{
|
|
GameInfoState giState;
|
|
CommsAddrRec* addr = NULL;
|
|
XP_Bool changed = XP_FALSE;
|
|
|
|
XP_MEMSET( &giState, 0, sizeof(giState) );
|
|
giState.dlgHdr.globals = globals;
|
|
giState.isNewGame = XP_TRUE;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_GAMEINFO, globals->hWnd,
|
|
(DLGPROC)GameInfo, (long)&giState );
|
|
|
|
if ( !giState.userCancelled
|
|
#ifndef STUBBED_DICT
|
|
&& ( giState.newDictName[0] != '\0' )
|
|
#endif
|
|
) {
|
|
|
|
if ( globals->curGameName != NULL ) {
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
globals->curGameName = NULL;
|
|
}
|
|
|
|
if ( giState.prefsChanged ) {
|
|
loadCurPrefsFromState( globals, &globals->appPrefs,
|
|
&globals->gameInfo, &giState.prefsPrefs );
|
|
if ( giState.colorsChanged ) {
|
|
updateForColors( globals );
|
|
}
|
|
}
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
if ( giState.addrChanged ) {
|
|
addr = &giState.prefsPrefs.addrRec;
|
|
}
|
|
#endif
|
|
|
|
ceInitAndStartBoard( globals, XP_TRUE, addr );
|
|
ceSetTitleFromName( globals );
|
|
changed = XP_TRUE;
|
|
}
|
|
|
|
return changed;
|
|
} /* ceDoNewGame */
|
|
|
|
static void
|
|
ceChooseAndOpen( CEAppGlobals* globals )
|
|
{
|
|
assertOnTop( globals->hWnd );
|
|
// Save in case we'll be duplicating it
|
|
again:
|
|
if ( ceSaveCurGame( globals, XP_FALSE ) ) {
|
|
SavedGamesResult choice;
|
|
wchar_t newName[256];
|
|
newName[0] = 0;
|
|
|
|
ceSetTitleFromName( globals ); /* in case we named it above */
|
|
|
|
choice = ceSavedGamesDlg( globals, globals->curGameName, newName,
|
|
VSIZE(newName) );
|
|
if ( CE_SVGAME_CANCEL != choice ) {
|
|
XP_UCHAR* name;
|
|
XP_U16 len;
|
|
|
|
len = wcslen(newName);
|
|
name = XP_MALLOC( globals->mpool, len + 1 );
|
|
|
|
WideCharToMultiByte( CP_ACP, 0, newName, len + 1,
|
|
name, len + 1, NULL, NULL );
|
|
|
|
if ( globals->curGameName != NULL
|
|
&& 0 == XP_STRCMP( name, globals->curGameName ) ){
|
|
/* User chose already-open game; no-op */
|
|
XP_FREE( globals->mpool, name );
|
|
} else {
|
|
/* Save old name in case fail to open new, e.g. because dict
|
|
not there */
|
|
XP_UCHAR* oldName;
|
|
|
|
/* Need to save a second time, with auto-save, in case user
|
|
wants to overwrite yet chooses a game whose dict is
|
|
missing -- since then we'll be re-opening this game! */
|
|
ceSaveCurGame( globals, XP_TRUE ); /* may change curGameName */
|
|
|
|
oldName = globals->curGameName;
|
|
globals->curGameName = NULL; /* prevent being destroyed */
|
|
closeGame( globals );
|
|
|
|
if ( CE_SVGAME_RENAME == choice ) {
|
|
XP_U16 len = 1 + XP_STRLEN( oldName );
|
|
wchar_t widebuf[len];
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, oldName, len,
|
|
widebuf, len );
|
|
(void)MoveFile( widebuf, newName );
|
|
}
|
|
|
|
globals->curGameName = name;
|
|
if ( ceLoadSavedGame( globals ) ) {
|
|
XP_FREE( globals->mpool, oldName );
|
|
} else {
|
|
XP_ASSERT( CE_SVGAME_RENAME != choice );
|
|
XP_LOGF( "failed to open chosen game" );
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
globals->curGameName = oldName;
|
|
if ( !ceLoadSavedGame( globals ) ) {
|
|
XP_LOGF( "failed to open old game too!!!" );
|
|
}
|
|
}
|
|
ceInitAndStartBoard( globals, XP_FALSE, NULL );
|
|
if ( CE_SVGAME_RENAME == choice ) {
|
|
goto again;
|
|
}
|
|
}
|
|
} else {
|
|
XP_LOGF( "GetOpenFileName() failed" );
|
|
}
|
|
}
|
|
} /* ceChooseAndOpen */
|
|
|
|
static void
|
|
updateForColors( CEAppGlobals* globals )
|
|
{
|
|
ce_draw_update( globals->draw );
|
|
if ( !!globals->game.board ) {
|
|
board_invalAll( globals->game.board );
|
|
}
|
|
} /* updateForColors */
|
|
|
|
static void
|
|
ceDoPrefsDlg( CEAppGlobals* globals )
|
|
{
|
|
CePrefsPrefs prefsPrefs;
|
|
XP_Bool colorsChanged;
|
|
|
|
loadStateFromCurPrefs( globals, &globals->appPrefs, &globals->gameInfo,
|
|
&prefsPrefs );
|
|
|
|
assertOnTop( globals->hWnd );
|
|
if ( WrapPrefsDialog( globals->hWnd, globals, &prefsPrefs,
|
|
XP_FALSE, &colorsChanged ) ) {
|
|
loadCurPrefsFromState( globals, &globals->appPrefs, &globals->gameInfo,
|
|
&prefsPrefs );
|
|
|
|
(void)cePositionBoard( globals );
|
|
|
|
if ( colorsChanged ) {
|
|
updateForColors( globals );
|
|
}
|
|
/* need to reflect vars set in state into globals, and update/inval
|
|
as appropriate. */
|
|
}
|
|
} /* ceDoPrefsDlg */
|
|
|
|
static void
|
|
ceWriteToFile( XWStreamCtxt* stream, void* closure )
|
|
{
|
|
FileWriteState* fwState = (FileWriteState*)closure;
|
|
#ifdef MEM_DEBUG
|
|
CEAppGlobals* globals = fwState->globals;
|
|
#endif
|
|
XP_UCHAR* path = fwState->path;
|
|
XP_U16 len = (XP_U16)XP_STRLEN( path ) + 1; /* 1: null byte */
|
|
wchar_t widebuf[len];
|
|
HANDLE fileH;
|
|
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, path, len, widebuf, len );
|
|
|
|
fileH = CreateFile( widebuf, GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
if ( fileH != INVALID_HANDLE_VALUE ) {
|
|
XP_UCHAR* buf;
|
|
XP_U32 nWritten;
|
|
|
|
len = stream_getSize( stream );
|
|
buf = XP_MALLOC( globals->mpool, len );
|
|
stream_getBytes( stream, buf, len );
|
|
|
|
WriteFile( fileH, buf, len, &nWritten, NULL );
|
|
SetEndOfFile( fileH );
|
|
CloseHandle( fileH );
|
|
|
|
XP_FREE( globals->mpool, buf );
|
|
}
|
|
} /* ceWriteToFile */
|
|
|
|
static XP_Bool
|
|
isDefaultName( CEAppGlobals* globals, const XP_UCHAR* name )
|
|
{
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
(void)ceGetPath( globals, DEFAULT_GAME_PATH, path, VSIZE(path) );
|
|
return 0 == XP_STRCMP( path, name );
|
|
} /* isDefaultName */
|
|
|
|
static XP_Bool
|
|
ceSaveCurGame( CEAppGlobals* globals, XP_Bool autoSave )
|
|
{
|
|
XP_Bool confirmed = XP_FALSE;
|
|
/* If it doesn't yet have a name, get a path at which to save it. User
|
|
has a chance to cancel this. But if there is a name go ahead and save
|
|
using it and don't bother giving cancel chance -- since there's no
|
|
harm in making 'em restart. Not sure how this changes when IR's
|
|
involved. */
|
|
XP_UCHAR* name = globals->curGameName;
|
|
if ( name == NULL || isDefaultName( globals, name ) ) {
|
|
XP_UCHAR* newName = NULL;
|
|
|
|
if ( autoSave ) {
|
|
XP_U16 len;
|
|
wchar_t path[CE_MAX_PATH_LEN];
|
|
len = 1 + ceGetPath( globals, DEFAULT_GAME_PATH,
|
|
path, VSIZE(path) );
|
|
newName = XP_MALLOC( globals->mpool, len );
|
|
XP_MEMCPY( newName, path, len );
|
|
|
|
confirmed = XP_TRUE;
|
|
} else {
|
|
wchar_t nameBuf[MAX_PATH];
|
|
|
|
confirmed = ceConfirmUniqueName( globals, globals->hWnd,
|
|
IDS_SAVENAME, nameBuf,
|
|
VSIZE(nameBuf) );
|
|
if ( confirmed ) {
|
|
XP_U16 len = wcslen(nameBuf);
|
|
newName = XP_MALLOC( globals->mpool, len + 1 );
|
|
WideCharToMultiByte( CP_ACP, 0, nameBuf, len + 1,
|
|
newName, len + 1, NULL, NULL );
|
|
}
|
|
}
|
|
|
|
if ( confirmed ) {
|
|
XP_ASSERT( !!newName );
|
|
if ( !!globals->curGameName ) {
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
}
|
|
globals->curGameName = newName;
|
|
}
|
|
} else {
|
|
confirmed = XP_TRUE;
|
|
}
|
|
|
|
if ( confirmed ) {
|
|
if ( !!globals->game.server ) {
|
|
XWStreamCtxt* memStream;
|
|
DictionaryCtxt* dict;
|
|
FileWriteState fwState;
|
|
const char* dictName;
|
|
XP_U8 flags;
|
|
|
|
fwState.path = globals->curGameName;
|
|
fwState.globals = globals;
|
|
board_hideTray( globals->game.board ); /* so won't be visible when
|
|
next opened */
|
|
memStream = mem_stream_make( MEMPOOL globals->vtMgr, &fwState, 0,
|
|
ceWriteToFile );
|
|
stream_open( memStream );
|
|
|
|
/* the dictionary */
|
|
dict = model_getDictionary( globals->game.model );
|
|
#ifdef STUBBED_DICT /* don't check this in!!! */
|
|
dictName = NULL;
|
|
#else
|
|
dictName = !!dict? dict_getName( dict ) : NULL;
|
|
#endif
|
|
flags = !!dictName? 0x01 : 0x00;
|
|
flags |= CE_GAMEFILE_VERSION << 1;
|
|
stream_putU8( memStream, flags );
|
|
|
|
if ( !!dictName ) {
|
|
stringToStream( memStream, dictName );
|
|
}
|
|
|
|
ce_draw_toStream( globals->draw, memStream );
|
|
|
|
game_saveToStream( &globals->game, &globals->gameInfo, memStream );
|
|
|
|
stream_destroy( memStream );
|
|
}
|
|
}
|
|
|
|
return confirmed;
|
|
} /* ceSaveCurGame */
|
|
|
|
static void
|
|
ceSaveAndExit( CEAppGlobals* globals )
|
|
{
|
|
(void)ceSaveCurGame( globals, XP_TRUE );
|
|
ceSavePrefs( globals );
|
|
DestroyWindow(globals->hWnd);
|
|
} /* ceSaveAndExit */
|
|
|
|
static void
|
|
closeGame( CEAppGlobals* globals )
|
|
{
|
|
game_dispose( &globals->game );
|
|
gi_disposePlayerInfo( MPPARM(globals->mpool) &globals->gameInfo );
|
|
|
|
if ( !!globals->curGameName ) {
|
|
XP_FREE( globals->mpool, globals->curGameName );
|
|
}
|
|
}
|
|
|
|
static void
|
|
freeGlobals( CEAppGlobals* globals )
|
|
{
|
|
XP_U16 ii;
|
|
MPSLOT;
|
|
|
|
MPASSIGN( mpool, globals->mpool );
|
|
|
|
draw_destroyCtxt( (DrawCtx*)globals->draw );
|
|
|
|
closeGame( globals );
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
if ( !!globals->socketWrap ) {
|
|
ce_sockwrap_delete( globals->socketWrap );
|
|
globals->socketWrap = NULL;
|
|
}
|
|
WSACleanup();
|
|
#endif
|
|
|
|
if ( !!globals->vtMgr ) {
|
|
vtmgr_destroy( MPPARM(mpool) globals->vtMgr );
|
|
}
|
|
if ( !!globals->util.vtable ) {
|
|
XP_FREE( mpool, globals->util.vtable );
|
|
}
|
|
for ( ii = 0; ii < N_CACHED_PATHS; ++ii ) {
|
|
if ( !!globals->specialDirs[ii] ) {
|
|
XP_FREE( mpool, globals->specialDirs[ii] );
|
|
}
|
|
}
|
|
|
|
ceFreeResStrings( globals );
|
|
if ( globals->locInst != globals->hInst ) {
|
|
ceCloseResFile( globals->locInst );
|
|
}
|
|
if ( globals->langFileName != NULL ) {
|
|
XP_FREE( globals->mpool, globals->langFileName );
|
|
globals->langFileName = NULL;
|
|
}
|
|
|
|
XP_FREE( globals->mpool, globals );
|
|
mpool_destroy( mpool );
|
|
} /* freeGlobals */
|
|
|
|
#ifdef _WIN32_WCE
|
|
static HWND
|
|
makeCommandBar( HWND hwnd, HINSTANCE hInst )
|
|
{
|
|
SHMENUBARINFO mbi;
|
|
HWND result = NULL;
|
|
|
|
XP_MEMSET( &mbi, 0, sizeof(mbi) );
|
|
mbi.cbSize = sizeof(mbi);
|
|
mbi.hwndParent = hwnd;
|
|
mbi.nToolBarId = IDM_MAIN_MENUBAR;
|
|
mbi.hInstRes = hInst;
|
|
/* Don't set dwFlags if you want the Wince5 two-button softkey menu. */
|
|
/* mbi.dwFlags = SHCMBF_HMENU; */
|
|
|
|
//mbi.dwFlags = SHCMBF_HIDESIPBUTTON; /* eeh added. Why??? */
|
|
|
|
if ( SHCreateMenuBar(&mbi) ) {
|
|
result = mbi.hwndMB;
|
|
} else {
|
|
/* will want to use this to change menubar: SHEnableSoftkey? */
|
|
XP_LOGF( "SHCreateMenuBar failed" );
|
|
}
|
|
|
|
return result;
|
|
} /* makeCommandBar */
|
|
#endif
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
static XP_Bool
|
|
handleScroll( CEAppGlobals* globals, XP_S16 pos, /* only valid for THUMB* */
|
|
XP_S16 code, HWND wnd )
|
|
{
|
|
XP_Bool result = XP_FALSE;
|
|
|
|
if ( wnd == globals->scrollHandle ) {
|
|
XP_U16 curYOffset = board_getYOffset( globals->game.board );
|
|
XP_S16 newOffset = curYOffset;
|
|
|
|
XP_ASSERT( !!globals->game.board );
|
|
|
|
switch ( code ) {
|
|
/* case SB_BOTTOM: // Scrolls to the lower right */
|
|
/* case SB_ENDSCROLL: // Ends scroll */
|
|
|
|
case SB_LINEUP: // Scrolls one line up
|
|
case SB_PAGEUP: //
|
|
--newOffset;
|
|
break;
|
|
|
|
case SB_LINEDOWN: // Scrolls one line down
|
|
case SB_PAGEDOWN: // Scrolls one page down
|
|
++newOffset;
|
|
break;
|
|
|
|
case SB_THUMBTRACK: /* still dragging; don't redraw */
|
|
case SB_THUMBPOSITION:
|
|
newOffset = pos;
|
|
default:
|
|
break;
|
|
/* do nothing: leave newOffset == curYOffset */
|
|
}
|
|
|
|
result = curYOffset != newOffset
|
|
&& board_setYOffset( globals->game.board, newOffset );
|
|
}
|
|
return result;
|
|
} /* handleScroll */
|
|
#endif
|
|
|
|
static XP_Bool
|
|
ceFireTimer( CEAppGlobals* globals, XWTimerReason why )
|
|
{
|
|
XP_Bool draw = XP_FALSE;
|
|
XWTimerProc proc;
|
|
void* closure;
|
|
|
|
proc = globals->timerProcs[why];
|
|
if ( !!proc ) {
|
|
globals->timerProcs[why] = NULL;
|
|
closure = globals->timerClosures[why];
|
|
draw = (*proc)( closure, why );
|
|
/* Setting draw after firing timer allows scrolling to happen
|
|
while pen is held down. This is a hack. Perhaps having
|
|
the timer proc return whether drawing is needed would be
|
|
more consistent. */
|
|
} else {
|
|
XP_LOGF( "skipped timer; alread fired?" );
|
|
}
|
|
return draw;
|
|
} /* ceFireTimer */
|
|
|
|
/* WM_TIMER messages are low-priority. Hold a key down and key events will
|
|
* crowd it out of the queue so that the app doesn't see it until the key is
|
|
* released. There are more reliable timers, but they seem to require
|
|
* advanced techniques like semaphores. At least one article recommends
|
|
* polling over going to those lengths. This is better that polling. I hope
|
|
* it's enough.
|
|
*/
|
|
static XP_Bool
|
|
checkFireLateKeyTimer( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool drop = XP_FALSE;
|
|
XWTimerReason whys[] = { TIMER_PENDOWN, TIMER_TIMERTICK
|
|
#if defined XWFEATURE_RELAY || defined COMMS_HEARTBEAT
|
|
, TIMER_COMMS
|
|
#endif
|
|
};
|
|
XP_U32 now = GetCurrentTime();
|
|
XP_U16 i;
|
|
|
|
for ( i = 0; i < sizeof(whys)/sizeof(whys[0]); ++i ) {
|
|
XWTimerReason why = whys[i];
|
|
if ( !!globals->timerProcs[why] ) {
|
|
if ( now >= globals->timerWhens[why] ) {
|
|
(void)ceFireTimer( globals, why );
|
|
drop = XP_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return drop;
|
|
} /* checkFireLateKeyTimer */
|
|
|
|
static XP_Bool
|
|
checkPenDown( CEAppGlobals* globals )
|
|
{
|
|
XP_Bool draw = globals->penDown;
|
|
if ( draw ) {
|
|
draw = board_handlePenUp( globals->game.board, 0x7FFF, 0x7FFF );
|
|
globals->penDown = XP_FALSE;
|
|
}
|
|
return draw;
|
|
} /* checkPenDown */
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
|
|
static XP_Bool
|
|
ceCheckHandleFocusKey( CEAppGlobals* globals, WPARAM wParam, LPARAM lParam,
|
|
XP_Bool keyDown, XP_Bool* handledP )
|
|
{
|
|
XP_Bool draw = XP_FALSE;
|
|
|
|
/* Sometimes, e.g. after a menu is released, we get KEY_UP not preceeded
|
|
by KEY_DOWN. Just drop those. */
|
|
if ( !keyDown && !globals->keyDown ) {
|
|
XP_LOGF( "%s: keyUp not preceeded by keyDown: dropping", __func__ );
|
|
} else {
|
|
XP_Bool isRepeat = keyDown && ((HIWORD(lParam) & KF_REPEAT) != 0);
|
|
XP_Key key;
|
|
XP_S16 incr = 0;
|
|
|
|
switch ( wParam ) {
|
|
case VK_UP:
|
|
key = XP_CURSOR_KEY_UP;
|
|
incr = -1;
|
|
break;
|
|
case VK_RIGHT:
|
|
key = XP_CURSOR_KEY_RIGHT;
|
|
incr = 1;
|
|
break;
|
|
case VK_DOWN:
|
|
key = XP_CURSOR_KEY_DOWN;
|
|
incr = 1;
|
|
break;
|
|
case VK_LEFT:
|
|
key = XP_CURSOR_KEY_LEFT;
|
|
incr = -1;
|
|
break;
|
|
case 0x0d:
|
|
case 0x5d: /* center key on WinMo5 Treo (at least) -- but also ']'*/
|
|
case VK_HOME:
|
|
key = XP_RETURN_KEY;
|
|
if ( isRepeat ) {
|
|
(void)checkFireLateKeyTimer( globals );
|
|
}
|
|
break;
|
|
|
|
/* Still need to produce these somehow */
|
|
/* XP_CURSOR_KEY_ALTRIGHT, */
|
|
/* XP_CURSOR_KEY_ALTUP, */
|
|
/* XP_CURSOR_KEY_ALTLEFT, */
|
|
/* XP_CURSOR_KEY_ALTDOWN, */
|
|
|
|
default:
|
|
key = XP_KEY_NONE;
|
|
break;
|
|
}
|
|
|
|
if ( key != XP_KEY_NONE ) {
|
|
BoardCtxt* board = globals->game.board;
|
|
|
|
if ( isRepeat ) {
|
|
draw = board_handleKeyRepeat( board, key, handledP );
|
|
} else if ( keyDown ) {
|
|
draw = board_handleKeyDown( board, key, handledP );
|
|
} else {
|
|
draw = board_handleKeyUp( board, key, handledP );
|
|
}
|
|
|
|
if ( !*handledP && incr != 0 && !keyDown ) {
|
|
BoardObjectType orderScroll[] = {
|
|
OBJ_SCORE, OBJ_BOARD, OBJ_NONE, OBJ_TRAY };
|
|
BoardObjectType orderNoScroll[] = {
|
|
OBJ_SCORE, OBJ_BOARD, OBJ_TRAY };
|
|
BoardObjectType* order;
|
|
XP_U16 orderLen;
|
|
BoardObjectType cur = board_getFocusOwner( board );
|
|
XP_U16 index = 0;
|
|
|
|
if ( !!globals->scrollHandle ) {
|
|
order = orderScroll;
|
|
orderLen = VSIZE(orderScroll);
|
|
} else {
|
|
order = orderNoScroll;
|
|
orderLen = VSIZE(orderNoScroll);
|
|
}
|
|
|
|
if ( !!globals->scrollHandle || (cur != OBJ_NONE) ) {
|
|
for ( ; ; ) {
|
|
if ( order[index] == cur ) {
|
|
break;
|
|
}
|
|
++index;
|
|
XP_ASSERT( index < orderLen );
|
|
}
|
|
index = (index + orderLen + incr) % orderLen;
|
|
}
|
|
draw = board_focusChanged( board, order[index], XP_TRUE );
|
|
|
|
if ( !!globals->scrollHandle ) {
|
|
XP_Bool scrollerHasFocus = globals->scrollerHasFocus;
|
|
if ( order[index] == OBJ_NONE ) {
|
|
XP_ASSERT( !scrollerHasFocus );
|
|
SetFocus( globals->scrollHandle );
|
|
scrollerHasFocus = XP_TRUE;
|
|
} else if ( scrollerHasFocus ) {
|
|
SetFocus( globals->hWnd );
|
|
scrollerHasFocus = XP_FALSE;
|
|
}
|
|
if ( scrollerHasFocus != globals->scrollerHasFocus ) {
|
|
globals->scrollerHasFocus = scrollerHasFocus;
|
|
#ifdef _WIN32_WCE
|
|
InvalidateRect( globals->hWnd, &globals->scrollRects[0], FALSE );
|
|
InvalidateRect( globals->hWnd, &globals->scrollRects[1], FALSE );
|
|
#else
|
|
InvalidateRect( globals->scrollHandle, NULL, FALSE );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
globals->keyDown = keyDown;
|
|
return draw;
|
|
} /* ceCheckHandleFocusKey */
|
|
#endif /* KEYBOARD_NAV */
|
|
|
|
static void
|
|
ceToggleFullScreen( CEAppGlobals* globals )
|
|
{
|
|
globals->appPrefs.fullScreen = !globals->appPrefs.fullScreen;
|
|
|
|
ceSizeIfFullscreen( globals, globals->hWnd );
|
|
|
|
(void)cePositionBoard( globals );
|
|
} /* ceToggleFullScreen */
|
|
|
|
static void
|
|
doAbout( CEAppGlobals* globals )
|
|
{
|
|
wchar_t buf[1024];
|
|
(void)LoadString( globals->locInst, (UINT)IDS_ABOUT, buf, VSIZE(buf) );
|
|
assertOnTop( globals->hWnd );
|
|
MessageBox( globals->hWnd, buf, ceGetResStringL( globals, IDS_ABOUT_L ),
|
|
MB_OK | MB_ICONINFORMATION );
|
|
}
|
|
|
|
LRESULT CALLBACK
|
|
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT result = 0;
|
|
int wmId;
|
|
XP_Bool draw = XP_FALSE;
|
|
XWTimerReason why;
|
|
CEAppGlobals* globals;
|
|
XP_Bool handled = XP_FALSE;
|
|
XP_Bool callDefault = XP_FALSE;
|
|
|
|
if ( message == WM_CREATE ) {
|
|
globals = ((CREATESTRUCT*)lParam)->lpCreateParams;
|
|
SetWindowLongPtr( hWnd, GWL_USERDATA, (long)globals );
|
|
#ifdef _WIN32_WCE
|
|
globals->hwndCB = makeCommandBar( hWnd, globals->locInst );
|
|
#endif
|
|
|
|
#ifdef _WIN32_WCE
|
|
globals->sai.cbSize = sizeof(globals->sai);
|
|
#endif
|
|
} else {
|
|
/* XP_LOGF( "%s: event=%s (%d)", __func__, messageToStr(message), message ); */
|
|
globals = (CEAppGlobals*)GetWindowLongPtr( hWnd, GWL_USERDATA );
|
|
|
|
switch (message) {
|
|
|
|
#ifdef _WIN32_WCE
|
|
case WM_ACTIVATE:
|
|
// Notify shell of our activate message
|
|
SHHandleWMActivate( hWnd, wParam, lParam, &globals->sai, FALSE );
|
|
break;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
SHHandleWMSettingChange( hWnd, wParam, lParam, &globals->sai );
|
|
if ( !!globals && !!globals->game.model ) {
|
|
cePositionBoard( globals );
|
|
board_invalAll( globals->game.board );
|
|
draw = XP_TRUE;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
# ifndef _WIN32_WCE
|
|
/* WM_CTLCOLORSCROLLBAR aren't delivered on CE. Some say can
|
|
* work around using WM_PAINT or WM_ERASEBKGND but no luck
|
|
* yet. */
|
|
case WM_CTLCOLORSCROLLBAR:
|
|
if ( (HWND)lParam == globals->scrollHandle ) {
|
|
if ( globals->scrollerHasFocus ) {
|
|
return (LRESULT)ce_draw_getFocusBrush( globals->draw );
|
|
}
|
|
}
|
|
break;
|
|
# endif
|
|
case WM_VSCROLL:
|
|
draw = checkPenDown( globals );
|
|
draw = handleScroll( globals, (short int)HIWORD(wParam),
|
|
(short int)LOWORD(wParam),
|
|
(HWND)lParam ) || draw;
|
|
break;
|
|
#endif
|
|
|
|
case WM_COMMAND:
|
|
(void)checkPenDown( globals );
|
|
wmId = LOWORD(wParam);
|
|
|
|
// Parse the menu selections:
|
|
switch (wmId) {
|
|
case ID_FILE_ABOUT:
|
|
doAbout( globals );
|
|
break;
|
|
|
|
case ID_GAME_GAMEINFO: {
|
|
GameInfoState state;
|
|
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
state.dlgHdr.globals = globals;
|
|
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_GAMEINFO, hWnd,
|
|
(DLGPROC)GameInfo, (long)&state );
|
|
|
|
if ( !state.userCancelled ) {
|
|
if ( state.prefsChanged ) {
|
|
updateForColors( globals );
|
|
}
|
|
draw = server_do( globals->game.server );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ID_FILE_NEWGAME:
|
|
XP_LOGF( "ID_FILE_NEWGAME" );
|
|
if ( ceSaveCurGame( globals, XP_FALSE )
|
|
|| queryBoxChar( globals, hWnd,
|
|
ceGetResString( globals,
|
|
IDS_OVERWRITE ) ) ) {
|
|
draw = ceDoNewGame( globals );
|
|
}
|
|
break;
|
|
|
|
case ID_FILE_SAVEDGAMES:
|
|
ceChooseAndOpen( globals );
|
|
break;
|
|
|
|
case ID_FILE_PREFERENCES:
|
|
ceDoPrefsDlg( globals );
|
|
break;
|
|
case ID_FILE_FULLSCREEN:
|
|
ceToggleFullScreen( globals );
|
|
break;
|
|
case ID_GAME_FINALSCORES:
|
|
if ( server_getGameIsOver( globals->game.server ) ) {
|
|
ceDisplayFinalScores( globals );
|
|
} else if ( queryBoxChar( globals, hWnd,
|
|
ceGetResString( globals,
|
|
IDS_ENDNOW ) ) ) {
|
|
server_endGame( globals->game.server );
|
|
draw = TRUE;
|
|
}
|
|
break;
|
|
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
case ID_GAME_RESENDMSGS:
|
|
if ( !!globals->game.comms ) {
|
|
(void)comms_resendAll( globals->game.comms );
|
|
} else {
|
|
ceOopsId( globals, IDS_RESEND_STANDALONE );
|
|
}
|
|
break;
|
|
#endif
|
|
case ID_GAME_TILECOUNTSANDVALUES:
|
|
ceCountsAndValues( globals );
|
|
break;
|
|
|
|
case ID_GAME_TILESLEFT:
|
|
ceTilesLeft( globals );
|
|
break;
|
|
|
|
case ID_GAME_HISTORY:
|
|
ceDoHistory( globals );
|
|
break;
|
|
|
|
case ID_MOVE_TRADE:
|
|
draw = handleTradeCmd( globals );
|
|
break;
|
|
case ID_MOVE_JUGGLE:
|
|
draw = handleJuggleCmd( globals );
|
|
break;
|
|
|
|
case ID_MOVE_HIDETRAY:
|
|
draw = handleHidetrayCmd( globals );
|
|
break;
|
|
case ID_MOVE_TURNDONE:
|
|
draw = handleDoneCmd( globals);
|
|
break;
|
|
|
|
case ID_MOVE_FLIP:
|
|
draw = board_flip( globals->game.board );
|
|
ceCheckMenus( globals );
|
|
break;
|
|
case ID_MOVE_VALUES:
|
|
draw = board_toggle_showValues( globals->game.board );
|
|
ceSetLeftSoftkey( globals, ID_MOVE_VALUES );
|
|
ceCheckMenus( globals );
|
|
break;
|
|
|
|
case ID_MOVE_HINT:
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
case ID_MOVE_LIMITEDHINT:
|
|
globals->askTrayLimits = wmId == ID_MOVE_LIMITEDHINT;
|
|
#endif
|
|
board_resetEngine( globals->game.board );
|
|
/* fallthru */
|
|
case ID_MOVE_NEXTHINT:
|
|
draw = ceHandleHintRequest( globals );
|
|
break;
|
|
|
|
case ID_FILE_EXIT:
|
|
ceSaveAndExit( globals ); /* autosaves; no user interaction */
|
|
break;
|
|
|
|
case ID_MOVE_UNDOCURRENT:
|
|
draw = board_replaceTiles( globals->game.board );
|
|
break;
|
|
|
|
case ID_MOVE_UNDOLAST:
|
|
draw = server_handleUndo( globals->game.server );
|
|
break;
|
|
|
|
default:
|
|
callDefault = XP_TRUE;
|
|
}
|
|
break;
|
|
case WM_PAINT:
|
|
if ( !!globals ) {
|
|
RECT winrect;
|
|
if ( GetUpdateRect( hWnd, &winrect, FALSE ) ) {
|
|
if ( !!globals->game.board ) {
|
|
XP_Rect xprect;
|
|
/* When an obscuring window goes away, the update region
|
|
needs to be redrawn. This allows invalidating it. */
|
|
|
|
RECTtoXPR( &xprect, &winrect );
|
|
board_invalRect( globals->game.board, &xprect );
|
|
|
|
XP_ASSERT( globals->hWnd == hWnd );
|
|
drawInsidePaint( globals, &winrect );
|
|
}
|
|
if ( !ValidateRect( hWnd, &winrect ) ) {
|
|
logLastError( "WM_PAINT:ValidateRect" );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
draw = checkPenDown( globals );
|
|
globals->penDown = XP_TRUE;
|
|
draw = board_handlePenDown( globals->game.board, LOWORD(lParam),
|
|
HIWORD(lParam), &handled )
|
|
|| draw;
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if ( globals->penDown ) {
|
|
draw = board_handlePenMove( globals->game.board,
|
|
LOWORD(lParam),
|
|
HIWORD(lParam) );
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if ( globals->penDown ) {
|
|
draw = board_handlePenUp( globals->game.board, LOWORD(lParam),
|
|
HIWORD(lParam) );
|
|
globals->penDown = XP_FALSE;
|
|
}
|
|
break;
|
|
|
|
#ifdef OVERRIDE_BACKKEY
|
|
/* Make the back key mean raise focus, but only if dived.
|
|
Otherwise allow the OS to do what it wants. Which means
|
|
exit? */
|
|
case WM_HOTKEY:
|
|
if ( (VK_TBACK == HIWORD(lParam)) && !!globals->game.board ) {
|
|
draw = board_handleKey( globals->game.board,
|
|
XP_RAISEFOCUS_KEY, &handled );
|
|
if ( !draw && !handled
|
|
/* Hack alert. Winders sends two WM_HOTKEY events per
|
|
press of the key. (lParam isn't well documented for
|
|
this event, but likely they're down and up.)
|
|
Unfiltered, the first raises focus and the second
|
|
exits the app. Bad. So we'll only raise if the
|
|
first was not handled. Note that this may well break
|
|
on devices I haven't tested on, later, whenever. */
|
|
&& (0 == (BACK_KEY_UP_MAYBE & LOWORD(lParam))) ) {
|
|
XP_LOGF( "calling ceSaveAndExit for VK_TBACK" );
|
|
/* I'm actually exiting the app rather than minimize. As
|
|
it stands, minimizing means that even if I relaunch
|
|
the app and quit properly I can't delete the .exe,
|
|
suggesting that the minimized guy isn't getting
|
|
reassociated when I relaunch. Until I fix that
|
|
exiting is best.
|
|
*/
|
|
ceSaveAndExit( globals );
|
|
/* SHNavigateBack() is the right way to handle this, but
|
|
isn't available via cegcc yet. Others have suggested
|
|
this as well as ShowWindow( hWnd, SW_MINIMIZE );
|
|
or SetWindowPos( hWnd, &CWnd::wndBottom, 0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
|
|
*/
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
#ifdef KEYBOARD_NAV
|
|
case WM_KEYDOWN:
|
|
case WM_KEYUP:
|
|
draw = ceCheckHandleFocusKey( globals, wParam, lParam,
|
|
message==WM_KEYDOWN, &handled );
|
|
break;
|
|
#endif
|
|
case WM_CHAR:
|
|
if ( wParam == 0x08 ) {
|
|
wParam = XP_CURSOR_KEY_DEL;
|
|
#ifdef KEYBOARD_NAV
|
|
} else if ( wParam == ' ' ) {
|
|
wParam = XP_RAISEFOCUS_KEY;
|
|
#endif
|
|
}
|
|
draw = board_handleKey( globals->game.board, wParam, &handled )
|
|
|| board_handleKey( globals->game.board, wParam - ('a'-'A'),
|
|
&handled );
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
why = (XWTimerReason)wParam;
|
|
if ( why == TIMER_PENDOWN || why == TIMER_TIMERTICK
|
|
#if defined XWFEATURE_RELAY || defined COMMS_HEARTBEAT
|
|
|| why == TIMER_COMMS
|
|
#endif
|
|
) {
|
|
XP_ASSERT( why < NUM_TIMERS_PLUS_ONE );
|
|
|
|
/* Kill since they otherwise repeat, but kill before firing
|
|
as fired proc may set another. */
|
|
(void)KillTimer( hWnd, globals->timerIDs[why] );
|
|
|
|
draw = ceFireTimer( globals, why );
|
|
}
|
|
break;
|
|
|
|
#ifdef _WIN32_WCE
|
|
/* case WM_SETFOCUS: */
|
|
/* hC = ImmGetContext( hWnd ); */
|
|
/* globals->imeWasOpen = ImmGetOpenStatus( hC ); */
|
|
/* ImmSetOpenStatus( hC, TRUE ); */
|
|
/* ImmEscape( NULL, hC, IME_ESC_SET_MODE, (LPVOID)IM_SPELL ); */
|
|
/* break; */
|
|
/* case WM_KILLFOCUS: */
|
|
/* ImmSetOpenStatus( hC, globals->imeWasOpen ); */
|
|
/* break; */
|
|
|
|
/* The code above this point works to turn 12-key->text
|
|
translation on, but not to turn it off, so other apps wind up
|
|
with it on after Crosswords quits. The recommended code is
|
|
below, but includes constants not in the version of cegcc I'm
|
|
using. Need to look into upgrading, but that requires a lot
|
|
of changes. Post B2.... */
|
|
|
|
/* DWORD dwRes = SendMessage((HWND)wParam, WM_IME_REQUEST, IMR_ISIMEAWARE, 0); */
|
|
/* hC = ImmGetContext( hWnd ); */
|
|
/* if ( (dwRes & IMEAF_AWARE) == IMEAF_AWARE ) { */
|
|
/* ImmEscape( NULL, hC, IME_ESC_RETAIN_MODE_ICON, (LPVOID)TRUE); */
|
|
/* } */
|
|
/* ImmSetOpenStatus( hC, FALSE); */
|
|
/* } */
|
|
/* break; */
|
|
/* case WM_IME_REQUEST: */
|
|
/* if ( wParam == IMR_ISIMEAWARE ) { */
|
|
/* return IMEAF_AWARE; */
|
|
/* } */
|
|
/* break; */
|
|
#endif
|
|
|
|
case WM_DESTROY:
|
|
#ifdef _WIN32_WCE
|
|
CommandBar_Destroy(globals->hwndCB); /* supposedly not needed */
|
|
#endif
|
|
PostQuitMessage(0);
|
|
freeGlobals( globals );
|
|
break;
|
|
|
|
case XWWM_TIME_RQST:
|
|
draw = server_do( globals->game.server );
|
|
break;
|
|
|
|
case XWWM_REM_SEL:
|
|
ceTilesLeft( globals );
|
|
break;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
case XWWM_HOSTNAME_ARRIVED:
|
|
ce_sockwrap_hostname( globals->socketWrap, wParam, lParam );
|
|
break;
|
|
|
|
case XWWM_SOCKET_EVT:
|
|
draw = ce_sockwrap_event( globals->socketWrap, wParam, lParam );
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
callDefault = XP_TRUE;
|
|
}
|
|
}
|
|
|
|
if ( callDefault ) {
|
|
result = DefWindowProc(hWnd, message, wParam, lParam );
|
|
} else if ( draw ) {
|
|
/* This is stupid. We can't just say "draw" because windoze clips
|
|
drawing to the inval rect, and the board isn't set up to tell us
|
|
what its inval rect is. So we inval everything, and then when the
|
|
WM_PAINT message comes we inval the whole board because there's a
|
|
huge inval rect. Dumb. Need to figure out how to have the
|
|
methods in cedraw.c set the clip region to encompass the object
|
|
being drawn -- taking board's word for it -- or the intersection
|
|
of that with the actual clip rgn in the case where some window's
|
|
gone away and revealed a large rect board didn't know about. That
|
|
was the source of some trouble on Palm, and CE's so fast this
|
|
works. But it's stupid. */
|
|
RECT r = { 100, 100, 102, 102 };
|
|
InvalidateRect( globals->hWnd, &r, FALSE /* erase */ );
|
|
}
|
|
|
|
return result;
|
|
} /* WndProc */
|
|
|
|
// Mesage handler for the About box.
|
|
LRESULT CALLBACK
|
|
ceAbout(HWND hDlg, UINT message, WPARAM wParam, LPARAM XP_UNUSED(lParam))
|
|
{
|
|
switch (message) {
|
|
case WM_INITDIALOG:
|
|
return TRUE;
|
|
|
|
case WM_COMMAND:
|
|
if ((LOWORD(wParam) == IDOK) || (LOWORD(wParam) == IDCANCEL)) {
|
|
EndDialog(hDlg, LOWORD(wParam));
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
} /* ceAbout */
|
|
|
|
static XP_Bool
|
|
ceMsgFromStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons, XP_Bool destroy )
|
|
{
|
|
/* It seems we want to use messagebox for everything on smartphone and
|
|
Windows, but not on PPC since it doesn't scroll and doesn't use
|
|
softkeys. Well, on Windows since there's no scrolling limit by
|
|
size */
|
|
XP_Bool saidYes;
|
|
XP_Bool useMB;
|
|
#ifdef _WIN32_WCE
|
|
useMB = IS_SMARTPHONE(globals);
|
|
#else
|
|
useMB = stream_getSize(stream) <= 256; /* arbitrary... */
|
|
#endif
|
|
if ( useMB ) {
|
|
int result = messageBoxStream( globals, stream, title, buttons );
|
|
saidYes = (IDOK == result) | (IDRETRY == result) | (IDYES == result);
|
|
} else {
|
|
StrBoxState state;
|
|
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.title = title;
|
|
state.stream = stream;
|
|
state.isQuery = (buttons & ~MB_ICONMASK) != MB_OK;
|
|
state.dlgHdr.globals = globals;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_STRBOX, globals->hWnd,
|
|
(DLGPROC)StrBox, (long)&state );
|
|
saidYes = state.result == IDOK;
|
|
}
|
|
|
|
if ( destroy ) {
|
|
stream_destroy( stream );
|
|
}
|
|
|
|
return saidYes;
|
|
} /* ceMsgFromStream */
|
|
|
|
static XP_UCHAR*
|
|
ceStreamToStrBuf( MPFORMAL XWStreamCtxt* stream )
|
|
{
|
|
XP_U16 len = stream_getSize( stream );
|
|
XP_UCHAR* buf = XP_MALLOC( mpool, len + 1 );
|
|
stream_getBytes( stream, buf, len );
|
|
buf[len] = '\0';
|
|
|
|
return buf;
|
|
} /* ceStreamToStrBuf */
|
|
|
|
static int
|
|
ceOops( CEAppGlobals* globals, const XP_UCHAR* str )
|
|
{
|
|
XP_Bool isUTF8 = ceCurDictIsUTF8( globals );
|
|
return ceMessageBoxChar( globals, str, isUTF8,
|
|
ceGetResStringL( globals, IDS_FYI_L ),
|
|
MB_OK | MB_ICONHAND );
|
|
}
|
|
|
|
static int
|
|
ceOopsId( CEAppGlobals* globals, XP_U16 strId )
|
|
{
|
|
return ceOops( globals, ceGetResString( globals, strId ) );
|
|
}
|
|
|
|
static int
|
|
messageBoxStream( CEAppGlobals* globals, XWStreamCtxt* stream,
|
|
const wchar_t* title, XP_U16 buttons )
|
|
{
|
|
XP_UCHAR* buf = ceStreamToStrBuf( MPPARM(globals->mpool) stream );
|
|
int result;
|
|
|
|
result = ceMessageBoxChar( globals, buf, ceCurDictIsUTF8(globals),
|
|
title, buttons );
|
|
|
|
XP_FREE( globals->mpool, buf );
|
|
return result;
|
|
} /* messageBoxStream */
|
|
|
|
XP_Bool
|
|
queryBoxChar( CEAppGlobals* globals, HWND hWnd, const XP_UCHAR* msg )
|
|
{
|
|
wchar_t widebuf[128];
|
|
XP_U16 answer;
|
|
|
|
(void)MultiByteToWideChar( CP_UTF8, 0, msg, -1, widebuf, VSIZE(widebuf) );
|
|
|
|
answer = MessageBox( hWnd, widebuf,
|
|
ceGetResStringL( globals, IDS_QUESTION_L ),
|
|
MB_YESNO | MB_ICONQUESTION );
|
|
return answer == IDOK || answer == IDYES;
|
|
} /* queryBoxChar */
|
|
|
|
static XP_Bool
|
|
ceQueryFromStream( CEAppGlobals* globals, XWStreamCtxt* stream )
|
|
{
|
|
return ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_QUESTION_L ),
|
|
MB_OKCANCEL | MB_ICONQUESTION, XP_FALSE );
|
|
} /* ceQueryFromStream */
|
|
|
|
static void
|
|
RECTtoXPR( XP_Rect* dest, const RECT* src )
|
|
{
|
|
dest->top = (short)src->top;
|
|
dest->left = (short)src->left;
|
|
dest->width = (short)(src->right - src->left);
|
|
dest->height = (short)(src->bottom - src->top);
|
|
} /* RECTtoXPR */
|
|
|
|
void
|
|
wince_assert( XP_UCHAR* XP_UNUSED_LOG(s), int XP_UNUSED_LOG(line),
|
|
const char* XP_UNUSED_LOG(fileName),
|
|
const char* XP_UNUSED_LOG(func) )
|
|
{
|
|
XP_WARNF( "ASSERTION FAILED %s: %s in file %s, line %d\n", s,
|
|
func, fileName, line );
|
|
} /* wince_assert */
|
|
|
|
#ifdef ENABLE_LOGGING
|
|
static void
|
|
makeTimeStamp( XP_UCHAR* timeStamp, XP_U16 XP_UNUSED_DBG(size) )
|
|
{
|
|
SYSTEMTIME st;
|
|
DWORD tid;
|
|
|
|
tid = GetCurrentThreadId();
|
|
|
|
GetLocalTime( &st );
|
|
sprintf( timeStamp, "<%lx>%d:%.2d:%.2d ", tid, st.wHour, st.wMinute,
|
|
st.wSecond );
|
|
XP_ASSERT( size > strlen(timeStamp) );
|
|
} /* makeTimeStamp */
|
|
|
|
void
|
|
wince_warnf(const XP_UCHAR* format, ...)
|
|
{
|
|
XP_UCHAR buf[256];
|
|
va_list ap;
|
|
XP_U16 slen;
|
|
|
|
va_start( ap, format );
|
|
vsnprintf( buf, sizeof(buf), format, ap );
|
|
va_end(ap);
|
|
|
|
wince_debugf( "%s", buf );
|
|
|
|
slen = strlen(buf)+1;
|
|
wchar_t widebuf[slen];
|
|
|
|
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, buf, slen,
|
|
widebuf, slen );
|
|
|
|
MessageBox( NULL, widebuf, L"WARNF", MB_OK | MB_ICONHAND );
|
|
} /* wince_warnf */
|
|
|
|
void
|
|
wince_debugf(const XP_UCHAR* format, ...)
|
|
{
|
|
#ifdef XWFEATURE_RELAY
|
|
static HANDLE s_logMutex = NULL;
|
|
#endif
|
|
XP_UCHAR buf[256];
|
|
XP_UCHAR timeStamp[32];
|
|
XP_U16 nBytes;
|
|
XP_U32 nWritten;
|
|
HANDLE fileH;
|
|
va_list ap;
|
|
wchar_t* logFileName;
|
|
|
|
va_start( ap, format );
|
|
vsprintf( buf, format, ap );
|
|
va_end(ap);
|
|
|
|
/* Create logfile if necessary and write to it in ascii. If there are
|
|
multiple threads, protect with mutex. */
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
if ( s_logMutex == NULL ) {
|
|
s_logMutex = CreateMutex( NULL, FALSE, NULL );
|
|
}
|
|
if ( WaitForSingleObject( s_logMutex, 200L ) == WAIT_OBJECT_0 ) {
|
|
#endif
|
|
makeTimeStamp(timeStamp, sizeof(timeStamp));
|
|
|
|
#ifdef _WIN32_WCE
|
|
logFileName = L"\\My Documents\\" LCROSSWORDS_DIR L"\\xwDbgLog.txt";
|
|
#else
|
|
logFileName = L"xwDbgLog.txt";
|
|
#endif
|
|
fileH = CreateFile( logFileName,
|
|
GENERIC_WRITE, 0, NULL,
|
|
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
|
|
|
|
#ifdef _WIN32_WCE_EMULATION
|
|
strcat( buf, "\n" );
|
|
#else
|
|
strcat( buf, XP_CR );
|
|
#endif
|
|
SetFilePointer( fileH, 0, 0, FILE_END );
|
|
#ifdef DEBUG_TS
|
|
nBytes = strlen( timeStamp );
|
|
WriteFile( fileH, timeStamp, nBytes, &nWritten, NULL );
|
|
#endif
|
|
nBytes = strlen( buf );
|
|
WriteFile( fileH, buf, nBytes, &nWritten, NULL );
|
|
CloseHandle( fileH );
|
|
#ifdef XWFEATURE_RELAY
|
|
ReleaseMutex( s_logMutex );
|
|
}
|
|
#endif
|
|
} /* wince_debugf */
|
|
#endif /* ENABLE_LOGGING */
|
|
|
|
XP_U16
|
|
wince_snprintf( XP_UCHAR* buf, XP_U16 len, const XP_UCHAR* format, ... )
|
|
{
|
|
va_list ap;
|
|
|
|
va_start( ap, format );
|
|
|
|
_vsnprintf( buf, len, format, ap );
|
|
|
|
/* FormatMessage( */
|
|
/* FORMAT_MESSAGE_FROM_STRING, */
|
|
/* format, */
|
|
/* 0, */
|
|
/* 0, // Default language */
|
|
/* (LPTSTR)buf, */
|
|
/* len, &ap ); */
|
|
|
|
va_end(ap);
|
|
|
|
return strlen(buf);
|
|
} /* wince_snprintf */
|
|
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
static XP_Bool
|
|
got_data_proc( XP_U8* data, XP_U16 len, void* closure )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
XWStreamCtxt* stream;
|
|
XP_Bool draw = XP_FALSE;
|
|
|
|
stream = make_generic_stream( globals );
|
|
stream_putBytes( stream, data, len );
|
|
|
|
XP_ASSERT( !!globals->game.comms );
|
|
if ( comms_checkIncomingStream( globals->game.comms, stream, NULL ) ) {
|
|
draw = server_receiveMessage( globals->game.server, stream );
|
|
}
|
|
stream_destroy( stream );
|
|
ce_util_requestTime( &globals->util );
|
|
|
|
return draw;
|
|
} /* got_data_proc */
|
|
|
|
static void
|
|
sock_state_change( void* closure, CeConnState oldState, CeConnState newState )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
globals->socketState = newState;
|
|
InvalidateRect( globals->hWnd, &globals->relayStatusR, TRUE /* erase */ );
|
|
|
|
if ( newState < oldState ) {
|
|
comms_transportFailed( globals->game.comms );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef COMMS_HEARTBEAT
|
|
static void
|
|
ce_reset_proc( void* XP_UNUSED(closure) )
|
|
{
|
|
LOG_FUNC();
|
|
}
|
|
#endif
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static void
|
|
ce_relay_status( void* closure, CommsRelayState newState )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
globals->relayState = newState;
|
|
InvalidateRect( globals->hWnd, &globals->relayStatusR, TRUE /* erase */ );
|
|
}
|
|
|
|
static XP_S16
|
|
ce_send_proc( const XP_U8* buf, XP_U16 len, const CommsAddrRec* addrp,
|
|
void* closure )
|
|
{
|
|
XP_S16 nSent = -1;
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
CommsAddrRec addr;
|
|
LOG_FUNC();
|
|
|
|
if ( !addrp ) {
|
|
comms_getAddr( globals->game.comms, &addr );
|
|
addrp = &addr;
|
|
}
|
|
|
|
XP_ASSERT( !!addrp ); /* firing */
|
|
switch( addrp->conType ) {
|
|
#if defined XWFEATURE_RELAY || defined XWFEATURE_BLUETOOTH
|
|
case COMMS_CONN_IP_DIRECT:
|
|
break;
|
|
case COMMS_CONN_RELAY:
|
|
if ( !globals->socketWrap ) {
|
|
globals->socketWrap = ce_sockwrap_new( MPPARM(globals->mpool)
|
|
globals->hWnd,
|
|
got_data_proc,
|
|
sock_state_change, globals );
|
|
}
|
|
|
|
nSent = ce_sockwrap_send( globals->socketWrap, buf, len, addrp );
|
|
break;
|
|
#endif
|
|
#ifdef XWFEATURE_SMS
|
|
case COMMS_CONN_SMS:
|
|
nSent = ce_sms_send( globals, buf, len, addrp );
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_LOGF( "unexpected conType %d", addrp->conType );
|
|
XP_ASSERT( 0 );
|
|
}
|
|
|
|
return nSent;
|
|
} /* ce_send_proc */
|
|
|
|
static void
|
|
ce_send_on_close( XWStreamCtxt* stream, void* closure )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)closure;
|
|
|
|
XP_ASSERT( !!globals->game.comms );
|
|
comms_send( globals->game.comms, stream );
|
|
}
|
|
#endif
|
|
|
|
static VTableMgr*
|
|
ce_util_getVTManager( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
return globals->vtMgr;
|
|
} /* ce_util_getVTManager */
|
|
|
|
static void
|
|
ce_util_userError( XW_UtilCtxt* uc, UtilErrID id )
|
|
{
|
|
XP_U16 resID = 0;
|
|
|
|
switch( id ) {
|
|
case ERR_TILES_NOT_IN_LINE:
|
|
resID = IDS_TILES_NOT_IN_LINE;
|
|
break;
|
|
case ERR_NO_EMPTIES_IN_TURN:
|
|
resID = IDS_NO_EMPTIES_IN_TURN;
|
|
break;
|
|
|
|
case ERR_TWO_TILES_FIRST_MOVE:
|
|
resID = IDS_TWO_TILES_FIRST_MOVE;
|
|
break;
|
|
case ERR_TILES_MUST_CONTACT:
|
|
resID = IDS_TILES_MUST_CONTACT;
|
|
break;
|
|
case ERR_NOT_YOUR_TURN:
|
|
resID = IDS_NOT_YOUR_TURN;
|
|
break;
|
|
case ERR_NO_PEEK_ROBOT_TILES:
|
|
resID = IDS_NO_PEEK_ROBOT_TILES;
|
|
break;
|
|
case ERR_CANT_TRADE_MID_MOVE:
|
|
resID = IDS_CANT_TRADE_MID_MOVE;
|
|
break;
|
|
case ERR_TOO_FEW_TILES_LEFT_TO_TRADE:
|
|
resID = IDS_TOO_FEW_TILES_LEFT_TO_TRADE;
|
|
break;
|
|
case ERR_CANT_UNDO_TILEASSIGN:
|
|
resID = IDS_CANT_UNDO_TILEASSIGN;
|
|
break;
|
|
|
|
case ERR_CANT_HINT_WHILE_DISABLED:
|
|
resID = IDS_CANT_HINT_WHILE_DISABLED;
|
|
break;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
case ERR_NO_PEEK_REMOTE_TILES:
|
|
resID = IDS_NO_PEEK_REMOTE_TILES;
|
|
break;
|
|
case ERR_REG_UNEXPECTED_USER:
|
|
resID = IDS_REG_UNEXPECTED_USER;
|
|
break;
|
|
case ERR_SERVER_DICT_WINS:
|
|
resID = IDS_SERVER_DICT_WINS;
|
|
break;
|
|
case ERR_REG_SERVER_SANS_REMOTE:
|
|
resID = IDS_REG_SERVER_SANS_REMOTE;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_TIMEOUT:
|
|
resID = IDS_XWRELAY_ERROR_TIMEOUT;
|
|
break;
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_YOU:
|
|
resID = IDS_ERROR_HEART_YOU;
|
|
break;
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_HEART_OTHER:
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_LOST_OTHER:
|
|
case ERR_RELAY_BASE + XWRELAY_ERROR_OTHER_DISCON:
|
|
resID = IDS_XWRELAY_ERROR_HEART_OTHER;
|
|
break;
|
|
/* Same string as above for now */
|
|
/* resID = IDS_XWRELAY_ERROR_LOST_OTHER; */
|
|
/* break; */
|
|
#endif
|
|
|
|
default:
|
|
XP_WARNF( "unknown error code: %d", id );
|
|
break;
|
|
}
|
|
|
|
if ( 0 != resID ) {
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
ceOopsId( globals, resID );
|
|
}
|
|
} /* ce_util_userError */
|
|
|
|
static XP_Bool
|
|
ce_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
|
|
{
|
|
const char* query = NULL;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
switch( id ) {
|
|
case QUERY_COMMIT_TURN:
|
|
return ceQueryFromStream( globals, stream );
|
|
|
|
case QUERY_COMMIT_TRADE:
|
|
query = ceGetResString( globals, IDS_QUERY_TRADE );
|
|
assertOnTop( globals->hWnd );
|
|
return queryBoxChar( globals, globals->hWnd, query );
|
|
|
|
case QUERY_ROBOT_MOVE:
|
|
return ceMsgFromStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_FYI_L),
|
|
MB_OK | MB_ICONINFORMATION, XP_FALSE );
|
|
|
|
case QUERY_ROBOT_TRADE:
|
|
messageBoxStream( globals, stream,
|
|
ceGetResStringL( globals, IDS_FYI_L),
|
|
MB_OK | MB_ICONINFORMATION);
|
|
break;
|
|
|
|
default:
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
return XP_FALSE;
|
|
} /* ce_util_userQuery */
|
|
|
|
static XWBonusType
|
|
ce_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* XP_UNUSED(model),
|
|
XP_U16 col, XP_U16 row )
|
|
{
|
|
XP_U16 index;
|
|
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
if ( !globals->bonusInfo ) {
|
|
HRSRC rsrcH;
|
|
HGLOBAL globH;
|
|
|
|
rsrcH = FindResource( globals->hInst, MAKEINTRESOURCE(ID_BONUS_RES),
|
|
TEXT("BONS") );
|
|
if ( !!rsrcH ) {
|
|
globH = LoadResource( globals->hInst, rsrcH );
|
|
|
|
if ( !!globH ) {
|
|
globals->bonusInfo = (XP_U16*)globH;
|
|
/* We don't want to call DeleteObject here, but should when
|
|
the app closes. Or does Wince free up all memory
|
|
associated with a process when it closes? PENDING(eeh) */
|
|
// DeleteObject( globH );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( col > 7 ) col = 14 - col;
|
|
if ( row > 7 ) row = 14 - row;
|
|
index = (row*8) + col;
|
|
|
|
if ( !globals->bonusInfo || (index >= 8*8) ) {
|
|
XP_ASSERT( 0 );
|
|
return (XWBonusType)BONUS_NONE;
|
|
} else {
|
|
/* This is probably a bit slow. Consider caching the resource in
|
|
memory with one bonus value per byte. */
|
|
XP_U16 value = globals->bonusInfo[index/4];
|
|
value >>= ((3 - (index % 4)) * 4);
|
|
return value & 0x0F;
|
|
}
|
|
} /* ce_util_getSquareBonus */
|
|
|
|
static XP_S16
|
|
ce_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
|
|
XP_U16 playerNum,
|
|
const XP_UCHAR** texts, XP_U16 nTiles )
|
|
{
|
|
BlankDialogState state;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.dlgHdr.globals = globals;
|
|
state.texts = texts;
|
|
state.nTiles = nTiles;
|
|
state.playerNum = playerNum;
|
|
state.pi = pi;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_ASKBLANK, globals->hWnd,
|
|
(DLGPROC)BlankDlg, (long)&state );
|
|
return state.result;
|
|
} /* ce_util_userPickTile */
|
|
|
|
static XP_Bool
|
|
ce_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name,
|
|
XP_UCHAR* buf, XP_U16* len )
|
|
{
|
|
PasswdDialogState state;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_MEMSET( &state, 0, sizeof(state) );
|
|
|
|
state.dlgHdr.globals = globals;
|
|
state.name = name;
|
|
state.buf = buf;
|
|
state.lenp = len;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_ASKPASS, globals->hWnd,
|
|
(DLGPROC)PasswdDlg, (long)&state );
|
|
|
|
return !state.userCancelled;
|
|
} /* ce_util_askPassword */
|
|
|
|
static void
|
|
ce_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState XP_UNUSED(newState),
|
|
XP_U16 nVisibleRows )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_U16 nHiddenRows;
|
|
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
/* If there's a scrollbar, hide/show it. It wants to be
|
|
active/visible only when the tray is NOT hidden */
|
|
|
|
if ( !!globals->scrollHandle ) {
|
|
nHiddenRows = model_numRows( globals->game.model ) - nVisibleRows;
|
|
updateScrollInfo( globals, nHiddenRows );
|
|
}
|
|
#endif
|
|
ceCheckMenus( globals );
|
|
drawInsidePaint( globals, NULL );
|
|
} /* ce_util_trayHiddenChange */
|
|
|
|
static void
|
|
ce_util_yOffsetChange( XW_UtilCtxt* uc, XP_U16 XP_UNUSED(oldOffset),
|
|
XP_U16 newOffset )
|
|
{
|
|
#ifdef CEFEATURE_CANSCROLL
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
if ( !!globals->scrollHandle ) {
|
|
(void)SetScrollPos( globals->scrollHandle, SB_CTL, newOffset, XP_TRUE );
|
|
}
|
|
#endif
|
|
} /* ce_util_yOffsetChange */
|
|
|
|
static void
|
|
ce_util_turnChanged( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
ceSetLeftSoftkey( globals, ID_MOVE_TURNDONE );
|
|
}
|
|
|
|
static void
|
|
ce_util_notifyGameOver( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
drawInsidePaint( globals, NULL );
|
|
ceDisplayFinalScores( globals );
|
|
|
|
ceSetLeftSoftkey( globals, ID_FILE_NEWGAME );
|
|
} /* ce_util_notifyGameOver */
|
|
|
|
static XP_Bool
|
|
ce_util_hiliteCell( XW_UtilCtxt* XP_UNUSED(uc), XP_U16 XP_UNUSED(col),
|
|
XP_U16 XP_UNUSED(row) )
|
|
{
|
|
return XP_TRUE;
|
|
} /* ce_util_hiliteCell */
|
|
|
|
static XP_Bool
|
|
ce_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
return XP_TRUE;
|
|
} /* ce_util_engineProgressCallback */
|
|
|
|
static void
|
|
ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why,
|
|
XP_U16 XP_UNUSED_STANDALONE(when), XWTimerProc proc,
|
|
void* closure )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_U32 timerID;
|
|
XP_U32 howLong;
|
|
|
|
XP_ASSERT( why < NUM_TIMERS_PLUS_ONE );
|
|
globals->timerProcs[why] = proc;
|
|
globals->timerClosures[why] = closure;
|
|
|
|
switch ( why ) {
|
|
case TIMER_PENDOWN:
|
|
howLong = 400;
|
|
break;
|
|
case TIMER_TIMERTICK:
|
|
howLong = 1000; /* 1 second */
|
|
break;
|
|
#if defined XWFEATURE_RELAY || defined COMMS_HEARTBEAT
|
|
case TIMER_COMMS:
|
|
howLong = when * 1000;
|
|
break;
|
|
#endif
|
|
default:
|
|
XP_ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
globals->timerWhens[why] = GetCurrentTime() + howLong;
|
|
|
|
timerID = SetTimer( globals->hWnd, why, howLong, NULL);
|
|
|
|
globals->timerIDs[why] = timerID;
|
|
} /* ce_util_setTimer */
|
|
|
|
static XP_Bool
|
|
ce_util_altKeyDown( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
return GetKeyState(VK_LSHIFT) < 0
|
|
|| GetKeyState(VK_RSHIFT) < 0;
|
|
}
|
|
|
|
static void
|
|
ce_util_requestTime( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
PostMessage( globals->hWnd, XWWM_TIME_RQST, 0, 0 );
|
|
} /* palm_util_requestTime */
|
|
|
|
static XP_U32
|
|
ce_util_getCurSeconds( XW_UtilCtxt* XP_UNUSED(uc) )
|
|
{
|
|
/* This function is never called! */
|
|
XP_U32 ticks = GetCurrentTime();
|
|
ticks /= 1000;
|
|
LOG_RETURNF( "%ld", ticks );
|
|
return ticks;
|
|
/* return 0L; */
|
|
} /* ce_util_getCurSeconds */
|
|
|
|
static DictionaryCtxt*
|
|
ce_util_makeEmptyDict( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
#ifdef STUBBED_DICT
|
|
return make_stubbed_dict( MPPARM_NOCOMMA(globals->mpool) );
|
|
#else
|
|
return ce_dictionary_make_empty( globals );
|
|
#endif
|
|
} /* ce_util_makeEmptyDict */
|
|
|
|
#ifdef XWFEATURE_RELAY
|
|
static XWStreamCtxt*
|
|
ce_util_makeStreamFromAddr( XW_UtilCtxt* uc, XP_PlayerAddr channelNo )
|
|
{
|
|
XWStreamCtxt* stream;
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
stream = make_generic_stream( globals );
|
|
stream_setOnCloseProc( stream, ce_send_on_close );
|
|
stream_setAddress( stream, channelNo );
|
|
|
|
return stream;
|
|
} /* ce_util_makeStreamFromAddr */
|
|
#endif
|
|
|
|
static const XP_UCHAR*
|
|
ce_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_U16 resID = 0;
|
|
const XP_UCHAR* result = NULL;
|
|
|
|
switch( stringCode ) {
|
|
case STRD_REMAINING_TILES_ADD:
|
|
resID = IDS_REMAINING_TILES_ADD;
|
|
break;
|
|
case STRD_UNUSED_TILES_SUB:
|
|
resID = IDS_UNUSED_TILES_SUB;
|
|
break;
|
|
case STR_BONUS_ALL:
|
|
resID = IDS_BONUS_ALL;
|
|
break;
|
|
case STRD_TURN_SCORE:
|
|
resID = IDS_TURN_SCORE;
|
|
break;
|
|
case STR_COMMIT_CONFIRM:
|
|
resID = IDS_COMMIT_CONFIRM;
|
|
break;
|
|
case STR_LOCAL_NAME:
|
|
resID = IDS_LOCAL_NAME;
|
|
break;
|
|
case STR_NONLOCAL_NAME:
|
|
resID = IDS_NONLOCAL_NAME;
|
|
break;
|
|
case STRD_TIME_PENALTY_SUB:
|
|
resID = IDS_TIME_PENALTY_SUB;
|
|
break;
|
|
|
|
case STRD_CUMULATIVE_SCORE:
|
|
resID = IDS_CUMULATIVE_SCORE;
|
|
break;
|
|
case STRS_MOVE_ACROSS:
|
|
resID = IDS_MOVE_ACROSS;
|
|
break;
|
|
case STRS_MOVE_DOWN:
|
|
resID = IDS_MOVE_DOWN;
|
|
break;
|
|
case STRS_TRAY_AT_START:
|
|
resID = IDS_TRAY_AT_START;
|
|
break;
|
|
|
|
case STRS_NEW_TILES:
|
|
resID = IDS_NEW_TILES;
|
|
break;
|
|
case STRSS_TRADED_FOR:
|
|
resID = IDS_TRADED_FOR;
|
|
break;
|
|
case STR_PASS:
|
|
resID = IDS_PASS;
|
|
break;
|
|
case STR_PHONY_REJECTED:
|
|
resID = IDS_PHONY_REJECTED;
|
|
break;
|
|
|
|
case STRD_ROBOT_TRADED:
|
|
resID = IDS_ROBOT_TRADED;
|
|
break;
|
|
case STR_ROBOT_MOVED:
|
|
resID = IDS_ROBOT_MOVED;
|
|
break;
|
|
case STR_REMOTE_MOVED:
|
|
resID = IDS_REMOTE_MOVED;
|
|
break;
|
|
|
|
case STR_PASSED:
|
|
resID = IDS_PASSED;
|
|
break;
|
|
case STRSD_SUMMARYSCORED:
|
|
resID = IDS_SUMMARYSCORED;
|
|
break;
|
|
case STRD_TRADED:
|
|
resID = IDS_TRADED;
|
|
break;
|
|
case STR_LOSTTURN:
|
|
resID = IDS_LOSTTURN;
|
|
break;
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
case STR_LOCALPLAYERS:
|
|
resID = IDS_LOCALPLAYERS;
|
|
break;
|
|
#endif
|
|
case STR_TOTALPLAYERS:
|
|
resID = IDS_TOTALPLAYERS;
|
|
break;
|
|
|
|
case STRS_VALUES_HEADER:
|
|
resID = IDS_VALUES_HEADER;
|
|
break;
|
|
|
|
default:
|
|
XP_WARNF( "id for stringCode %d not found", stringCode );
|
|
break;
|
|
}
|
|
|
|
if ( 0 != resID ) {
|
|
result = ceGetResString( globals, resID );
|
|
} else {
|
|
XP_WARNF( "%s: no resource for id %d", __func__, stringCode );
|
|
result = "";
|
|
}
|
|
return result;
|
|
} /* ce_util_getUserString */
|
|
|
|
static void
|
|
ce_formatBadWords( BadWordInfo* bwi, XP_UCHAR buf[], XP_U16 bufsiz )
|
|
{
|
|
XP_U16 i;
|
|
|
|
for ( i = 0, buf[0] = '\0'; ; ) {
|
|
XP_UCHAR wordBuf[24];
|
|
sprintf( wordBuf, "\"%s\"", bwi->words[i] );
|
|
XP_ASSERT( strlen(wordBuf) < sizeof(wordBuf)-1 );
|
|
strncat( buf, wordBuf, bufsiz - 1 );
|
|
if ( ++i == bwi->nWords ) {
|
|
break;
|
|
}
|
|
bufsiz -= strlen( wordBuf );
|
|
strncat( buf, ", ", bufsiz - 1 );
|
|
bufsiz -= 2;
|
|
}
|
|
} /* ce_formatBadWords */
|
|
|
|
static XP_Bool
|
|
ce_util_warnIllegalWord( XW_UtilCtxt* uc, BadWordInfo* bwi,
|
|
XP_U16 XP_UNUSED(turn), XP_Bool turnLost )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
XP_UCHAR wordsBuf[256];
|
|
XP_UCHAR msgBuf[256];
|
|
const XP_UCHAR* fmt;
|
|
XP_Bool isOk;
|
|
|
|
ce_formatBadWords( bwi, wordsBuf, sizeof(wordsBuf) );
|
|
fmt = ceGetResString( globals, IDS_WRDNOTFOUND );
|
|
snprintf( msgBuf, VSIZE(msgBuf), fmt, wordsBuf );
|
|
|
|
if ( turnLost ) {
|
|
XP_Bool isUTF8 = ceCurDictIsUTF8( globals );
|
|
ceMessageBoxChar( globals, msgBuf, isUTF8,
|
|
ceGetResStringL( globals, IDS_ILLEGALWRD_L ),
|
|
MB_OK | MB_ICONHAND );
|
|
isOk = XP_TRUE;
|
|
} else {
|
|
const XP_UCHAR* str = ceGetResString( globals, IDS_USEANYWAY );
|
|
XP_U16 len = strlen( msgBuf );
|
|
XP_SNPRINTF( &msgBuf[len], VSIZE(msgBuf)-len, " %s", str );
|
|
assertOnTop( globals->hWnd );
|
|
isOk = queryBoxChar( globals, globals->hWnd, msgBuf );
|
|
}
|
|
|
|
return isOk;
|
|
} /* ce_util_warnIllegalWord */
|
|
|
|
static void
|
|
ce_util_remSelected( XW_UtilCtxt* uc )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
PostMessage( globals->hWnd, XWWM_REM_SEL, 0, 0 );
|
|
}
|
|
|
|
#ifndef XWFEATURE_STANDALONE_ONLY
|
|
static void
|
|
ce_util_addrChange( XW_UtilCtxt* uc,
|
|
const CommsAddrRec* XP_UNUSED_DBG(oldAddr),
|
|
const CommsAddrRec* newAddr )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
|
|
XP_LOGF( "%s: old: %s -> new: %s", __func__,
|
|
ConnType2Str( oldAddr->conType ),
|
|
ConnType2Str( newAddr->conType ) );
|
|
|
|
/* A lot more needs to be tested for and done here... */
|
|
if ( COMMS_CONN_NONE == newAddr->conType ) {
|
|
if ( !!globals->socketWrap ) {
|
|
ce_sockwrap_delete( globals->socketWrap );
|
|
globals->socketWrap = NULL;
|
|
}
|
|
}
|
|
|
|
XP_LOGF( "ce_util_addrChange called; DO SOMETHING." );
|
|
} /* ce_util_addrChange */
|
|
#endif
|
|
|
|
#ifdef XWFEATURE_SEARCHLIMIT
|
|
static XP_Bool
|
|
ce_util_getTraySearchLimits( XW_UtilCtxt* uc, XP_U16* min, XP_U16* max )
|
|
{
|
|
CEAppGlobals* globals = (CEAppGlobals*)uc->closure;
|
|
HintLimitsState hls;
|
|
|
|
XP_MEMSET( &hls, 0, sizeof(hls) );
|
|
|
|
hls.dlgHdr.globals = globals;
|
|
hls.min = *min;
|
|
hls.max = *max;
|
|
|
|
assertOnTop( globals->hWnd );
|
|
DialogBoxParam( globals->locInst, (LPCTSTR)IDD_ASKHINTLIMTS, globals->hWnd,
|
|
(DLGPROC)HintLimitsDlg, (long)&hls );
|
|
|
|
if ( !hls.cancelled ) {
|
|
*min = hls.min;
|
|
*max = hls.max;
|
|
}
|
|
|
|
return !hls.cancelled;
|
|
} /* ce_util_getTraySearchLimits */
|
|
#endif
|
|
|
|
#ifdef SHOW_PROGRESS
|
|
blah blah -- these are not in use
|
|
static void
|
|
ce_util_engineStarting( XW_UtilCtxt* uc )
|
|
{
|
|
} /* ce_util_engineStarting */
|
|
|
|
static void
|
|
ce_util_engineStopping( XW_UtilCtxt* uc )
|
|
{
|
|
} /* ce_util_engineStopping */
|
|
#endif
|