Newer
Older
/*
* config.c - the platform-independent parts of the PuTTY
* configuration box.
*/
#include <assert.h>
#include <stdlib.h>
#include "putty.h"
#include "dialog.h"
#include "storage.h"
#define PRINTER_DISABLED_STRING "None (printing disabled)"
#define HOST_BOX_TITLE "Host Name (or IP address)"
#define PORT_BOX_TITLE "Port"
void conf_radiobutton_handler(union control *ctrl, dlgparam *dlg,
{
int button;
Conf *conf = (Conf *)data;
/*
* For a standard radio button set, the context parameter gives
* the primary key (CONF_foo), and the extra data per button
* gives the value the target field should take if that button
* is the one selected.
*/
if (event == EVENT_REFRESH) {
int val = conf_get_int(conf, ctrl->radio.context.i);
for (button = 0; button < ctrl->radio.nbuttons; button++)
if (val == ctrl->radio.buttondata[button].i)
break;
/* We expected that `break' to happen, in all circumstances. */
assert(button < ctrl->radio.nbuttons);
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
conf_set_int(conf, ctrl->radio.context.i,
ctrl->radio.buttondata[button].i);
void conf_radiobutton_bool_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
int button;
Conf *conf = (Conf *)data;
/*
* Same as conf_radiobutton_handler, but using conf_set_bool in
* place of conf_set_int, because it's dealing with a bool-typed
* config option.
*/
if (event == EVENT_REFRESH) {
int val = conf_get_bool(conf, ctrl->radio.context.i);
for (button = 0; button < ctrl->radio.nbuttons; button++)
if (val == ctrl->radio.buttondata[button].i)
break;
/* We expected that `break' to happen, in all circumstances. */
assert(button < ctrl->radio.nbuttons);
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
conf_set_bool(conf, ctrl->radio.context.i,
ctrl->radio.buttondata[button].i);
}
}
#define CHECKBOX_INVERT (1<<30)
void conf_checkbox_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
/*
* For a standard checkbox, the context parameter gives the
* primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
*/
key = ctrl->checkbox.context.i;
if (key & CHECKBOX_INVERT) {
key &= ~CHECKBOX_INVERT;
invert = true;
/*
* C lacks a logical XOR, so the following code uses the idiom
* (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
* iff exactly one of a and b is nonzero, otherwise 0.)
*/
if (event == EVENT_REFRESH) {
bool val = conf_get_bool(conf, key);
dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
} else if (event == EVENT_VALCHANGE) {
conf_set_bool(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
}
}
void conf_editbox_handler(union control *ctrl, dlgparam *dlg,
{
/*
* The standard edit-box handler expects the main `context'
* field to contain the primary key. The secondary `context2'
* field indicates the type of this field:
*
* - if context2 > 0, the field is a string.
* - if context2 == -1, the field is an int and the edit box
* is numeric.
* - if context2 < -1, the field is an int and the edit box is
* _floating_, and (-context2) gives the scale. (E.g. if
* context2 == -1000, then typing 1.2 into the box will set
* the field to 1200.)
*/
int key = ctrl->editbox.context.i;
int length = ctrl->editbox.context2.i;
Conf *conf = (Conf *)data;
if (length > 0) {
if (event == EVENT_REFRESH) {
char *field = conf_get_str(conf, key);
dlg_editbox_set(ctrl, dlg, field);
} else if (event == EVENT_VALCHANGE) {
char *field = dlg_editbox_get(ctrl, dlg);
conf_set_str(conf, key, field);
sfree(field);
}
} else if (length < 0) {
if (event == EVENT_REFRESH) {
char str[80];
int value = conf_get_int(conf, key);
if (length == -1)
sprintf(str, "%d", value);
else
sprintf(str, "%g", (double)value / (double)(-length));
dlg_editbox_set(ctrl, dlg, str);
} else if (event == EVENT_VALCHANGE) {
char *str = dlg_editbox_get(ctrl, dlg);
if (length == -1)
conf_set_int(conf, key, atoi(str));
else
conf_set_int(conf, key, (int)((-length) * atof(str)));
sfree(str);
}
}
}
void conf_filesel_handler(union control *ctrl, dlgparam *dlg,
{
int key = ctrl->fileselect.context.i;
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
ctrl, dlg, conf_get_filename(conf, key));
} else if (event == EVENT_VALCHANGE) {
Filename *filename = dlg_filesel_get(ctrl, dlg);
conf_set_filename(conf, key, filename);
filename_free(filename);
}
}
void conf_fontsel_handler(union control *ctrl, dlgparam *dlg,
{
int key = ctrl->fontselect.context.i;
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
ctrl, dlg, conf_get_fontspec(conf, key));
} else if (event == EVENT_VALCHANGE) {
FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg);
conf_set_fontspec(conf, key, fontspec);
fontspec_free(fontspec);
}
}
static void config_host_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
/*
* This function works just like the standard edit box handler,
* only it has to choose the control's label and text from two
* different places depending on the protocol.
*/
if (event == EVENT_REFRESH) {
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
/*
* This label text is carefully chosen to contain an n,
* since that's the shortcut for the host name control.
*/
dlg_label_change(ctrl, dlg, "Serial line");
dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
} else {
dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
}
} else if (event == EVENT_VALCHANGE) {
char *s = dlg_editbox_get(ctrl, dlg);
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
conf_set_str(conf, CONF_serline, s);
else
conf_set_str(conf, CONF_host, s);
sfree(s);
}
}
static void config_port_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
char buf[80];
/*
* This function works similarly to the standard edit box handler,
* only it has to choose the control's label and text from two
* different places depending on the protocol.
*/
if (event == EVENT_REFRESH) {
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
/*
* This label text is carefully chosen to contain a p,
* since that's the shortcut for the port control.
*/
dlg_label_change(ctrl, dlg, "Speed");
sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
} else {
dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
if (conf_get_int(conf, CONF_port) != 0)
sprintf(buf, "%d", conf_get_int(conf, CONF_port));
else
/* Display an (invalid) port of 0 as blank */
buf[0] = '\0';
}
dlg_editbox_set(ctrl, dlg, buf);
} else if (event == EVENT_VALCHANGE) {
char *s = dlg_editbox_get(ctrl, dlg);
int i = atoi(s);
sfree(s);
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
conf_set_int(conf, CONF_serspeed, i);
else
conf_set_int(conf, CONF_port, i);
}
}
struct hostport {
union control *host, *port, *protradio, *protlist;
bool mid_refresh;
};
/*
* Shared handler for protocol radio-button and drop-list controls.
* Handles the interaction of those two controls, and also changes
* the setting of the port box to match the protocol if necessary,
* and refreshes both host and port boxes when switching to/from the
* serial backend.
*/
static void config_protocols_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
Conf *conf = (Conf *)data;
int curproto = conf_get_int(conf, CONF_protocol);
struct hostport *hp = (struct hostport *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
* Refresh the states of the controls from Conf.
*
* When refreshing these controls, we have to watch out for
* re-entrancy: because there are two controls involved, the
* refresh is not atomic, so the VALCHANGE and/or SELCHANGE
* callbacks resulting from our updates here might cause other
* settings here to change unwantedly. (E.g. setting the list
* selection shouldn't trigger the SELCHANGE side effect of
* selecting the Other radio button; setting the radio button
* to Other here shouldn't have the side effect of selecting
* whatever protocol is _currently_ selected in the list box,
* if we haven't selected the right one yet.)
*/
hp->mid_refresh = true;
if (ctrl == hp->protradio) {
/* Available buttons were set up when control was created.
* Just select one of them, possibly. */
for (int button = 0; button < ctrl->radio.nbuttons; button++)
/* The final button is "Other:". If we reach that one, the
* current protocol must be in the drop list, so we should
* select the "Other:" button. */
if (curproto == ctrl->radio.buttondata[button].i ||
button == ctrl->radio.nbuttons-1) {
dlg_radiobutton_set(ctrl, dlg, button);
break;
}
} else if (ctrl == hp->protlist) {
int curentry = -1;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT);
for (size_t i = n_ui_backends;
i < PROTOCOL_LIMIT && backends[i]; i++) {
dlg_listbox_addwithid(ctrl, dlg,
backends[i]->displayname,
backends[i]->protocol);
if (backends[i]->protocol == curproto)
curentry = i - n_ui_backends;
}
if (curentry > 0) {
/*
* The currently configured protocol is one of the
* list-box ones, so select it in protlist.
*
* (The corresponding refresh event for protradio
* should have selected the "Other:" radio button, to
* keep things consistent.)
*/
dlg_listbox_select(ctrl, dlg, curentry);
} else {
/*
* If the currently configured protocol is one of the
* radio buttons, we must still ensure *something* is
* selected in the list box. The sensible default is
* the first list element, which be_*.c ought to have
* arranged to be the 'runner-up' in protocol
* popularity out of the ones relegated to the list
* box.
*
* We don't make much effort to retain the state of
* the list box when it doesn't correspond to an
* actual protocol. So it's easy for this case to be
* reached as a side effect of other actions, e.g.
* loading a saved session that has a radio-button
* protocol configured.
*/
dlg_listbox_select(ctrl, dlg, 0);
}
dlg_update_done(ctrl, dlg);
}
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
hp->mid_refresh = false;
} else if (!hp->mid_refresh) {
/*
* Potentially update Conf from the states of the controls.
*/
int newproto = curproto;
if (event == EVENT_VALCHANGE && ctrl == hp->protradio) {
int button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
if (ctrl->radio.buttondata[button].i == -1) {
/*
* The 'Other' radio button was selected, which means we
* have to set CONF_protocol based on the currently
* selected list box entry.
*
* (We conditionalise this on there _being_ a selected
* list box entry. I hope the case where nothing is
* selected can't actually come up except during
* initialisation, and I also hope that hp->mid_session
* will prevent that case from getting here. But as a
* last-ditch fallback, this if statement should at least
* guarantee that we don't pass a nonsense value to
* dlg_listbox_getid.)
*/
int i = dlg_listbox_index(hp->protlist, dlg);
if (i >= 0)
newproto = dlg_listbox_getid(hp->protlist, dlg, i);
} else {
newproto = ctrl->radio.buttondata[button].i;
}
} else if (event == EVENT_SELCHANGE && ctrl == hp->protlist) {
int i = dlg_listbox_index(ctrl, dlg);
if (i >= 0) {
newproto = dlg_listbox_getid(ctrl, dlg, i);
/* Select the "Other" radio button, too */
dlg_radiobutton_set(hp->protradio, dlg,
hp->protradio->radio.nbuttons-1);
}
}
if (newproto != curproto) {
conf_set_int(conf, CONF_protocol, newproto);
const struct BackendVtable *cvt = backend_vt_from_proto(curproto);
const struct BackendVtable *nvt = backend_vt_from_proto(newproto);
/*
* Iff the user hasn't changed the port from the old
* protocol's default, update it with the new protocol's
* default.
*
* (This includes a "default" of 0, implying that there is
* no sensible default for that protocol; in this case
* it's displayed as a blank.)
*
* This helps with the common case of tabbing through the
* controls in order and setting a non-default port before
* getting to the protocol; we want that non-default port
* to be preserved.
*/
int port = conf_get_int(conf, CONF_port);
if (port == cvt->default_port)
conf_set_int(conf, CONF_port, nvt->default_port);
dlg_refresh(hp->host, dlg);
dlg_refresh(hp->port, dlg);
}
}
static void loggingbuttons_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
/* This function works just like the standard radio-button handler,
* but it has to fall back to "no logging" in situations where the
* configured logging type isn't applicable.
*/
if (event == EVENT_REFRESH) {
int logtype = conf_get_int(conf, CONF_logtype);
for (button = 0; button < ctrl->radio.nbuttons; button++)
if (logtype == ctrl->radio.buttondata[button].i)
break;
/* We fell off the end, so we lack the configured logging type */
if (button == ctrl->radio.nbuttons) {
button = 0;
conf_set_int(conf, CONF_logtype, LGTYP_NONE);
}
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
static void numeric_keypad_handler(union control *ctrl, dlgparam *dlg,
{
int button;
Conf *conf = (Conf *)data;
/*
* This function works much like the standard radio button
* handler, but it has to handle two fields in Conf.
*/
if (event == EVENT_REFRESH) {
if (conf_get_bool(conf, CONF_nethack_keypad))
button = 2;
else if (conf_get_bool(conf, CONF_app_keypad))
button = 1;
else
button = 0;
assert(button < ctrl->radio.nbuttons);
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
if (button == 2) {
conf_set_bool(conf, CONF_app_keypad, false);
conf_set_bool(conf, CONF_nethack_keypad, true);
} else {
conf_set_bool(conf, CONF_app_keypad, (button != 0));
conf_set_bool(conf, CONF_nethack_keypad, false);
}
}
}
static void cipherlist_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
static const struct { const char *s; int c; } ciphers[] = {
{ "ChaCha20 (SSH-2 only)", CIPHER_CHACHA20 },
{ "3DES", CIPHER_3DES },
{ "Blowfish", CIPHER_BLOWFISH },
{ "DES", CIPHER_DES },
{ "AES (SSH-2 only)", CIPHER_AES },
{ "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
{ "-- warn below here --", CIPHER_WARN }
};
/* Set up the "selected ciphers" box. */
/* (cipherlist assumed to contain all ciphers) */
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < CIPHER_MAX; i++) {
int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
int j;
const char *cstr = NULL;
for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
if (ciphers[j].c == c) {
cstr = ciphers[j].s;
break;
}
}
dlg_listbox_addwithid(ctrl, dlg, cstr, c);
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
/* Update array to match the list box. */
for (i=0; i < CIPHER_MAX; i++)
conf_set_int_int(conf, CONF_ssh_cipherlist, i,
dlg_listbox_getid(ctrl, dlg, i));
}
}
#ifndef NO_GSSAPI
static void gsslist_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < ngsslibs; i++) {
int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
assert(id >= 0 && id < ngsslibs);
dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
/* Update array to match the list box. */
for (i=0; i < ngsslibs; i++)
conf_set_int_int(conf, CONF_ssh_gsslist, i,
dlg_listbox_getid(ctrl, dlg, i));
}
}
#endif
static void kexlist_handler(union control *ctrl, dlgparam *dlg,

Jacob Nevins
committed
{
Conf *conf = (Conf *)data;

Jacob Nevins
committed
if (event == EVENT_REFRESH) {

Jacob Nevins
committed
static const struct { const char *s; int k; } kexes[] = {
{ "Diffie-Hellman group 1", KEX_DHGROUP1 },
{ "Diffie-Hellman group 14", KEX_DHGROUP14 },
{ "Diffie-Hellman group exchange", KEX_DHGEX },
{ "RSA-based key exchange", KEX_RSA },
{ "-- warn below here --", KEX_WARN }
};

Jacob Nevins
committed
/* Set up the "kex preference" box. */
/* (kexlist assumed to contain all algorithms) */
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < KEX_MAX; i++) {
int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);
int j;
const char *kstr = NULL;
for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
if (kexes[j].k == k) {
kstr = kexes[j].s;
break;
}
}
dlg_listbox_addwithid(ctrl, dlg, kstr, k);
}
dlg_update_done(ctrl, dlg);

