From 24659044481fa42b3846cb9e801fd06abe0a102e Mon Sep 17 00:00:00 2001 From: H.G.Muller Date: Sun, 27 Nov 2016 14:47:03 +0100 Subject: [PATCH] Implement saving of (modified) engine settings The Engine #N Settings dialogs will now have an extra button at the top, labelled "Make Persistent". Pressing it will 'OK' the dialog, and in addition will add all non-default settings of the options to the engine line in the Engine List (-firstChessProgramNames), as a -firstOptions XBoard option. If such an option is already in the engine line, its value will be altered to the newly specified settings. The function ResendOptions was modified to also allow returning the options as a string, rather than sending them as 'option' commands to the engine. Currently this will only work for engines that were loaded through the Load Engine dialogs, not for those specified with -fcp/scp at startup. Because the latter need not be in the engine list. The Load Engine dialogs will make XBoard remember the entire engine line of the engine that was last loaded, and pressing the button will search a line exactly like it in the engine list, to add/change its -firstOptions option. It would fail silently if no such line is found. --- backend.c | 59 ++++++++++++++++++++++++++++++++++++++++++------- backend.h | 1 + gtk/xoptions.c | 8 +++++- winboard/wsettings.c | 6 +++++ xaw/xoptions.c | 8 +++++- 5 files changed, 69 insertions(+), 13 deletions(-) diff --git a/backend.c b/backend.c index 2f3db5e..929b30f 100644 --- a/backend.c +++ b/backend.c @@ -981,13 +981,14 @@ FloatToFront(char **list, char *engineLine) ASSIGN(*list, tidy+1); } -char *insert, *wbOptions; // point in ChessProgramNames were we should insert new engine +char *insert, *wbOptions, *currentEngine[2]; // point in ChessProgramNames were we should insert new engine void Load (ChessProgramState *cps, int i) { char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ], buf3[MSG_SIZ], jar; if(engineLine && engineLine[0]) { // an engine was selected from the combo box + ASSIGN(currentEngine[i], engineLine); snprintf(buf, MSG_SIZ, "-fcp %s", engineLine); SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second* ParseArgsFromString(resetOptions); appData.pvSAN[0] = FALSE; @@ -1046,6 +1047,7 @@ Load (ChessProgramState *cps, int i) snprintf(firstChessProgramNames, len, "%s\n%s%s", q, buf, insert); if(q) free(q); FloatToFront(&appData.recentEngineList, buf); + ASSIGN(currentEngine[i], buf); } ReplaceEngine(cps, i); } @@ -10928,12 +10930,14 @@ InitChessProgram (ChessProgramState *cps, int setup) } -void -ResendOptions (ChessProgramState *cps) +char * +ResendOptions (ChessProgramState *cps, int toEngine) { // send the stored value of the options int i; - char buf[MSG_SIZ]; + static char buf2[MSG_SIZ*10]; + char buf[MSG_SIZ], *p = buf2; Option *opt = cps->option; + *p = NULLCHAR; for(i=0; inrOptions; i++, opt++) { *buf = NULLCHAR; switch(opt->type) { @@ -10941,22 +10945,32 @@ ResendOptions (ChessProgramState *cps) case Slider: case CheckBox: if(opt->value != *(int*) (opt->name + MSG_SIZ - 104)) - snprintf(buf, MSG_SIZ, "option %s=%d\n", opt->name, opt->value); + snprintf(buf, MSG_SIZ, "%s=%d", opt->name, opt->value); break; case ComboBox: if(opt->value != *(int*) (opt->name + MSG_SIZ - 104)) - snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->choice[opt->value]); + snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->choice[opt->value]); break; default: if(strcmp(opt->textValue, opt->name + MSG_SIZ - 100)) - snprintf(buf, MSG_SIZ, "option %s=%s\n", opt->name, opt->textValue); + snprintf(buf, MSG_SIZ, "%s=%s", opt->name, opt->textValue); break; case Button: case SaveButton: continue; } - if(*buf) SendToProgram(buf, cps); + if(*buf) { + if(toEngine) { + snprintf(buf2, MSG_SIZ, "option %s\n", buf); + SendToProgram(buf2, cps); + } else { + if(p != buf2) *p++ = ','; + strncpy(p, buf, 10*MSG_SIZ-1 - (p - buf2)); + while(*p) p++; + } + } } + return buf2; } void @@ -11004,7 +11018,7 @@ StartChessProgram (ChessProgramState *cps) cps->comboCnt = 0; // and values of combo boxes } SendToProgram(buf, cps); - if(cps->reload) ResendOptions(cps); + if(cps->reload) ResendOptions(cps, TRUE); } else { SendToProgram("xboard\n", cps); } @@ -11258,6 +11272,32 @@ NamesToList (char *names, char **engineList, char **engineMnemonic, char *group) return i; } +void +SaveEngineSettings (int n) +{ + int len; char *p, *q, *s, buf[MSG_SIZ], *optionSettings; + if(!currentEngine[n] || !currentEngine[n][0]) return; // no engine from list is loaded + p = strstr(firstChessProgramNames, currentEngine[n]); + if(!p) return; // sanity check; engine could be deleted from list after loading + optionSettings = ResendOptions(n ? &second : &first, FALSE); + len = strlen(currentEngine[n]); + q = p + len; *p = 0; // cut list into head and tail piece + s = strstr(currentEngine[n], "firstOptions"); + if(s && (s[-1] == '-' || s[-1] == '/') && (s[12] == ' ' || s[12] == '=') && (s[13] == '"' || s[13] == '\'')) { + char *r = s + 14; + while(*r && *r != s[13]) r++; + s[14] = 0; // cut currentEngine into head and tail part, removing old settings + snprintf(buf, MSG_SIZ, "%s%s%s", currentEngine[n], optionSettings, *r ? r : "\""); // synthesize new engine line + } else if(*optionSettings) { + snprintf(buf, MSG_SIZ, "%s -firstOptions \"%s\"", currentEngine[n], optionSettings); + } + ASSIGN(currentEngine[n], buf); // updated engine line + len = p - firstChessProgramNames + strlen(q) + strlen(currentEngine[n]) + 1; + s = malloc(len); + snprintf(s, len, "%s%s%s", firstChessProgramNames, currentEngine[n], q); + FREE(firstChessProgramNames); firstChessProgramNames = s; // new list +} + // following implemented as macro to avoid type limitations #define SWAP(item, temp) temp = appData.item[0]; appData.item[0] = appData.item[n]; appData.item[n] = temp; @@ -17367,6 +17407,7 @@ ParseFeatures (char *args, ChessProgramState *cps) if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue; if (StringFeature(&p, "option", &q, cps)) { // read to freshly allocated temp buffer first if(cps->reload) { FREE(q); q = NULL; continue; } // we are reloading because of xreuse + if(cps->nrOptions == 0) { ASSIGN(cps->option[0].name, _("Make Persistent -save")); ParseOption(&(cps->option[cps->nrOptions++]), cps); } FREE(cps->option[cps->nrOptions].name); cps->option[cps->nrOptions].name = q; q = NULL; if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature diff --git a/backend.h b/backend.h index fae2ee6..83c61ff 100644 --- a/backend.h +++ b/backend.h @@ -416,6 +416,7 @@ extern char *recentEngines; extern char *comboLine; extern Boolean partnerUp, twoBoards; extern char engineVariant[]; +void SaveEngineSettings P((int n)); char *EngineDefinedVariant P((ChessProgramState *cps, int n)); void SettingsPopUp P((ChessProgramState *cps)); // [HGM] really in front-end, but CPS not known in frontend.h int WaitForEngine P((ChessProgramState *cps, DelayedEventCallback x)); diff --git a/gtk/xoptions.c b/gtk/xoptions.c index fbe1c23..b6b38b6 100644 --- a/gtk/xoptions.c +++ b/gtk/xoptions.c @@ -1097,8 +1097,12 @@ void GenericCallback(GtkWidget *widget, gpointer gdata) if(currentCps) { name = gtk_button_get_label (GTK_BUTTON(widget)); if(currentOption[data].type == SaveButton) GenericReadout(currentOption, -1); - snprintf(buf, MSG_SIZ, "option %s\n", name); - SendToProgram(buf, currentCps); + if(data == 0) { // XBoard save button + SaveEngineSettings(currentCps == &second); PopDown(dlg); + } else { + snprintf(buf, MSG_SIZ, "option %s\n", name); + SendToProgram(buf, currentCps); + } } else ((ButtonCallback*) currentOption[data].target)(data); shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now) diff --git a/winboard/wsettings.c b/winboard/wsettings.c index 893d91b..521ce78 100644 --- a/winboard/wsettings.c +++ b/winboard/wsettings.c @@ -541,6 +541,12 @@ LRESULT CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPa GetOptionValues(hDlg, activeCps, activeList); else if( activeList[j].type != Button) break; else if( !activeCps ) { (*(ButtonCallback*) activeList[j].target)(hDlg); break; } + if(j == 0) { // WinBoard save button + SaveEngineSettings(activeCps == &second); + EndDialog( hDlg, 0 ); + comboCallback = NULL; activeCps = NULL; settingsDlg = NULL; + return TRUE; + } snprintf(buf, MSG_SIZ, "option %s\n", activeList[j].name); SendToProgram(buf, activeCps); } diff --git a/xaw/xoptions.c b/xaw/xoptions.c index 1c0a897..ef82272 100644 --- a/xaw/xoptions.c +++ b/xaw/xoptions.c @@ -839,8 +839,12 @@ GenericCallback (Widget w, XtPointer client_data, XtPointer call_data) XtSetArg(args[0], XtNlabel, &name); XtGetValues(w, args, 1); if(currentOption[data].type == SaveButton) GenericReadout(currentOption, -1); - snprintf(buf, MSG_SIZ, "option %s\n", name); - SendToProgram(buf, currentCps); + if(data == 0) { // XBoard save button + SaveEngineSettings(currentCps == &second); PopDown(dlg); + } else { + snprintf(buf, MSG_SIZ, "option %s\n", name); + SendToProgram(buf, currentCps); + } } else ((ButtonCallback*) currentOption[data].target)(data); shells[dlg] = oldSh; // in case of multiple instances, restore previous (as this one could be popped down now) -- 1.7.0.4