// [HGM] kibitz: write kibitz line; split window for it if necessary void OutputKibitz (int window, char *text) { static int currentLineEnd[2]; int where = 0; if(!EngineOutputIsUp()) return; if(!opponentKibitzes) { // on first kibitz of game, clear memos DoClearMemo(1); currentLineEnd[1] = 0; if(gameMode == IcsObserving) { DoClearMemo(0); currentLineEnd[0] = 0; } } opponentKibitzes = TRUE; // this causes split window DisplayMode in ICS modes. VerifyDisplayMode(); strncpy(text+strlen(text)-1, "\r\n",sizeof(text+strlen(text)-1)); // to not lose line breaks on copying if(gameMode == IcsObserving) { DoSetWindowText(0, nLabel, gameInfo.white); SetIcon( 0, nColorIcon, nColorWhite); SetIcon( 0, nStateIcon, nClear); } DoSetWindowText(1, nLabel, gameMode == IcsPlayingBlack ? gameInfo.white : gameInfo.black); // opponent name SetIcon( 1, nColorIcon, gameMode == IcsPlayingBlack ? nColorWhite : nColorBlack); SetIcon( 1, nStateIcon, nClear); if(strstr(text, "\\ ") == text) where = currentLineEnd[window-1]; // continuation line //if(appData.debugMode) fprintf(debugFP, "insert '%s' at %d (end = %d,%d)\n", text, where, currentLineEnd[0], currentLineEnd[1]); InsertIntoMemo(window-1, text, where); // [HGM] multivar: always at top currentLineEnd[window-1] = where + strlen(text); }
void OutputChatMessage(int partner, char *text) { int j, n = strlen(text); if(!chatHandle[partner]) return; text[n+1] = 0; text[n] = '\n'; text[n-1] = '\r'; // Needs CR to not lose line breaks on copy-paste InsertIntoMemo(chatHandle[partner], text); if(partner != onTop) for(j=0; j<MAX_CHAT; j++) if(j != partner && chatHandle[j]) Button_SetState(GetDlgItem(chatHandle[j], IDC_Focus1+partner-(j<partner)), TRUE); }
// This seems pure front end LRESULT CALLBACK ChatProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { static SnapData sd; char buf[MSG_SIZ], mess[MSG_SIZ]; int partner = -1, i, x, y; static BOOL filterHasFocus[MAX_CHAT]; WORD wMask; HWND hMemo; for(i=0; i<MAX_CHAT; i++) if(hDlg == chatHandle[i]) { partner = i; break; } switch (message) { case WM_INITDIALOG: Translate(hDlg, DLG_Chat); if(partner<0) { for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] == NULL) { partner = i; break; } chatHandle[partner] = hDlg; snprintf(buf, MSG_SIZ, T_("Chat Window %s"), ics_handle[0] ? ics_handle : first.tidy); SetWindowText(hDlg, buf); } for(i=0; i<MAX_CHAT; i++) if(chatHandle[i]) { if(i == partner) continue; // set our button in other open chats SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), chatPartner[partner]); EnableWindow( GetDlgItem(chatHandle[i], IDC_Focus1+partner-(i<partner)), 1 ); // and buttons for other chats in ours SetDlgItemText(hDlg, IDC_Focus1+i-(i>partner), chatPartner[i]); } else EnableWindow( GetDlgItem(hDlg, IDC_Focus1+i-(i>partner)), 0 ); for(i=0; i<MAX_CHAT-1; i++) { Button_SetStyle(GetDlgItem(hDlg, IDC_Focus1+i), BS_PUSHBUTTON|BS_LEFT, TRUE); } x = wpConsole.x; y = wpConsole.y; EnsureOnScreen(&x, &y, 0, 0); SetWindowPos(hDlg, NULL, x, y, 0, 0, SWP_NOZORDER|SWP_NOSIZE); SendMessage( GetDlgItem(hDlg, IDC_ChatPartner), // [HGM] clickbox: initialize with requested handle WM_SETTEXT, 0, (LPARAM) chatPartner[partner] ); filterHasFocus[partner] = TRUE; onTop = partner; // a newly opened box becomes top one if(chatPartner[partner][0]) { filterHasFocus[partner] = FALSE; SetFocus( GetDlgItem(hDlg, OPT_ChatInput) ); } hMemo = GetDlgItem(hDlg, IDC_ChatMemo); wMask = (WORD) SendMessage(hMemo, EM_GETEVENTMASK, 0, 0L); SendMessage(hMemo, EM_SETEVENTMASK, 0, wMask | ENM_LINK); SendMessage(hMemo, EM_AUTOURLDETECT, TRUE, 0L); chatInputWindowProc = (WNDPROC) // cloned from ConsoleWndProc(). Assume they all share same proc. SetWindowLongPtr(GetDlgItem(hDlg, OPT_ChatInput), GWLP_WNDPROC, (LONG_PTR) InterceptArrowKeys); return FALSE; case WM_NOTIFY: if (((NMHDR*)lParam)->code == EN_LINK) { ENLINK *pLink = (ENLINK*)lParam; if (pLink->msg == WM_LBUTTONUP) { TEXTRANGE tr; tr.chrg = pLink->chrg; tr.lpstrText = malloc(1+tr.chrg.cpMax-tr.chrg.cpMin); SendMessage( GetDlgItem(hDlg, IDC_ChatMemo), EM_GETTEXTRANGE, 0, (LPARAM)&tr); ShellExecute(NULL, "open", tr.lpstrText, NULL, NULL, SW_SHOW); free(tr.lpstrText); } } break; case WM_COMMAND: /* [AS] If <Enter> is pressed while editing the filter, it's better to apply the filter rather than selecting the current game. */ if( LOWORD(wParam) == IDC_ChatPartner ) { switch( HIWORD(wParam) ) { case EN_SETFOCUS: filterHasFocus[partner] = TRUE; break; case EN_KILLFOCUS: filterHasFocus[partner] = FALSE; break; } } if( filterHasFocus[partner] && (LOWORD(wParam) == IDC_Send) ) { SetFocus(GetDlgItem(hDlg, OPT_ChatInput)); wParam = IDC_Change; } /* [AS] End command replacement */ switch (LOWORD(wParam)) { case IDCANCEL: /* let Esc key switch focus back to console */ SetFocus(GetDlgItem(hwndConsole, OPT_ConsoleInput)); break; case IDC_Clear: SendMessage( GetDlgItem(hDlg, IDC_ChatMemo), WM_SETTEXT, 0, (LPARAM) "" ); break; case IDC_Change: GetDlgItemText(hDlg, IDC_ChatPartner, chatPartner[partner], MSG_SIZ); for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) { // set our button in other open chats SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), chatPartner[partner]); } break; case IDC_Send: GetDlgItemText(hDlg, OPT_ChatInput, mess, MSG_SIZ); SetDlgItemText(hDlg, OPT_ChatInput, ""); // from here on it could be back-end SaveInHistory(mess); if(!strcmp("whispers", chatPartner[partner])) snprintf(buf, MSG_SIZ, "whisper %s\n", mess); // WHISPER box uses "whisper" to send else if(!strcmp("shouts", chatPartner[partner])) snprintf(buf, MSG_SIZ, "shout %s\n", mess); // SHOUT box uses "shout" to send else { if(!atoi(chatPartner[partner])) { snprintf(buf, MSG_SIZ, "> %s\r\n", mess); // echo only tells to handle, not channel InsertIntoMemo(hDlg, buf); snprintf(buf, MSG_SIZ, "xtell %s %s\n", chatPartner[partner], mess); } else snprintf(buf, MSG_SIZ, "tell %s %s\n", chatPartner[partner], mess); } SendToICS(buf); break; case IDC_Focus1: case IDC_Focus2: case IDC_Focus3: case IDC_Focus4: i = LOWORD(wParam) - IDC_Focus1; if(i >= partner) i++; onTop = i; SetFocus(GetDlgItem(hDlg, IDC_Send)); if(chatHandle[i]) { int j; for(j=0; j<MAX_CHAT; j++) if(i != j && chatHandle[j]) Button_SetState(GetDlgItem(chatHandle[j], IDC_Focus1+i-(j<i)), FALSE); SetFocus(GetDlgItem(chatHandle[i], OPT_ChatInput)); } break; default: break; } break; case WM_CLOSE: chatHandle[partner] = 0; chatPartner[partner][0] = 0; ChatPopDown(); for(i=0; i<MAX_CHAT; i++) if(chatHandle[i] && i != partner) { // set our button in other open chats SetDlgItemText(chatHandle[i], IDC_Focus1+partner-(i<partner), ""); EnableWindow( GetDlgItem(chatHandle[i], IDC_Focus1+partner-(i<partner)), 0 ); } EndDialog(hDlg, TRUE); break; case WM_SIZE: ResizeWindowControls( hDlg ); break; case WM_ENTERSIZEMOVE: return OnEnterSizeMove( &sd, hDlg, wParam, lParam ); case WM_SIZING: return OnSizing( &sd, hDlg, wParam, lParam ); case WM_MOVING: return OnMoving( &sd, hDlg, wParam, lParam ); case WM_EXITSIZEMOVE: return OnExitSizeMove( &sd, hDlg, wParam, lParam ); } return FALSE; }
// pure back end, now SetWindowText is called via wrapper DoSetWindowText static void UpdateControls (EngineOutputData *ed) { // int isPondering = FALSE; char s_label[MAX_NAME_LENGTH + 32]; int h; char * name = ed->name; /* Label */ if( name == 0 || *name == '\0' ) { name = "?"; } strncpy( s_label, name, MAX_NAME_LENGTH ); s_label[ MAX_NAME_LENGTH-1 ] = '\0'; #ifdef SHOW_PONDERING if( IsEnginePondering( ed->which ) ) { char buf[8]; buf[0] = '\0'; if( ed->hint != 0 && *ed->hint != '\0' ) { strncpy( buf, ed->hint, sizeof(buf) ); buf[sizeof(buf)-1] = '\0'; } else if( ed->pv != 0 && *ed->pv != '\0' ) { char * sep = strchr( ed->pv, ' ' ); int buflen = sizeof(buf); if( sep != NULL ) { buflen = sep - ed->pv + 1; if( buflen > sizeof(buf) ) buflen = sizeof(buf); } strncpy( buf, ed->pv, buflen ); buf[ buflen-1 ] = '\0'; } SetEngineState( ed->which, STATE_PONDERING, buf ); } else if( gameMode == TwoMachinesPlay ) { SetEngineState( ed->which, STATE_THINKING, "" ); } else if( gameMode == AnalyzeMode || gameMode == AnalyzeFile || (gameMode == IcsObserving && appData.icsEngineAnalyze)) { // [HGM] ICS-analyze char buf[64]; int time_secs = ed->time / 100; int time_mins = time_secs / 60; buf[0] = '\0'; if( ed->an_move_index != 0 && ed->an_move_count != 0 && *ed->hint != '\0' ) { char mov[16]; strncpy( mov, ed->hint, sizeof(mov) ); mov[ sizeof(mov)-1 ] = '\0'; snprintf( buf, sizeof(buf)/sizeof(buf[0]), "[%d] %d/%d: %s [%02d:%02d:%02d]", ed->depth, ed->an_move_index, ed->an_move_count, mov, time_mins / 60, time_mins % 60, time_secs % 60 ); } SetEngineState( ed->which, STATE_ANALYZING, buf ); } else { SetEngineState( ed->which, STATE_IDLE, "" ); } #endif DoSetWindowText( ed->which, nLabel, s_label ); s_label[0] = '\0'; if( ed->time > 0 && ed->nodes > 0 ) { unsigned long nps_100 = ed->nodes / ed->time; if( nps_100 < 100000 ) { snprintf( s_label, sizeof(s_label)/sizeof(s_label[0]), "%s: %lu", _("NPS"), nps_100 * 100 ); } else { snprintf( s_label, sizeof(s_label)/sizeof(s_label[0]), "%s: %.1fk", _("NPS"), nps_100 / 10.0 ); } } DoSetWindowText( ed->which, nLabelNPS, s_label ); /* Memo */ if( ed->pv != 0 && *ed->pv != '\0' ) { char s_nodes[24]; char s_score[16]; char s_time[24]; char buf[256]; int buflen; int time_secs = ed->time / 100; int time_cent = ed->time % 100; /* Nodes */ if( ed->nodes < 1000000 ) { snprintf( s_nodes, sizeof(s_nodes)/sizeof(s_nodes[0]), u64Display, ed->nodes ); } else { snprintf( s_nodes, sizeof(s_nodes)/sizeof(s_nodes[0]), "%.1fM", u64ToDouble(ed->nodes) / 1000000.0 ); } /* Score */ h = ((gameMode == AnalyzeMode && appData.whitePOV || appData.scoreWhite) && !WhiteOnMove(currentMove) ? -1 : 1) * ed->score; if( h > 0 ) { snprintf( s_score, sizeof(s_score)/sizeof(s_score[0]), "+%.2f", h / 100.0 ); } else { snprintf( s_score, sizeof(s_score)/sizeof(s_score[0]), "%.2f", h / 100.0 ); } /* Time */ snprintf( s_time, sizeof(s_time)/sizeof(s_time[0]), "%d:%02d.%02d", time_secs / 60, time_secs % 60, time_cent ); /* Put all together... */ if(ed->nodes == 0 && ed->score == 0 && ed->time == 0) snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%3d\t", ed->depth ); else snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%3d\t%s\t%s\t%s\t", ed->depth, s_score, s_nodes, s_time ); /* Add PV */ buflen = strlen(buf); strncpy( buf + buflen, ed->pv, sizeof(buf) - buflen ); buf[ sizeof(buf) - 3 ] = '\0'; strcat( buf + buflen, "\r\n" ); /* Update memo */ InsertIntoMemo( ed->which, buf, InsertionPoint(strlen(buf), ed) ); strncpy(lastLine[ed->which], buf, MSG_SIZ); } /* Colors */ SetEngineColorIcon( ed->which ); }
// back end, now the front-end wrapper ClearMemo is used, and ed no longer contains handles. void SetProgramStats (FrontEndProgramStats * stats) // now directly called by back-end { EngineOutputData ed; int clearMemo = FALSE; int which, depth, multi; ChessMove moveType; int ff, ft, rf, rt; char pc; if( stats == 0 ) { SetEngineState( 0, STATE_IDLE, "" ); SetEngineState( 1, STATE_IDLE, "" ); return; } if(gameMode == IcsObserving && !appData.icsEngineAnalyze) return; // [HGM] kibitz: shut up engine if we are observing an ICS game which = stats->which; depth = stats->depth; if( which < 0 || which > 1 || depth < 0 || stats->time < 0 || stats->pv == 0 ) { return; } if( !EngineOutputDialogExists() ) { return; } VerifyDisplayMode(); ed.which = which; ed.depth = depth; ed.nodes = stats->nodes; ed.score = stats->score; ed.time = stats->time; ed.pv = stats->pv; ed.hint = stats->hint; ed.an_move_index = stats->an_move_index; ed.an_move_count = stats->an_move_count; /* Get target control. [HGM] this is moved to front end, which get them from a table */ if( which == 0 ) { ed.name = first.tidy; } else { ed.name = second.tidy; } if( ed.pv != 0 && ed.pv[0] == ' ' ) { if( strncmp( ed.pv, " no PV", 6 ) == 0 ) { /* Hack on hack! :-O */ ed.pv = ""; } } /* Clear memo if needed */ if( lastDepth[which] > depth || (lastDepth[which] == depth && depth <= 1 && ed.pv[0]) ) { // no reason to clear if we won't add line clearMemo = TRUE; } if( lastForwardMostMove[which] != forwardMostMove ) { clearMemo = TRUE; } if( clearMemo ) { DoClearMemo(which); nrVariations[which] = 0; header[which][0] = NULLCHAR; if(gameMode == AnalyzeMode) { ChessProgramState *cps = (which ? &second : &first); if((multi = MultiPV(cps)) >= 0) { snprintf(header[which], MSG_SIZ, "\t%s viewpoint\t\tfewer / Multi-PV setting = %d / more\n", appData.whitePOV || appData.scoreWhite ? "white" : "mover", cps->option[multi].value); } if(!which) snprintf(header[which]+strlen(header[which]), MSG_SIZ-strlen(header[which]), "%s", exclusionHeader); InsertIntoMemo( which, header[which], 0); } else if(appData.ponderNextMove && lastLine[which][0]) { InsertIntoMemo( which, lastLine[which], 0 ); InsertIntoMemo( which, "\n", 0 ); } } if(ed.pv && ed.pv[0] && ParseOneMove(ed.pv, currentMove, &moveType, &ff, &rf, &ft, &rt, &pc)) ed.moveKey = (ff<<24 | rf << 16 | ft << 8 | rt) ^ pc*87161; else ed.moveKey = ed.nodes; // kludge to get unique key unlikely to match any move /* Update */ lastDepth[which] = depth == 1 && ed.nodes == 0 ? 0 : depth; // [HGM] info-line kudge lastForwardMostMove[which] = forwardMostMove; UpdateControls( &ed ); }