Jacob Nevins
committed
} else if (event == EVENT_VALCHANGE) {

Jacob Nevins
committed
/* Update array to match the list box. */
for (i=0; i < KEX_MAX; i++)
conf_set_int_int(conf, CONF_ssh_kexlist, i,
dlg_listbox_getid(ctrl, dlg, i));

Jacob Nevins
committed
}
}
static void hklist_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int i;
static const struct { const char *s; int k; } hks[] = {
{ "Ed25519", HK_ED25519 },
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
{ "ECDSA", HK_ECDSA },
{ "DSA", HK_DSA },
{ "RSA", HK_RSA },
{ "-- warn below here --", HK_WARN }
};
/* Set up the "host key preference" box. */
/* (hklist assumed to contain all algorithms) */
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < HK_MAX; i++) {
int k = conf_get_int_int(conf, CONF_ssh_hklist, i);
int j;
const char *kstr = NULL;
for (j = 0; j < lenof(hks); j++) {
if (hks[j].k == k) {
kstr = hks[j].s;
break;
}
}
dlg_listbox_addwithid(ctrl, dlg, kstr, k);
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
int i;
/* Update array to match the list box. */
for (i=0; i < HK_MAX; i++)
conf_set_int_int(conf, CONF_ssh_hklist, i,
dlg_listbox_getid(ctrl, dlg, i));
}
}
static void printerbox_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int nprinters, i;
printer_enum *pe;
const char *printer;
dlg_update_start(ctrl, dlg);
/*
* Some backends may wish to disable the drop-down list on
* this edit box. Be prepared for this.
*/
if (ctrl->editbox.has_list) {
dlg_listbox_clear(ctrl, dlg);
dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
pe = printer_start_enum(&nprinters);
for (i = 0; i < nprinters; i++)
dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
printer_finish_enum(pe);
}
printer = conf_get_str(conf, CONF_printer);
if (!printer)
printer = PRINTER_DISABLED_STRING;
dlg_editbox_set(ctrl, dlg, printer);
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
char *printer = dlg_editbox_get(ctrl, dlg);
if (!strcmp(printer, PRINTER_DISABLED_STRING))
printer[0] = '\0';
conf_set_str(conf, CONF_printer, printer);
sfree(printer);
}
}
static void codepage_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int i;
const char *cp, *thiscp;
dlg_update_start(ctrl, dlg);
thiscp = cp_name(decode_codepage(conf_get_str(conf,
CONF_line_codepage)));
dlg_listbox_clear(ctrl, dlg);
for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
dlg_listbox_add(ctrl, dlg, cp);
dlg_editbox_set(ctrl, dlg, thiscp);
conf_set_str(conf, CONF_line_codepage, thiscp);
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
char *codepage = dlg_editbox_get(ctrl, dlg);
conf_set_str(conf, CONF_line_codepage,
cp_name(decode_codepage(codepage)));
sfree(codepage);
}
}
static void sshbug_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
/*
* We must fetch the previously configured value from the Conf
* before we start modifying the drop-down list, otherwise the
* spurious SELCHANGE we trigger in the process will overwrite
* the value we wanted to keep.
*/
int oldconf = conf_get_int(conf, ctrl->listbox.context.i);
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
switch (oldconf) {
case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_SELCHANGE) {
int i = dlg_listbox_index(ctrl, dlg);
if (i < 0)
i = AUTO;
else
i = dlg_listbox_getid(ctrl, dlg, i);
conf_set_int(conf, ctrl->listbox.context.i, i);
}
}
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
static void oidc_config_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
oidc_configurations *ocs = get_loaded_oidc_configurations();
if (ocs != NULL) {
char *old_conf = conf_get_str(conf, CONF_oidc_selected_configuration);
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for(int i = 0; i < ocs->n_confs; i++) {
dlg_listbox_addwithid(ctrl, dlg, ocs->configurations[i]->configuration_name, i);
if (strcmp(old_conf, ocs->configurations[i]->configuration_name) == 0) {
dlg_listbox_select(ctrl, dlg, i);
}
}
free_oidc_configurations(ocs);
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_SELCHANGE) {
char *selected_conf = dlg_listbox_get(ctrl, dlg);
conf_set_str(conf, CONF_oidc_selected_configuration, selected_conf);
}
}
static void config_motley_cue_port_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
char buf[80];
if (event == EVENT_REFRESH) {
sprintf(buf, "%d", conf_get_int(conf, CONF_oidc_motley_cue_port));
if (strcmp(buf, "0") != 0) {
dlg_editbox_set(ctrl, dlg, buf);
}
} else if (event == EVENT_VALCHANGE) {
char *s = dlg_editbox_get(ctrl, dlg);
int i = atoi(s);
sfree(s);
conf_set_int(conf, CONF_oidc_motley_cue_port, i);
}
}
struct sessionsaver_data {
union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
union control *okbutton, *cancelbutton;

Jacob Nevins
committed
struct sesslist sesslist;
char *savedsession; /* the current contents of ssd->editbox */
};
static void sessionsaver_data_free(void *ssdv)
{
struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv;
sfree(ssd->savedsession);
sfree(ssd);
}
* Helper function to load the session selected in the list box, if
* any, as this is done in more than one place below. Returns 0 for
* failure.
*/
static bool load_selected_session(
struct sessionsaver_data *ssd,
dlgparam *dlg, Conf *conf, bool *maybe_launch)
{
int i = dlg_listbox_index(ssd->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
return false;

Jacob Nevins
committed
isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
load_settings(ssd->sesslist.sessions[i], conf);
sfree(ssd->savedsession);
ssd->savedsession = dupstr(isdef ? "" : ssd->sesslist.sessions[i]);
if (maybe_launch)
*maybe_launch = !isdef;
dlg_refresh(NULL, dlg);
/* Restore the selection, which might have been clobbered by
* changing the value of the edit box. */
dlg_listbox_select(ssd->listbox, dlg, i);
}
static void sessionsaver_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
struct sessionsaver_data *ssd =
(struct sessionsaver_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == ssd->editbox) {
dlg_editbox_set(ctrl, dlg, ssd->savedsession);
} else if (ctrl == ssd->listbox) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < ssd->sesslist.nsessions; i++)
dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_VALCHANGE) {
int top, bottom, halfway, i;
if (ctrl == ssd->editbox) {
sfree(ssd->savedsession);
ssd->savedsession = dlg_editbox_get(ctrl, dlg);
top = ssd->sesslist.nsessions;
bottom = -1;
while (top-bottom > 1) {
halfway = (top+bottom)/2;
i = strcmp(ssd->savedsession, ssd->sesslist.sessions[halfway]);
if (i <= 0 ) {
top = halfway;
} else {
bottom = halfway;
}
}
if (top == ssd->sesslist.nsessions) {
top -= 1;
}
dlg_listbox_select(ssd->listbox, dlg, top);
}
} else if (event == EVENT_ACTION) {
bool mbl = false;
if (!ssd->midsession &&
(ctrl == ssd->listbox ||
(ssd->loadbutton && ctrl == ssd->loadbutton))) {
/*
* The user has double-clicked a session, or hit Load.
* We must load the selected session, and then
* terminate the configuration dialog _if_ there was a
* double-click on the list box _and_ that session
* contains a hostname.
*/
if (load_selected_session(ssd, dlg, conf, &mbl) &&
(mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
dlg_end(dlg, 1); /* it's all over, and succeeded */
}
} else if (ctrl == ssd->savebutton) {
bool isdef = !strcmp(ssd->savedsession, "Default Settings");
if (!ssd->savedsession[0]) {
int i = dlg_listbox_index(ssd->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
return;
}
isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
sfree(ssd->savedsession);
ssd->savedsession = dupstr(isdef ? "" :
ssd->sesslist.sessions[i]);
char *errmsg = save_settings(ssd->savedsession, conf);
if (errmsg) {
dlg_error_msg(dlg, errmsg);
sfree(errmsg);
}
}
get_sesslist(&ssd->sesslist, false);
get_sesslist(&ssd->sesslist, true);
dlg_refresh(ssd->editbox, dlg);
dlg_refresh(ssd->listbox, dlg);
} else if (!ssd->midsession &&
ssd->delbutton && ctrl == ssd->delbutton) {
int i = dlg_listbox_index(ssd->listbox, dlg);
if (i <= 0) {
dlg_beep(dlg);
} else {
del_settings(ssd->sesslist.sessions[i]);
get_sesslist(&ssd->sesslist, false);
get_sesslist(&ssd->sesslist, true);
dlg_refresh(ssd->listbox, dlg);
}
} else if (ctrl == ssd->okbutton) {

Jacob Nevins
committed
if (ssd->midsession) {
/* In a mid-session Change Settings, Apply is always OK. */
return;
}
/*
* Annoying special case. If the `Open' button is
* pressed while no host name is currently set, _and_
* the session list previously had the focus, _and_
* there was a session selected in that which had a
* valid host name in it, then load it and go.
*/
if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
!conf_launchable(conf) && dlg_is_visible(ssd->listbox, dlg)) {
Conf *conf2 = conf_new();
bool mbl = false;
if (!load_selected_session(ssd, dlg, conf2, &mbl)) {
dlg_beep(dlg);
conf_free(conf2);
return;
}
/* If at this point we have a valid session, go! */
if (mbl && conf_launchable(conf2)) {
conf_copy_into(conf, conf2);
dlg_end(dlg, 1);
} else
dlg_beep(dlg);
conf_free(conf2);
}
/*
* Otherwise, do the normal thing: if we have a valid
* session, get going.
*/
if (conf_launchable(conf)) {
dlg_end(dlg, 1);
} else
dlg_beep(dlg);
} else if (ctrl == ssd->cancelbutton) {
dlg_end(dlg, 0);
}
}
}
struct charclass_data {
union control *listbox, *editbox, *button;
};
static void charclass_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
struct charclass_data *ccd =
(struct charclass_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == ccd->listbox) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < 128; i++) {
char str[100];
sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
(i >= 0x21 && i != 0x7F) ? i : ' ',
conf_get_int_int(conf, CONF_wordness, i));
dlg_listbox_add(ctrl, dlg, str);
}
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_ACTION) {
if (ctrl == ccd->button) {
char *str;
int i, n;
str = dlg_editbox_get(ccd->editbox, dlg);
n = atoi(str);
sfree(str);
for (i = 0; i < 128; i++) {
if (dlg_listbox_issel(ccd->listbox, dlg, i))
conf_set_int_int(conf, CONF_wordness, i, n);
}
dlg_refresh(ccd->listbox, dlg);
}
}
}
struct colour_data {
union control *listbox, *redit, *gedit, *bedit, *button;
};
/* Array of the user-visible colour names defined in the list macro in
* putty.h */
static const char *const colours[] = {
#define CONF_COLOUR_NAME_DECL(id,name) name,
CONF_COLOUR_LIST(CONF_COLOUR_NAME_DECL)
#undef CONF_COLOUR_NAME_DECL
};
static void colour_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
struct colour_data *cd =
(struct colour_data *)ctrl->generic.context.p;
bool update = false, clear = false;
int r, g, b;
if (event == EVENT_REFRESH) {
if (ctrl == cd->listbox) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < lenof(colours); i++)
dlg_listbox_add(ctrl, dlg, colours[i]);
dlg_update_done(ctrl, dlg);
clear = true;
update = true;
}
} else if (event == EVENT_SELCHANGE) {
if (ctrl == cd->listbox) {
/* The user has selected a colour. Update the RGB text. */
int i = dlg_listbox_index(ctrl, dlg);
if (i < 0) {
clear = true;
} else {
clear = false;
r = conf_get_int_int(conf, CONF_colours, i*3+0);
g = conf_get_int_int(conf, CONF_colours, i*3+1);
b = conf_get_int_int(conf, CONF_colours, i*3+2);
}
update = true;
}
} else if (event == EVENT_VALCHANGE) {
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
/* The user has changed the colour using the edit boxes. */
char *str;
int i, cval;
str = dlg_editbox_get(ctrl, dlg);
cval = atoi(str);
sfree(str);
if (cval > 255) cval = 255;
if (cval < 0) cval = 0;
i = dlg_listbox_index(cd->listbox, dlg);
if (i >= 0) {
if (ctrl == cd->redit)
conf_set_int_int(conf, CONF_colours, i*3+0, cval);
else if (ctrl == cd->gedit)
conf_set_int_int(conf, CONF_colours, i*3+1, cval);
else if (ctrl == cd->bedit)
conf_set_int_int(conf, CONF_colours, i*3+2, cval);
}
}
} else if (event == EVENT_ACTION) {
if (ctrl == cd->button) {
int i = dlg_listbox_index(cd->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
return;
}
/*
* Start a colour selector, which will send us an
* EVENT_CALLBACK when it's finished and allow us to
* pick up the results.
*/
dlg_coloursel_start(ctrl, dlg,
conf_get_int_int(conf, CONF_colours, i*3+0),
conf_get_int_int(conf, CONF_colours, i*3+1),
conf_get_int_int(conf, CONF_colours, i*3+2));
}
} else if (event == EVENT_CALLBACK) {
if (ctrl == cd->button) {
int i = dlg_listbox_index(cd->listbox, dlg);
/*
* Collect the results of the colour selector. Will
* return nonzero on success, or zero if the colour
* selector did nothing (user hit Cancel, for example).
*/
if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
conf_set_int_int(conf, CONF_colours, i*3+0, r);
conf_set_int_int(conf, CONF_colours, i*3+1, g);
conf_set_int_int(conf, CONF_colours, i*3+2, b);
clear = false;
update = true;
}
}
}
if (update) {
if (clear) {
dlg_editbox_set(cd->redit, dlg, "");
dlg_editbox_set(cd->gedit, dlg, "");
dlg_editbox_set(cd->bedit, dlg, "");
} else {
char buf[40];
sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
}
}
}
struct ttymodes_data {
union control *valradio, *valbox, *setbutton, *listbox;
};
static void ttymodes_handler(union control *ctrl, dlgparam *dlg,
{
Conf *conf = (Conf *)data;
struct ttymodes_data *td =
(struct ttymodes_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == td->listbox) {
char *key, *val;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
val != NULL;
val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
char *disp = dupprintf("%s\t%s", key,
(val[0] == 'A') ? "(auto)" :
((val[0] == 'N') ? "(don't send)"
: val+1));
dlg_listbox_add(ctrl, dlg, disp);
sfree(disp);
}
dlg_update_done(ctrl, dlg);
} else if (ctrl == td->valradio) {
dlg_radiobutton_set(ctrl, dlg, 0);
}
} else if (event == EVENT_SELCHANGE) {
if (ctrl == td->listbox) {
int ind = dlg_listbox_index(td->listbox, dlg);
char *val;
if (ind < 0) {
return; /* no item selected */
}
val = conf_get_str_str(conf, CONF_ttymodes,
conf_get_str_nthstrkey(conf, CONF_ttymodes,
ind));
assert(val != NULL);
/* Do this first to defuse side-effects on radio buttons: */
dlg_editbox_set(td->valbox, dlg, val+1);
dlg_radiobutton_set(td->valradio, dlg,
val[0] == 'A' ? 0 : (val[0] == 'N' ? 1 : 2));
}
} else if (event == EVENT_VALCHANGE) {
if (ctrl == td->valbox) {
/* If they're editing the text box, we assume they want its
* value to be used. */
dlg_radiobutton_set(td->valradio, dlg, 2);
}
} else if (event == EVENT_ACTION) {
if (ctrl == td->setbutton) {
int ind = dlg_listbox_index(td->listbox, dlg);
const char *key;
char *str, *val;
char type;
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
int button = dlg_radiobutton_get(td->valradio, dlg);
assert(button >= 0 && button < lenof(types));
type = types[button];
}
/* Construct new entry */
if (ind >= 0) {
key = conf_get_str_nthstrkey(conf, CONF_ttymodes, ind);
str = (type == 'V' ? dlg_editbox_get(td->valbox, dlg)
: dupstr(""));
val = dupprintf("%c%s", type, str);
sfree(str);
conf_set_str_str(conf, CONF_ttymodes, key, val);
sfree(val);
dlg_refresh(td->listbox, dlg);
dlg_listbox_select(td->listbox, dlg, ind);
} else {
/* Not a multisel listbox, so this means nothing selected */
dlg_beep(dlg);
}
}
}
}
struct environ_data {
union control *varbox, *valbox, *addbutton, *rembutton, *listbox;
};
static void environ_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
struct environ_data *ed =
(struct environ_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == ed->listbox) {
char *key, *val;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key);
val != NULL;
val = conf_get_str_strs(conf, CONF_environmt, key, &key)) {
char *p = dupprintf("%s\t%s", key, val);
dlg_listbox_add(ctrl, dlg, p);
sfree(p);
}
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_ACTION) {
if (ctrl == ed->addbutton) {
char *key, *val, *str;
key = dlg_editbox_get(ed->varbox, dlg);
if (!*key) {
sfree(key);
dlg_beep(dlg);
return;
}
val = dlg_editbox_get(ed->valbox, dlg);
if (!*val) {
sfree(key);
sfree(val);
dlg_beep(dlg);
return;
}
conf_set_str_str(conf, CONF_environmt, key, val);
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
dlg_editbox_set(ed->varbox, dlg, "");
dlg_editbox_set(ed->valbox, dlg, "");
sfree(str);
sfree(key);
sfree(val);
dlg_refresh(ed->listbox, dlg);
} else if (ctrl == ed->rembutton) {
int i = dlg_listbox_index(ed->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
} else {
char *key, *val;
key = conf_get_str_nthstrkey(conf, CONF_environmt, i);
if (key) {
/* Populate controls with the entry we're about to delete
* for ease of editing */
val = conf_get_str_str(conf, CONF_environmt, key);
dlg_editbox_set(ed->varbox, dlg, key);
dlg_editbox_set(ed->valbox, dlg, val);
/* And delete it */
conf_del_str_str(conf, CONF_environmt, key);
}
}
dlg_refresh(ed->listbox, dlg);
}
}
}
struct portfwd_data {
union control *addbutton, *rembutton, *listbox;
union control *sourcebox, *destbox, *direction;
#ifndef NO_IPV6
union control *addressfamily;
};
static void portfwd_handler(union control *ctrl, dlgparam *dlg,
Conf *conf = (Conf *)data;
struct portfwd_data *pfd =
(struct portfwd_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == pfd->listbox) {
char *key, *val;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);
val != NULL;
val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {
char *p;
if (!strcmp(val, "D")) {
char *L;
/*
* A dynamic forwarding is stored as L12345=D or
* 6L12345=D (since it's mutually exclusive with
* L12345=anything else), but displayed as D12345
* to match the fiction that 'Local', 'Remote' and
* 'Dynamic' are three distinct modes and also to
* align with OpenSSH's command line option syntax
* that people will already be used to. So, for
* display purposes, find the L in the key string
* and turn it into a D.
*/
p = dupprintf("%s\t", key);
L = strchr(p, 'L');
if (L) *L = 'D';
} else
p = dupprintf("%s\t%s", key, val);
dlg_listbox_add(ctrl, dlg, p);
sfree(p);
}
dlg_update_done(ctrl, dlg);
} else if (ctrl == pfd->direction) {
/*
* Default is Local.
*/
dlg_radiobutton_set(ctrl, dlg, 0);
#ifndef NO_IPV6
} else if (ctrl == pfd->addressfamily) {
dlg_radiobutton_set(ctrl, dlg, 0);
} else if (event == EVENT_ACTION) {
if (ctrl == pfd->addbutton) {
const char *family, *type;
#ifndef NO_IPV6
whichbutton = dlg_radiobutton_get(pfd->addressfamily, dlg);
if (whichbutton == 1)
family = "4";
else if (whichbutton == 2)
family = "6";
else
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
family = "";
whichbutton = dlg_radiobutton_get(pfd->direction, dlg);
if (whichbutton == 0)
type = "L";
else if (whichbutton == 1)
type = "R";
else
type = "D";
src = dlg_editbox_get(pfd->sourcebox, dlg);
if (!*src) {
dlg_error_msg(dlg, "You need to specify a source port number");
sfree(src);
return;
}
if (*type != 'D') {
val = dlg_editbox_get(pfd->destbox, dlg);
if (!*val || !host_strchr(val, ':')) {
dlg_error_msg(dlg,
"You need to specify a destination address\n"
"in the form \"host.name:port\"");
sfree(src);
sfree(val);
return;
}
} else {
type = "L";
val = dupstr("D"); /* special case */
sfree(src);
if (conf_get_str_str_opt(conf, CONF_portfwd, key)) {
dlg_error_msg(dlg, "Specified forwarding already exists");
} else {
conf_set_str_str(conf, CONF_portfwd, key, val);
}
sfree(key);
sfree(val);
dlg_refresh(pfd->listbox, dlg);
} else if (ctrl == pfd->rembutton) {
int i = dlg_listbox_index(pfd->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
} else {
char *key, *p;
key = conf_get_str_nthstrkey(conf, CONF_portfwd, i);
if (key) {
static const char *const afs = "A46";
static const char *const dirs = "LRD";
const char *afp;
int dir;
#endif
/* Populate controls with the entry we're about to delete
* for ease of editing */
p = key;
#ifndef NO_IPV6
idx = afp ? afp-afs : 0;
#ifndef NO_IPV6
dlg_radiobutton_set(pfd->addressfamily, dlg, idx);
#endif
val = conf_get_str_str(conf, CONF_portfwd, key);
if (!strcmp(val, "D")) {
dir = 'D';
val = "";
}
dlg_radiobutton_set(pfd->direction, dlg,
strchr(dirs, dir) - dirs);
p++;
dlg_editbox_set(pfd->sourcebox, dlg, p);
dlg_editbox_set(pfd->destbox, dlg, val);
/* And delete it */
conf_del_str_str(conf, CONF_portfwd, key);
}
}
dlg_refresh(pfd->listbox, dlg);
}
}
}
struct manual_hostkey_data {
union control *addbutton, *rembutton, *listbox, *keybox;
};
static void manual_hostkey_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
struct manual_hostkey_data *mh =
(struct manual_hostkey_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == mh->listbox) {
char *key, *val;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
val != NULL;
val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys,
key, &key)) {
dlg_listbox_add(ctrl, dlg, key);
}
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_ACTION) {
if (ctrl == mh->addbutton) {
char *key;
key = dlg_editbox_get(mh->keybox, dlg);
if (!*key) {
dlg_error_msg(dlg, "You need to specify a host key or "
"fingerprint");
sfree(key);
return;
}
if (!validate_manual_hostkey(key)) {
dlg_error_msg(dlg, "Host key is not in a valid format");
} else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys,
key)) {
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
dlg_error_msg(dlg, "Specified host key is already listed");
} else {
conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, "");
}
sfree(key);
dlg_refresh(mh->listbox, dlg);
} else if (ctrl == mh->rembutton) {
int i = dlg_listbox_index(mh->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
} else {
char *key;
key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i);
if (key) {
dlg_editbox_set(mh->keybox, dlg, key);
/* And delete it */
conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key);
}
}
dlg_refresh(mh->listbox, dlg);
}
}
}
static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
int setting = ctrl->generic.context.i;
#ifdef NAMED_CLIPBOARDS
int strsetting = ctrl->editbox.context2.i;
#endif
static const struct {
const char *name;
int id;
} options[] = {
{"No action", CLIPUI_NONE},
{CLIPNAME_IMPLICIT, CLIPUI_IMPLICIT},
{CLIPNAME_EXPLICIT, CLIPUI_EXPLICIT},
};
if (event == EVENT_REFRESH) {
int i, val = conf_get_int(conf, setting);
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
#ifdef NAMED_CLIPBOARDS
for (i = 0; i < lenof(options); i++)
dlg_listbox_add(ctrl, dlg, options[i].name);
if (val == CLIPUI_CUSTOM) {
const char *sval = conf_get_str(conf, strsetting);
for (i = 0; i < lenof(options); i++)
if (!strcmp(sval, options[i].name))
break; /* needs escaping */
if (i < lenof(options) || sval[0] == '=') {
dlg_editbox_set(ctrl, dlg, escaped);
sfree(escaped);
} else {
dlg_editbox_set(ctrl, dlg, sval);
}
} else {
dlg_editbox_set(ctrl, dlg, options[0].name); /* fallback */
for (i = 0; i < lenof(options); i++)
if (val == options[i].id)
dlg_editbox_set(ctrl, dlg, options[i].name);
}
#else
for (i = 0; i < lenof(options); i++)
dlg_listbox_addwithid(ctrl, dlg, options[i].name, options[i].id);
dlg_listbox_select(ctrl, dlg, 0); /* fallback */
for (i = 0; i < lenof(options); i++)
if (val == options[i].id)
dlg_listbox_select(ctrl, dlg, i);
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_SELCHANGE
#ifdef NAMED_CLIPBOARDS
|| event == EVENT_VALCHANGE
#endif
) {
#ifdef NAMED_CLIPBOARDS
char *sval = dlg_editbox_get(ctrl, dlg);
int i;
for (i = 0; i < lenof(options); i++)
if (!strcmp(sval, options[i].name)) {
conf_set_int(conf, setting, options[i].id);
conf_set_str(conf, strsetting, "");
break;
}
if (i == lenof(options)) {
conf_set_int(conf, setting, CLIPUI_CUSTOM);
if (sval[0] == '=')
sval++;
conf_set_str(conf, strsetting, sval);
}
int index = dlg_listbox_index(ctrl, dlg);
if (index >= 0) {
int val = dlg_listbox_getid(ctrl, dlg, index);
conf_set_int(conf, setting, val);
}
static void clipboard_control(struct controlset *s, const char *label,
char shortcut, int percentage, intorptr helpctx,
int setting, int strsetting)
{
#ifdef NAMED_CLIPBOARDS
ctrl_combobox(s, label, shortcut, percentage, helpctx,
clipboard_selector_handler, I(setting), I(strsetting));
#else
/* strsetting isn't needed in this case */
ctrl_droplist(s, label, shortcut, percentage, helpctx,
clipboard_selector_handler, I(setting));
#endif
}
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
static void serial_parity_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
static const struct {
const char *name;
int val;
} parities[] = {
{"None", SER_PAR_NONE},
{"Odd", SER_PAR_ODD},
{"Even", SER_PAR_EVEN},
{"Mark", SER_PAR_MARK},
{"Space", SER_PAR_SPACE},
};
int mask = ctrl->listbox.context.i;
int i, j;
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
/* Fetching this once at the start of the function ensures we
* remember what the right value is supposed to be when
* operations below cause reentrant calls to this function. */
int oldparity = conf_get_int(conf, CONF_serparity);
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < lenof(parities); i++) {
if (mask & (1 << parities[i].val))
dlg_listbox_addwithid(ctrl, dlg, parities[i].name,
parities[i].val);
}
for (i = j = 0; i < lenof(parities); i++) {
if (mask & (1 << parities[i].val)) {
if (oldparity == parities[i].val) {
dlg_listbox_select(ctrl, dlg, j);
break;
}
j++;
}
}
if (i == lenof(parities)) { /* an unsupported setting was chosen */
dlg_listbox_select(ctrl, dlg, 0);
oldparity = SER_PAR_NONE;
}
dlg_update_done(ctrl, dlg);
conf_set_int(conf, CONF_serparity, oldparity); /* restore */
} else if (event == EVENT_SELCHANGE) {
int i = dlg_listbox_index(ctrl, dlg);
if (i < 0)
i = SER_PAR_NONE;
else
i = dlg_listbox_getid(ctrl, dlg, i);
conf_set_int(conf, CONF_serparity, i);
}
}
static void serial_flow_handler(union control *ctrl, dlgparam *dlg,
void *data, int event)
{
static const struct {
const char *name;
int val;
} flows[] = {
{"None", SER_FLOW_NONE},
{"XON/XOFF", SER_FLOW_XONXOFF},
{"RTS/CTS", SER_FLOW_RTSCTS},
{"DSR/DTR", SER_FLOW_DSRDTR},
};
int mask = ctrl->listbox.context.i;
int i, j;
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
/* Fetching this once at the start of the function ensures we
* remember what the right value is supposed to be when
* operations below cause reentrant calls to this function. */
int oldflow = conf_get_int(conf, CONF_serflow);
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < lenof(flows); i++) {
if (mask & (1 << flows[i].val))
dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val);
}
for (i = j = 0; i < lenof(flows); i++) {
if (mask & (1 << flows[i].val)) {
if (oldflow == flows[i].val) {
dlg_listbox_select(ctrl, dlg, j);
break;
}
j++;
}
}
if (i == lenof(flows)) { /* an unsupported setting was chosen */
dlg_listbox_select(ctrl, dlg, 0);
oldflow = SER_FLOW_NONE;
}
dlg_update_done(ctrl, dlg);
conf_set_int(conf, CONF_serflow, oldflow);/* restore */
} else if (event == EVENT_SELCHANGE) {
int i = dlg_listbox_index(ctrl, dlg);
if (i < 0)
i = SER_FLOW_NONE;
else
i = dlg_listbox_getid(ctrl, dlg, i);
conf_set_int(conf, CONF_serflow, i);
}
}
void setup_config_box(struct controlbox *b, bool midsession,
int protocol, int protcfginfo)
const struct BackendVtable *backvt;
struct controlset *s;
struct sessionsaver_data *ssd;
struct charclass_data *ccd;
struct colour_data *cd;
struct ttymodes_data *td;
struct environ_data *ed;
struct portfwd_data *pfd;
struct manual_hostkey_data *mh;
union control *c;
bool resize_forbidden = false;
ssd = (struct sessionsaver_data *)
ctrl_alloc_with_free(b, sizeof(struct sessionsaver_data),
sessionsaver_data_free);
memset(ssd, 0, sizeof(*ssd));
ssd->savedsession = dupstr("");

