//--------------------------------------------------------------------------------------- static void ProcessPGNCmd_Release( tGroupRegistration* pGroup, const tNmea2KMsg *pMsg, tcPgn65280NavicoData* pPgn ) { int result; UNUSED_PARAM( pMsg ); if (pGroup->state == State_IsMaster) { // current master has given up assert( pMsg->sourceAddr == pGroup->master ); result = AppCommand( pGroup, ArbitrationCmd_MasterQuit, pPgn->flags, pPgn->data24 ); if (result == ARBITRATION_OK_ACQUIRE && IsAllowedToAcquire( pGroup )) { DoSend( pGroup, ArbitrationPGNCmd_Acquire ); } else { pGroup->state = State_NoMaster; pGroup->timer = TIMEOUT_NOMASTER_FIRST; } } else if (pGroup->state == State_Master) { // assert my authority DoSend( pGroup, ArbitrationPGNCmd_Master ); } }
//--------------------------------------------------------------------------------------- static void ProcessPGNCmd_Acquire( tGroupRegistration* pGroup, const tNmea2KMsg *pMsg, tcPgn65280NavicoData* pPgn ) { int cmp; int result; eState state; state = pGroup->state; if (state != State_Acquire && state != State_Master) return; if (pGroup->statusFlags & (ArbitrationFlags_Watcher|ArbitrationFlags_NotSuitable)) { assert( 0 ); return; } cmp = ComparePriorities( pGroup, pMsg->sourceAddr, pPgn->flags, pPgn->data24 ); if (cmp < 0) { // they are better than me if (state == State_Master) { // but I'm already master, see if we want to allow them to take over result = AppCommand( pGroup, ArbitrationCmd_MasterChallenge, pPgn->flags, pPgn->data24 ); if (result == ARBITRATION_OK_RELEASE) { // we've had enough, let them take over MasterRelease( pGroup, tTrue ); } else { // assert my authority DoSend( pGroup, ArbitrationPGNCmd_Master ); } } else { // give up trying to be the master pGroup->state = State_Unknown; pGroup->timer = TIMEOUT_ACQUIRE_ABORTED; ClearSend( pGroup ); } } else { assert( cmp > 0 ); //TODO: should never be the same, but what if it is? // assume I'm better than them if (state == State_Master) { // assert my authority DoSend( pGroup, ArbitrationPGNCmd_Master ); } } }
//--------------------------------------------------------------------------------------- static void ProcessPGNCmd_Member( tGroupRegistration* pGroup, const tNmea2KMsg *pMsg, tcPgn65280NavicoData* pPgn ) { int result; UNUSED_PARAM( pMsg ); result = AppCommand( pGroup, ((pPgn->command == ArbitrationPGNCmd_NewMember)? ArbitrationCmd_NewMemberInfo : ArbitrationCmd_MemberInfo), pPgn->flags, pPgn->data24 ); if (pGroup->state == State_Master) { if (result == ARBITRATION_OK_RELEASE) { // we've had enough, let them take over MasterRelease( pGroup, tTrue ); } else { // let them know I'm the boss DoSend( pGroup, ArbitrationPGNCmd_Master ); } } }
//--------------------------------------------------------------------------------------- static int ComparePriorities( tGroupRegistration* pGroup, tNad addr, uint8_t flags, uint32_t groupData ) { tNad myAddr; int result; uint8_t myFlags = pGroup->statusFlags & (ArbitrationFlags_Watcher|ArbitrationFlags_NotSuitable); uint8_t theirFlags = flags & (ArbitrationFlags_Watcher|ArbitrationFlags_NotSuitable); if (myFlags != theirFlags) { return (myFlags < theirFlags)? +3 : -3; } result = AppCommand( pGroup, ArbitrationCmd_CompareGroupData, flags, groupData ); switch (result) { case ArbitrationCmp_Worse: return -2; case ArbitrationCmp_Better: return +2; case ArbitrationCmp_Same: break; default: assert( 0 ); } myAddr = CurrentNmea2KNad( s_Handle, GetMainDeviceIndex(s_Handle) ); return (addr < myAddr)? -1 : +1; }
///////////////////////////////////////////////////////////////////////////// // AppWndProc( hwnd, uiMessage, wParam, lParam ) // // The window proc for the app's main (tiled) window. This processes all // of the parent window's messages. // LONG FAR PASCAL _export AppWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; BOOL f; //is it the registered message? (this proc is called after the creation //of the msgApp). if (msg == msgApp) { if (bShowRendering) { HDC hdc = GetDC(hwndApp); AppPaint(hwndApp, hdc); ReleaseDC(hwndApp, hdc); bShowRendering = (int)wParam; //sent a 0 to turn off bShowRendering. } return 0L; } //well, how about standard Windows messages? switch (msg) { case WM_CREATE: //let WM_SIZE do all the work. break; case WM_ACTIVATEAPP: bAppActive = (BOOL)wParam; wt_reset_input(); // *** Remap the system colors and deal with the palette AppActivate(bAppActive); if (hpalApp) { HDC hdc = GetDC(hwnd); UnrealizeObject(hpalApp); SelectPalette(hdc, hpalApp, FALSE); RealizePalette(hdc); ReleaseDC(hwnd, hdc); } break; case WM_SIZE: wt_reset_input(); nBitmapW = LOWORD(lParam); //new size. nBitmapH = HIWORD(lParam); nBitmapW /= StretchFactor; nBitmapH /= StretchFactor; //Windows DIBs, including WinG bitmaps, are always a multiple of //32-bits wide. For us, using the typical 8-bit per pixel bitmap, //that means we need to ensure the width is a multiple of 4. This //is important because the WT engine treats a bitmap's width and //pitch as one - there is no way for WT to draw into a bitmap //using a width different than the pitch. So we force the bitmap's //width to be a multiple of 4, to be both Windows and WT compatible. //Note we could have patched WT to deal with the concept of a //bitmap pitch, but that's too much change. nBitmapW = ((nBitmapW+3)/4)*4; if(Buffer) { //resizing, minimizing, maximizing. HBITMAP hbm; int Counter; //Create a new 8-bit WinGBitmap with the new size BufferHeader.Header.biWidth = nBitmapW; BufferHeader.Header.biHeight = nBitmapH * Orientation; //probably don't need to do this, but do it anyway. for(Counter = 0;Counter < 256;Counter++) { BufferHeader.aColors[Counter].rgbRed = ColorTable[Counter].rgbRed; BufferHeader.aColors[Counter].rgbGreen = ColorTable[Counter].rgbGreen; BufferHeader.aColors[Counter].rgbBlue = ColorTable[Counter].rgbBlue; BufferHeader.aColors[Counter].rgbReserved = 0; } hbm = WinG.pCreateBitmap(Buffer, (BITMAPINFO *)&BufferHeader, &pBuffer); // Select it in and delete the old one hbm = (HBITMAP)SelectObject(Buffer, hbm); DeleteObject(hbm); PatBlt(Buffer, 0,0,nBitmapW,nBitmapH, BLACKNESS); wt_set_fb_mem(pBuffer); //tell WT about new bitmap address. wt_reinit(nBitmapW,nBitmapH); //and about new bitmap size. wt_render(); //and have WT render a frame. } else //first time. { // Create a new WinGDC and 8-bit WinGBitmap HBITMAP hbm; int Counter; // Get WinG to recommend the fastest DIB format if(WinG.pRecommendDIBFormat((BITMAPINFO *)&BufferHeader)) { // make sure it's 8bpp and remember the orientation BufferHeader.Header.biBitCount = 8; BufferHeader.Header.biCompression = BI_RGB; Orientation = BufferHeader.Header.biHeight; if (Orientation > 0) { DebugMsg("WT requires a top-down bitmap!\nYou are about to hit a brick wall at 90 MPH!"); PostQuitMessage(1); //works but slams palette. bummer. } } else { // set it up ourselves BufferHeader.Header.biSize = sizeof(BITMAPINFOHEADER); BufferHeader.Header.biPlanes = 1; BufferHeader.Header.biBitCount = 8; BufferHeader.Header.biCompression = BI_RGB; BufferHeader.Header.biSizeImage = 0; BufferHeader.Header.biClrUsed = 0; BufferHeader.Header.biClrImportant = 0; } BufferHeader.Header.biWidth = nBitmapW; BufferHeader.Header.biHeight = nBitmapH * Orientation; //#define BAD_PALETTE_CODE #ifdef BAD_PALETTE_CODE //This code sets an incorrect palette, but at least you can still use //regular windows tools. Good for debugging. HDC Screen; RGBQUAD *pColorTable; // create an identity palette from the DIB's color table // get the 20 system colors as PALETTEENTRIES Screen = GetDC(0); GetSystemPaletteEntries(Screen,0,10,LogicalPalette.aEntries); GetSystemPaletteEntries(Screen,246,10, LogicalPalette.aEntries + 246); ReleaseDC(0,Screen); // initialize the logical palette and DIB color table for(Counter = 0;Counter < 10;Counter++) { // copy the system colors into the DIB header // WinG will do this in WinGRecommendDIBFormat, // but it may have failed above so do it here anyway BufferHeader.aColors[Counter].rgbRed = LogicalPalette.aEntries[Counter].peRed; BufferHeader.aColors[Counter].rgbGreen = LogicalPalette.aEntries[Counter].peGreen; BufferHeader.aColors[Counter].rgbBlue = LogicalPalette.aEntries[Counter].peBlue; BufferHeader.aColors[Counter].rgbReserved = 0; LogicalPalette.aEntries[Counter].peFlags = 0; BufferHeader.aColors[Counter + 246].rgbRed = LogicalPalette.aEntries[Counter + 246].peRed; BufferHeader.aColors[Counter + 246].rgbGreen = LogicalPalette.aEntries[Counter + 246].peGreen; BufferHeader.aColors[Counter + 246].rgbBlue = LogicalPalette.aEntries[Counter + 246].peBlue; BufferHeader.aColors[Counter + 246].rgbReserved = 0; LogicalPalette.aEntries[Counter + 246].peFlags = 0; } for (i=0; i<256; i++) { ColorTable[i].rgbRed = 0; ColorTable[i].rgbGreen = 0; ColorTable[i].rgbBlue = 0; } nColors = wt_load_palette(); for (i=0; i<nColors; i++) { int r,g,b; wt_get_palette_entry(i,&r,&g,&b); ColorTable[i].rgbRed = r; ColorTable[i].rgbGreen = g; ColorTable[i].rgbBlue = b; } pColorTable = &ColorTable[0]; for(Counter = 10;Counter < 246;Counter++) { // copy from the original color table to the WinGBitmap's // color table and the logical palette BufferHeader.aColors[Counter].rgbRed = LogicalPalette.aEntries[Counter].peRed = pColorTable[Counter].rgbRed; BufferHeader.aColors[Counter].rgbGreen = LogicalPalette.aEntries[Counter].peGreen = pColorTable[Counter].rgbGreen; BufferHeader.aColors[Counter].rgbBlue = LogicalPalette.aEntries[Counter].peBlue = pColorTable[Counter].rgbBlue; BufferHeader.aColors[Counter].rgbReserved = 0; LogicalPalette.aEntries[Counter].peFlags = PC_NOCOLLAPSE; } hpalApp = CreatePalette((LOGPALETTE *)&LogicalPalette); #else //GOOD_PALETTE_CODE //Working palette code. Has correct colors. And identity. Same frame //rate as bad palette code. This really hoses Windows colors, so //a GUI debugger's windows are really hard to read. I couldn't read the //Symantec IDDE's windows - so I #ifdef'd the bad palette code in for //debugging. //Anyway, this code works. Need 3 things for identity, as far as I can tell: //1. you have to be writing to your bitmap using a specific palette, //2. this palette has to be made into a Windows palette, and selected. //3. this palette has to be copied into the BitmapInfo header of the WinG // bitmap, before creating it (actually as a parameter to creating it). // ClearSystemPalette(); CreateWTPalette(); for(Counter = 0;Counter < 256;Counter++) { BufferHeader.aColors[Counter].rgbRed = ColorTable[Counter].rgbRed; BufferHeader.aColors[Counter].rgbGreen = ColorTable[Counter].rgbGreen; BufferHeader.aColors[Counter].rgbBlue = ColorTable[Counter].rgbBlue; BufferHeader.aColors[Counter].rgbReserved = 0; } #endif // Create a WinGDC and Bitmap, then select away Buffer = WinG.pCreateDC(); hbm = WinG.pCreateBitmap(Buffer, (BITMAPINFO *)&BufferHeader, &pBuffer); // Store the old hbitmap to select back in before deleting gbmOldMonoBitmap = (HBITMAP)SelectObject(Buffer, hbm); PatBlt(Buffer, 0,0,nBitmapW,nBitmapH, BLACKNESS); wt_set_fb_mem(pBuffer); //hack to get around WT's code. strcpy(szDefaultWorldFileName, szModulePath); strcat(szDefaultWorldFileName, DEFAULT_WORLD_FILEPATH); wt_init(szDefaultWorldFileName, nBitmapW,nBitmapH); AppSetCaption(DEFAULT_WORLD_FILETITLE); wt_render(); } bWTinitialized = TRUE; break; case WM_KEYDOWN: //set WT's keyboard array, then do a WT cycle. switch (wParam) { case VK_UP: kbPressed[kbUpArrow] = 1; break; case VK_DOWN: kbPressed[kbDownArrow] = 1; break; case VK_LEFT: kbPressed[kbLeftArrow] = 1; break; case VK_RIGHT: kbPressed[kbRightArrow] = 1; break; case VK_CONTROL: kbPressed[kbCtrl] = 1; break; case VK_ESCAPE: kbPressed[kbEsc] = 1; //DestroyWindow() here? let's check to ensure that func // will send the proper msgs to close stuff. break; case VK_SPACE: kbPressed[kbSpace] = 1; break; case VK_SHIFT: kbPressed[kbLeftShift] = 1; break; case VK_TAB: kbPressed[kbAlt] = 1; break; } //tried using wt_input/wt_render/InvalidateRect sequence //here, but was sometimes jerky (missed frames, actually), //if there was too much keyboard activity. //I think windows was collapsing queued/pending //WM_PAINT messages, so that the app got one instead of a //sudden stream. Anyhow now I draw immediately, and it works //great. Note that AppIdle() processing is required to //have acceleration/deceleration/monsters/events occur without //keyboard input. I guess a timer could also be used. //So I ended up using this helper routine to repaint. AppDoCycle(); break; case WM_KEYUP: //set WT's keyboard array, then do a WT cycle. switch (wParam) { case VK_UP: kbPressed[kbUpArrow] = 0; break; case VK_DOWN: kbPressed[kbDownArrow] = 0; break; case VK_LEFT: kbPressed[kbLeftArrow] = 0; break; case VK_RIGHT: kbPressed[kbRightArrow] = 0; break; case VK_CONTROL: kbPressed[kbCtrl] = 0; break; case VK_ESCAPE: kbPressed[kbEsc] = 0; break; case VK_SPACE: kbPressed[kbSpace] = 0; break; case VK_SHIFT: kbPressed[kbLeftShift] = 0; break; case VK_TAB: kbPressed[kbAlt] = 0; break; } AppDoCycle(); break; case WM_LBUTTONDOWN: break; case WM_RBUTTONDOWN: break; case WM_MOUSEMOVE: break; case WM_COMMAND: return AppCommand(hwnd,msg,wParam,lParam); case WM_DESTROY: PostQuitMessage(0); break; case WM_CLOSE: break; case WM_PALETTECHANGED: if ((HWND)wParam == hwnd) break; // fall through to WM_QUERYNEWPALETTE case WM_QUERYNEWPALETTE: hdc = GetDC(hwnd); if (hpalApp) SelectPalette(hdc, hpalApp, FALSE); f = RealizePalette(hdc); ReleaseDC(hwnd,hdc); if (f) InvalidateRect(hwnd,NULL,TRUE); return f; case WM_PAINT: hdc = BeginPaint(hwnd,&ps); SelectPalette(hdc, hpalApp, FALSE); RealizePalette(hdc); AppPaint (hwnd,hdc); EndPaint(hwnd,&ps); return 0L; } return DefWindowProc(hwnd,msg,wParam,lParam); }
//--------------------------------------------------------------------------------------- static void ProcessPGNCmd_Master( tGroupRegistration* pGroup, const tNmea2KMsg *pMsg, tcPgn65280NavicoData* pPgn ) { int cmp; int result; if (pGroup->state == State_Master) { // bad news, more than one device thinks its the master assert( (pGroup->statusFlags & ArbitrationFlags_Watcher) == 0 ); ClearSend( pGroup ); cmp = ComparePriorities( pGroup, pMsg->sourceAddr, pPgn->flags, pPgn->data24 ); if (cmp < 0) { // they're better than me, let them retain master status result = AppCommandLocal( pGroup, ArbitrationCmd_MasterRelease, 0 ); assert( result >= 0 ); //SendCommand( pGroup, ArbitrationPGNCmd_Defer, 0 ); pGroup->state = State_IsMaster; pGroup->timer = TIMEOUT_MASTER_RELEASED; pGroup->master = pMsg->sourceAddr; result = AppCommand( pGroup, ArbitrationCmd_MasterKnown, pPgn->flags, pPgn->data24 ); assert( result >= 0 ); } else { assert( cmp > 0 ); //TODO: should never be the same, but what if it is? // I'm better than them, reassert my authority DoSend( pGroup, ArbitrationPGNCmd_Master ); } } else { if (pGroup->options & ArbitrationOpt_AutoChallenge) { cmp = ComparePriorities( pGroup, pMsg->sourceAddr, pPgn->flags, pPgn->data24 ); if (cmp > 1) { result = AppCommand( pGroup, ArbitrationCmd_MasterWeak, pPgn->flags, pPgn->data24 ); if (result == ARBITRATION_OK_ACQUIRE && IsAllowedToAcquire( pGroup )) { DoSend( pGroup, ArbitrationPGNCmd_Acquire ); return; } } } if (pGroup->state != State_IsMaster) { pGroup->state = State_IsMaster; pGroup->master = pMsg->sourceAddr; result = AppCommand( pGroup, ArbitrationCmd_MasterKnown, pPgn->flags, pPgn->data24 ); assert( result >= 0 ); } else if (pGroup->master != pMsg->sourceAddr) { pGroup->master = pMsg->sourceAddr; result = AppCommand( pGroup, ArbitrationCmd_MasterChanged, pPgn->flags, pPgn->data24 ); assert( result >= 0 ); } pGroup->timer = TIMEOUT_MASTER_CHECK; } }