Jacob Nevins
committed
ssd->midsession = midsession;
/*
* The standard panel that appears at the bottom of all panels:
* Open, Cancel, Apply etc.
*/
s = ctrl_getset(b, "", "", "");
ctrl_columns(s, 5, 20, 20, 20, 20, 20);
ssd->okbutton = ctrl_pushbutton(s,
(midsession ? "Apply" : "Open"),
(char)(midsession ? 'a' : 'o'),
HELPCTX(no_help),
sessionsaver_handler, P(ssd));
ssd->okbutton->button.isdefault = true;
ssd->okbutton->generic.column = 3;
ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help),
sessionsaver_handler, P(ssd));
ssd->cancelbutton->button.iscancel = true;
ssd->cancelbutton->generic.column = 4;
/* We carefully don't close the 5-column part, so that platform-
* specific add-ons can put extra buttons alongside Open and Cancel. */
/*
* The Session panel.
*/
str = dupprintf("Basic options for your %s session", appname);
ctrl_settitle(b, "Session", str);
sfree(str);
if (!midsession) {
struct hostport *hp = (struct hostport *)
ctrl_alloc(b, sizeof(struct hostport));
s = ctrl_getset(b, "Session", "hostport",
"Specify the destination you want to connect to");
ctrl_columns(s, 2, 75, 25);
c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100,
HELPCTX(session_hostname),
config_host_handler, I(0), I(0));
c->generic.column = 0;
hp->host = c;
c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100,
HELPCTX(session_hostname),
config_port_handler, I(0), I(0));
c->generic.column = 1;
hp->port = c;
ctrl_columns(s, 1, 100);
c = ctrl_text(s, "Connection type:", HELPCTX(session_hostname));
ctrl_columns(s, 2, 62, 38);
c = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3,
HELPCTX(session_hostname),
config_protocols_handler, P(hp), NULL);
c->generic.column = 0;
hp->protradio = c;
c->radio.buttons = sresize(c->radio.buttons, PROTOCOL_LIMIT, char *);
c->radio.shortcuts = sresize(c->radio.shortcuts, PROTOCOL_LIMIT, char);
c->radio.buttondata = sresize(c->radio.buttondata, PROTOCOL_LIMIT,
intorptr);
assert(c->radio.nbuttons == 0);
/* UI design assumes there exists at least one 'real' radio button */
assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT);
for (size_t i = 0; i < n_ui_backends; i++) {
assert(backends[i]);
c->radio.buttons[c->radio.nbuttons] =
dupstr(backends[i]->displayname);
c->radio.shortcuts[c->radio.nbuttons] =
(backends[i]->protocol == PROT_SSH ? 's' :
backends[i]->protocol == PROT_SERIAL ? 'r' :
backends[i]->protocol == PROT_RAW ? 'w' : /* FIXME unused */
NO_SHORTCUT);
c->radio.buttondata[c->radio.nbuttons] =
I(backends[i]->protocol);
c->radio.nbuttons++;
/* UI design assumes there exists at least one droplist entry */
assert(backends[c->radio.nbuttons]);
c->radio.buttons[c->radio.nbuttons] = dupstr("Other:");
c->radio.shortcuts[c->radio.nbuttons] = 't';
c->radio.buttondata[c->radio.nbuttons] = I(-1);
c->radio.nbuttons++;
c = ctrl_droplist(s, NULL, NO_SHORTCUT, 100,
HELPCTX(session_hostname),
config_protocols_handler, P(hp));
hp->protlist = c;
/* droplist is populated in config_protocols_handler */
c->generic.column = 1;
/* Vertically centre the two protocol controls w.r.t. each other */
hp->protlist->generic.align_next_to = hp->protradio;
ctrl_columns(s, 1, 100);
}
/*
* The Load/Save panel is available even in mid-session.
*/
s = ctrl_getset(b, "Session", "savedsessions",
midsession ? "Save the current session settings" :
"Load, save or delete a stored session");
ctrl_columns(s, 2, 75, 25);
ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100,
HELPCTX(session_saved),
sessionsaver_handler, P(ssd), P(NULL));
ssd->editbox->generic.column = 0;
/* Reset columns so that the buttons are alongside the list, rather
* than alongside that edit box. */
ctrl_columns(s, 1, 100);
ctrl_columns(s, 2, 75, 25);
ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT,
HELPCTX(session_saved),
sessionsaver_handler, P(ssd));
ssd->listbox->generic.column = 0;
ssd->listbox->listbox.height = 7;

Jacob Nevins
committed
if (!midsession) {
ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l',
HELPCTX(session_saved),
sessionsaver_handler, P(ssd));
ssd->loadbutton->generic.column = 1;

Jacob Nevins
committed
} else {
/* We can't offer the Load button mid-session, as it would allow the
* user to load and subsequently save settings they can't see. (And
* also change otherwise immutable settings underfoot; that probably
* shouldn't be a problem, but.) */
ssd->loadbutton = NULL;

Jacob Nevins
committed
}
/* "Save" button is permitted mid-session. */
ssd->savebutton = ctrl_pushbutton(s, "Save", 'v',
HELPCTX(session_saved),
sessionsaver_handler, P(ssd));
ssd->savebutton->generic.column = 1;

Jacob Nevins
committed
if (!midsession) {
ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd',
HELPCTX(session_saved),
sessionsaver_handler, P(ssd));
ssd->delbutton->generic.column = 1;

Jacob Nevins
committed
} else {
/* Disable the Delete button mid-session too, for UI consistency. */
ssd->delbutton = NULL;

Jacob Nevins
committed
}
ctrl_columns(s, 1, 100);
s = ctrl_getset(b, "Session", "otheropts", NULL);
ctrl_radiobuttons(s, "Close window on exit:", 'x', 4,
HELPCTX(session_coe),
conf_radiobutton_handler,
I(CONF_close_on_exit),
"Always", I(FORCE_ON),
"Never", I(FORCE_OFF),
"Only on clean exit", I(AUTO), NULL);
/*
* The Session/Logging panel.
*/
ctrl_settitle(b, "Session/Logging", "Options controlling session logging");
s = ctrl_getset(b, "Session/Logging", "main", NULL);
/*
* The logging buttons change depending on whether SSH packet
* logging can sensibly be available.
*/
{
const char *sshlogname, *sshrawlogname;
if ((midsession && protocol == PROT_SSH) ||
(!midsession && backend_vt_from_proto(PROT_SSH))) {
sshlogname = "SSH packets";
sshrawlogname = "SSH packets and raw data";
} else {
sshlogname = NULL; /* this will disable both buttons */
sshrawlogname = NULL; /* this will just placate optimisers */
ctrl_radiobuttons(s, "Session logging:", NO_SHORTCUT, 2,
HELPCTX(logging_main),
loggingbuttons_handler,
I(CONF_logtype),
"None", 't', I(LGTYP_NONE),
"Printable output", 'p', I(LGTYP_ASCII),
"All session output", 'l', I(LGTYP_DEBUG),
sshlogname, 's', I(LGTYP_PACKETS),
sshrawlogname, 'r', I(LGTYP_SSHRAW),
NULL);
ctrl_filesel(s, "Log file name:", 'f',
NULL, true, "Select session log file name",
HELPCTX(logging_filename),
conf_filesel_handler, I(CONF_logfilename));
ctrl_text(s, "(Log file name can contain &Y, &M, &D for date,"
" &T for time, &H for host name, and &P for port number)",
HELPCTX(logging_filename));
ctrl_radiobuttons(s, "What to do if the log file already exists:", 'e', 1,
HELPCTX(logging_exists),
conf_radiobutton_handler, I(CONF_logxfovr),
"Always overwrite it", I(LGXF_OVR),
"Always append to the end of it", I(LGXF_APN),
"Ask the user every time", I(LGXF_ASK), NULL);
ctrl_checkbox(s, "Flush log file frequently", 'u',
HELPCTX(logging_flush),
conf_checkbox_handler, I(CONF_logflush));
ctrl_checkbox(s, "Include header", 'i',
HELPCTX(logging_header),
conf_checkbox_handler, I(CONF_logheader));

Jacob Nevins
committed
if ((midsession && protocol == PROT_SSH) ||
(!midsession && backend_vt_from_proto(PROT_SSH))) {
s = ctrl_getset(b, "Session/Logging", "ssh",
"Options specific to SSH packet logging");
ctrl_checkbox(s, "Omit known password fields", 'k',
HELPCTX(logging_ssh_omit_password),
conf_checkbox_handler, I(CONF_logomitpass));
ctrl_checkbox(s, "Omit session data", 'd',
HELPCTX(logging_ssh_omit_data),
conf_checkbox_handler, I(CONF_logomitdata));

Jacob Nevins
committed
}
/*
* The Terminal panel.
*/
ctrl_settitle(b, "Terminal", "Options controlling the terminal emulation");
s = ctrl_getset(b, "Terminal", "general", "Set various terminal options");
ctrl_checkbox(s, "Auto wrap mode initially on", 'w',
HELPCTX(terminal_autowrap),
conf_checkbox_handler, I(CONF_wrap_mode));
ctrl_checkbox(s, "DEC Origin Mode initially on", 'd',
HELPCTX(terminal_decom),
conf_checkbox_handler, I(CONF_dec_om));
ctrl_checkbox(s, "Implicit CR in every LF", 'r',
HELPCTX(terminal_lfhascr),
conf_checkbox_handler, I(CONF_lfhascr));
ctrl_checkbox(s, "Implicit LF in every CR", 'f',
HELPCTX(terminal_crhaslf),
conf_checkbox_handler, I(CONF_crhaslf));
ctrl_checkbox(s, "Use background colour to erase screen", 'e',
HELPCTX(terminal_bce),
conf_checkbox_handler, I(CONF_bce));
ctrl_checkbox(s, "Enable blinking text", 'n',
HELPCTX(terminal_blink),
conf_checkbox_handler, I(CONF_blinktext));
ctrl_editbox(s, "Answerback to ^E:", 's', 100,
HELPCTX(terminal_answerback),
conf_editbox_handler, I(CONF_answerback), I(1));
s = ctrl_getset(b, "Terminal", "ldisc", "Line discipline options");
ctrl_radiobuttons(s, "Local echo:", 'l', 3,
HELPCTX(terminal_localecho),
conf_radiobutton_handler,I(CONF_localecho),
"Auto", I(AUTO),
"Force on", I(FORCE_ON),
"Force off", I(FORCE_OFF), NULL);
ctrl_radiobuttons(s, "Local line editing:", 't', 3,
HELPCTX(terminal_localedit),
conf_radiobutton_handler,I(CONF_localedit),
"Auto", I(AUTO),
"Force on", I(FORCE_ON),
"Force off", I(FORCE_OFF), NULL);
s = ctrl_getset(b, "Terminal", "printing", "Remote-controlled printing");
ctrl_combobox(s, "Printer to send ANSI printer output to:", 'p', 100,
HELPCTX(terminal_printing),
printerbox_handler, P(NULL), P(NULL));
/*
* The Terminal/Keyboard panel.
*/
ctrl_settitle(b, "Terminal/Keyboard",
"Options controlling the effects of keys");
s = ctrl_getset(b, "Terminal/Keyboard", "mappings",
"Change the sequences sent by:");
ctrl_radiobuttons(s, "The Backspace key", 'b', 2,
HELPCTX(keyboard_backspace),
conf_radiobutton_bool_handler,
I(CONF_bksp_is_delete),
"Control-H", I(0), "Control-? (127)", I(1), NULL);
ctrl_radiobuttons(s, "The Home and End keys", 'e', 2,
HELPCTX(keyboard_homeend),
conf_radiobutton_bool_handler,
I(CONF_rxvt_homeend),
"Standard", I(false), "rxvt", I(true), NULL);
ctrl_radiobuttons(s, "The Function keys and keypad", 'f', 3,
HELPCTX(keyboard_funkeys),
conf_radiobutton_handler,
I(CONF_funky_type),
"ESC[n~", I(0), "Linux", I(1), "Xterm R6", I(2),
"VT400", I(3), "VT100+", I(4), "SCO", I(5), NULL);
s = ctrl_getset(b, "Terminal/Keyboard", "appkeypad",
"Application keypad settings:");
ctrl_radiobuttons(s, "Initial state of cursor keys:", 'r', 3,
HELPCTX(keyboard_appcursor),
conf_radiobutton_bool_handler,
I(CONF_app_cursor),
"Normal", I(0), "Application", I(1), NULL);
ctrl_radiobuttons(s, "Initial state of numeric keypad:", 'n', 3,
HELPCTX(keyboard_appkeypad),
numeric_keypad_handler, P(NULL),
"Normal", I(0), "Application", I(1), "NetHack", I(2),
NULL);
/*
* The Terminal/Bell panel.
*/
ctrl_settitle(b, "Terminal/Bell",
"Options controlling the terminal bell");
s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell");
ctrl_radiobuttons(s, "Action to happen when a bell occurs:", 'b', 1,
HELPCTX(bell_style),
conf_radiobutton_handler, I(CONF_beep),
"None (bell disabled)", I(BELL_DISABLED),
"Make default system alert sound", I(BELL_DEFAULT),
"Visual bell (flash window)", I(BELL_VISUAL), NULL);
s = ctrl_getset(b, "Terminal/Bell", "overload",
"Control the bell overload behaviour");
ctrl_checkbox(s, "Bell is temporarily disabled when over-used", 'd',
HELPCTX(bell_overload),
conf_checkbox_handler, I(CONF_bellovl));
ctrl_editbox(s, "Over-use means this many bells...", 'm', 20,
HELPCTX(bell_overload),
conf_editbox_handler, I(CONF_bellovl_n), I(-1));
ctrl_editbox(s, "... in this many seconds", 't', 20,
HELPCTX(bell_overload),
conf_editbox_handler, I(CONF_bellovl_t),
I(-TICKSPERSEC));
ctrl_text(s, "The bell is re-enabled after a few seconds of silence.",
HELPCTX(bell_overload));
ctrl_editbox(s, "Seconds of silence required", 's', 20,
HELPCTX(bell_overload),
conf_editbox_handler, I(CONF_bellovl_s),
I(-TICKSPERSEC));
/*
* The Terminal/Features panel.
*/
ctrl_settitle(b, "Terminal/Features",
"Enabling and disabling advanced terminal features");
s = ctrl_getset(b, "Terminal/Features", "main", NULL);
ctrl_checkbox(s, "Disable application cursor keys mode", 'u',
HELPCTX(features_application),
conf_checkbox_handler, I(CONF_no_applic_c));
ctrl_checkbox(s, "Disable application keypad mode", 'k',
HELPCTX(features_application),
conf_checkbox_handler, I(CONF_no_applic_k));
ctrl_checkbox(s, "Disable xterm-style mouse reporting", 'x',
HELPCTX(features_mouse),
conf_checkbox_handler, I(CONF_no_mouse_rep));
ctrl_checkbox(s, "Disable remote-controlled terminal resizing", 's',
HELPCTX(features_resize),
conf_checkbox_handler,
I(CONF_no_remote_resize));
ctrl_checkbox(s, "Disable switching to alternate terminal screen", 'w',
HELPCTX(features_altscreen),
conf_checkbox_handler, I(CONF_no_alt_screen));
ctrl_checkbox(s, "Disable remote-controlled window title changing", 't',
HELPCTX(features_retitle),
conf_checkbox_handler,
I(CONF_no_remote_wintitle));
ctrl_radiobuttons(s, "Response to remote title query (SECURITY):", 'q', 3,
HELPCTX(features_qtitle),
conf_radiobutton_handler,
I(CONF_remote_qtitle_action),
"None", I(TITLE_NONE),
"Empty string", I(TITLE_EMPTY),
"Window title", I(TITLE_REAL), NULL);
ctrl_checkbox(s, "Disable remote-controlled clearing of scrollback", 'e',
HELPCTX(features_clearscroll),
conf_checkbox_handler,
I(CONF_no_remote_clearscroll));
ctrl_checkbox(s, "Disable destructive backspace on server sending ^?",'b',
HELPCTX(features_dbackspace),
conf_checkbox_handler, I(CONF_no_dbackspace));
ctrl_checkbox(s, "Disable remote-controlled character set configuration",
'r', HELPCTX(features_charset), conf_checkbox_handler,
I(CONF_no_remote_charset));
ctrl_checkbox(s, "Disable Arabic text shaping",
'l', HELPCTX(features_arabicshaping), conf_checkbox_handler,
I(CONF_no_arabicshaping));
ctrl_checkbox(s, "Disable bidirectional text display",
'd', HELPCTX(features_bidi), conf_checkbox_handler,
I(CONF_no_bidi));
/*
* The Window panel.
*/
str = dupprintf("Options controlling %s's window", appname);
ctrl_settitle(b, "Window", str);
sfree(str);
backvt = backend_vt_from_proto(protocol);
if (backvt)
resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN);
if (!resize_forbidden || !midsession) {
s = ctrl_getset(b, "Window", "size", "Set the size of the window");
ctrl_columns(s, 2, 50, 50);
c = ctrl_editbox(s, "Columns", 'm', 100,
HELPCTX(window_size),
conf_editbox_handler, I(CONF_width), I(-1));
c->generic.column = 0;
c = ctrl_editbox(s, "Rows", 'r', 100,
HELPCTX(window_size),
conf_editbox_handler, I(CONF_height),I(-1));
c->generic.column = 1;
ctrl_columns(s, 1, 100);
}
s = ctrl_getset(b, "Window", "scrollback",
"Control the scrollback in the window");
ctrl_editbox(s, "Lines of scrollback", 's', 50,
HELPCTX(window_scrollback),
conf_editbox_handler, I(CONF_savelines), I(-1));
ctrl_checkbox(s, "Display scrollbar", 'd',
HELPCTX(window_scrollback),
conf_checkbox_handler, I(CONF_scrollbar));
ctrl_checkbox(s, "Reset scrollback on keypress", 'k',
HELPCTX(window_scrollback),
conf_checkbox_handler, I(CONF_scroll_on_key));
ctrl_checkbox(s, "Reset scrollback on display activity", 'p',
HELPCTX(window_scrollback),
conf_checkbox_handler, I(CONF_scroll_on_disp));
ctrl_checkbox(s, "Push erased text into scrollback", 'e',
HELPCTX(window_erased),
conf_checkbox_handler,
I(CONF_erase_to_scrollback));
/*
* The Window/Appearance panel.
*/
str = dupprintf("Configure the appearance of %s's window", appname);
ctrl_settitle(b, "Window/Appearance", str);
sfree(str);
s = ctrl_getset(b, "Window/Appearance", "cursor",
"Adjust the use of the cursor");
ctrl_radiobuttons(s, "Cursor appearance:", NO_SHORTCUT, 3,
HELPCTX(appearance_cursor),
conf_radiobutton_handler,
I(CONF_cursor_type),
"Block", 'l', I(0),
"Underline", 'u', I(1),
"Vertical line", 'v', I(2), NULL);
ctrl_checkbox(s, "Cursor blinks", 'b',
HELPCTX(appearance_cursor),
conf_checkbox_handler, I(CONF_blink_cur));
s = ctrl_getset(b, "Window/Appearance", "font",
ctrl_fontsel(s, "Font used in the terminal window", 'n',
HELPCTX(appearance_font),
conf_fontsel_handler, I(CONF_font));
s = ctrl_getset(b, "Window/Appearance", "mouse",
Loading
Loading full blame...