1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Rework per-backend GUI configuration.

In commit 1f399bec58 I had the idea of generating the protocol radio
buttons in the GUI configurer by looping over the backends[] array,
which gets the reliably correct list of available backends for a given
binary rather than having to second-guess. That's given me an idea: we
can do the same for the per-backend config panels too.

Now the GUI config panel for every backend is guarded by a check of
backend_vt_from_proto, and we won't display the config for that
backend unless it's present.

In particular, this allows me to move the serial-port configuration
back into config.c from the separate file sercfg.c: we find out
whether to apply it by querying backend_vt_from_proto(PROT_SERIAL),
the same as any other backend.

In _particular_ particular, that also makes it much easier for me to
move the serial config up the pecking order, so that it's now second
only to SSH in the list of per-protocol config panes, which I think is
now where it deserves to be.

(A side effect of that is that I now have to come up with a different
method of having each serial backend specify the subset of parity and
flow control schemes it supports. I've done it by adding an extra pair
of serial-port specific bitmask fields to BackendVtable, taking
advantage of the new vtable definition idiom to avoid having to
boringly declare them as zero in all the other backends.)
This commit is contained in:
Simon Tatham 2020-03-10 21:18:08 +00:00
parent fb80717e7e
commit 18d273fcf1
8 changed files with 191 additions and 200 deletions

4
Recipe
View File

@ -244,10 +244,10 @@ TERMINAL = terminal stripctrl wcwidth logging tree234 minibidi
# GUI front end and terminal emulator (putty, puttytel). # GUI front end and terminal emulator (putty, puttytel).
GUITERM = TERMINAL window windlg winctrls sizetip winprint winutils GUITERM = TERMINAL window windlg winctrls sizetip winprint winutils
+ wincfg sercfg winhelp winjump sessprep winselgui + wincfg winhelp winjump sessprep winselgui
# Same thing on Unix. # Same thing on Unix.
UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs UXTERM = TERMINAL uxcfg uxucs uxprint timing callback miscucs
GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym
+ x11misc gtkcomm sessprep + x11misc gtkcomm sessprep
GTKMAIN = gtkmain cmdline GTKMAIN = gtkmain cmdline

182
config.c
View File

@ -1469,6 +1469,114 @@ static void clipboard_control(struct controlset *s, const char *label,
#endif #endif
} }
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, void setup_config_box(struct controlbox *b, bool midsession,
int protocol, int protcfginfo) int protocol, int protcfginfo)
{ {
@ -2316,13 +2424,22 @@ void setup_config_box(struct controlbox *b, bool midsession,
} }
/* /*
* All the SSH stuff is omitted in PuTTYtel, or in a reconfig * Each per-protocol configuration GUI panel is conditionally
* when we're not doing SSH. * displayed. We don't display it if this binary doesn't contain a
* backend for its protocol at all; we don't display it if we're
* already in mid-session with a different protocol selected; and
* even if we _do_ have this protocol selected, we don't display
* the panel if the protocol doesn't permit any mid-session
* reconfiguration anyway.
*/ */
if (backend_vt_from_proto(PROT_SSH) && #define DISPLAY_RECONFIGURABLE_PROTOCOL(which_proto) \
(!midsession || protocol == PROT_SSH)) { (backend_vt_from_proto(which_proto) && \
(!midsession || protocol == (which_proto)))
#define DISPLAY_NON_RECONFIGURABLE_PROTOCOL(which_proto) \
(backend_vt_from_proto(which_proto) && !midsession)
if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSH)) {
/* /*
* The Connection/SSH panel. * The Connection/SSH panel.
*/ */
@ -2828,11 +2945,50 @@ void setup_config_box(struct controlbox *b, bool midsession,
} }
} }
/* if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SERIAL)) {
* The Telnet panel exists in the base config box, and in a const BackendVtable *ser_vt = backend_vt_from_proto(PROT_SERIAL);
* mid-session reconfig box _if_ we're using Telnet.
*/ /*
if (!midsession || protocol == PROT_TELNET) { * The Connection/Serial panel.
*/
ctrl_settitle(b, "Connection/Serial",
"Options controlling local serial lines");
if (!midsession) {
/*
* We don't permit switching to a different serial port in
* midflight, although we do allow all other
* reconfiguration.
*/
s = ctrl_getset(b, "Connection/Serial", "serline",
"Select a serial line");
ctrl_editbox(s, "Serial line to connect to", 'l', 40,
HELPCTX(serial_line),
conf_editbox_handler, I(CONF_serline), I(1));
}
s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line");
ctrl_editbox(s, "Speed (baud)", 's', 40,
HELPCTX(serial_speed),
conf_editbox_handler, I(CONF_serspeed), I(-1));
ctrl_editbox(s, "Data bits", 'b', 40,
HELPCTX(serial_databits),
conf_editbox_handler, I(CONF_serdatabits), I(-1));
/*
* Stop bits come in units of one half.
*/
ctrl_editbox(s, "Stop bits", 't', 40,
HELPCTX(serial_stopbits),
conf_editbox_handler, I(CONF_serstopbits), I(-2));
ctrl_droplist(s, "Parity", 'p', 40,
HELPCTX(serial_parity), serial_parity_handler,
I(ser_vt->serial_parity_mask));
ctrl_droplist(s, "Flow control", 'f', 40,
HELPCTX(serial_flow), serial_flow_handler,
I(ser_vt->serial_flow_mask));
}
if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_TELNET)) {
/* /*
* The Connection/Telnet panel. * The Connection/Telnet panel.
*/ */
@ -2866,8 +3022,7 @@ void setup_config_box(struct controlbox *b, bool midsession,
I(CONF_telnet_newline)); I(CONF_telnet_newline));
} }
if (!midsession) { if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_RLOGIN)) {
/* /*
* The Connection/Rlogin panel. * The Connection/Rlogin panel.
*/ */
@ -2880,8 +3035,11 @@ void setup_config_box(struct controlbox *b, bool midsession,
HELPCTX(rlogin_localuser), HELPCTX(rlogin_localuser),
conf_editbox_handler, I(CONF_localusername), I(1)); conf_editbox_handler, I(CONF_localusername), I(1));
}
if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_SUPDUP)) {
/* /*
* The Protocol/SUPDUP panel. * The Connection/SUPDUP panel.
*/ */
ctrl_settitle(b, "Connection/SUPDUP", ctrl_settitle(b, "Connection/SUPDUP",
"Enabling and disabling SUPDUP user options"); "Enabling and disabling SUPDUP user options");

View File

@ -534,6 +534,10 @@ struct BackendVtable {
int protocol; int protocol;
int default_port; int default_port;
unsigned flags; unsigned flags;
/* Only relevant for the serial protocol: bit masks of which
* parity and flow control settings are supported. */
unsigned serial_parity_mask, serial_flow_mask;
}; };
static inline const char *backend_init( static inline const char *backend_init(

171
sercfg.c
View File

@ -1,171 +0,0 @@
/*
* sercfg.c - the serial-port specific parts of the PuTTY
* configuration box. Centralised as cross-platform code because
* more than one platform will want to use it, but not part of the
* main configuration. The expectation is that each platform's
* local config function will call out to ser_setup_config_box() if
* it needs to set up the standard serial stuff. (Of course, it can
* then apply local tweaks after ser_setup_config_box() returns, if
* it needs to.)
*/
#include <assert.h>
#include <stdlib.h>
#include "putty.h"
#include "dialog.h"
#include "storage.h"
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 << i))
dlg_listbox_addwithid(ctrl, dlg, parities[i].name,
parities[i].val);
}
for (i = j = 0; i < lenof(parities); i++) {
if (mask & (1 << i)) {
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 << i))
dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val);
}
for (i = j = 0; i < lenof(flows); i++) {
if (mask & (1 << i)) {
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 ser_setup_config_box(struct controlbox *b, bool midsession,
int parity_mask, int flow_mask)
{
struct controlset *s;
/*
* Entirely new Connection/Serial panel for serial port
* configuration.
*/
ctrl_settitle(b, "Connection/Serial",
"Options controlling local serial lines");
if (!midsession) {
/*
* We don't permit switching to a different serial port in
* midflight, although we do allow all other
* reconfiguration.
*/
s = ctrl_getset(b, "Connection/Serial", "serline",
"Select a serial line");
ctrl_editbox(s, "Serial line to connect to", 'l', 40,
HELPCTX(serial_line),
conf_editbox_handler, I(CONF_serline), I(1));
}
s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line");
ctrl_editbox(s, "Speed (baud)", 's', 40,
HELPCTX(serial_speed),
conf_editbox_handler, I(CONF_serspeed), I(-1));
ctrl_editbox(s, "Data bits", 'b', 40,
HELPCTX(serial_databits),
conf_editbox_handler, I(CONF_serdatabits), I(-1));
/*
* Stop bits come in units of one half.
*/
ctrl_editbox(s, "Stop bits", 't', 40,
HELPCTX(serial_stopbits),
conf_editbox_handler, I(CONF_serstopbits), I(-2));
ctrl_droplist(s, "Parity", 'p', 40,
HELPCTX(serial_parity),
serial_parity_handler, I(parity_mask));
ctrl_droplist(s, "Flow control", 'f', 40,
HELPCTX(serial_flow),
serial_flow_handler, I(flow_mask));
}

View File

@ -67,13 +67,4 @@ void unix_setup_config_box(struct controlbox *b, bool midsession, int protocol)
} }
} }
} }
/*
* Serial back end is available on Unix. However, we have to
* mask out a couple of the configuration options: mark and
* space parity are not conveniently supported, and neither is
* DSR/DTR flow control.
*/
if (!midsession || (protocol == PROT_SERIAL))
ser_setup_config_box(b, midsession, 0x07, 0x07);
} }

View File

@ -578,4 +578,10 @@ const BackendVtable serial_backend = {
.id = "serial", .id = "serial",
.displayname = "Serial", .displayname = "Serial",
.protocol = PROT_SERIAL, .protocol = PROT_SERIAL,
.serial_parity_mask = ((1 << SER_PAR_NONE) |
(1 << SER_PAR_ODD) |
(1 << SER_PAR_EVEN)),
.serial_flow_mask = ((1 << SER_FLOW_NONE) |
(1 << SER_FLOW_XONXOFF) |
(1 << SER_FLOW_RTSCTS)),
}; };

View File

@ -391,12 +391,6 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help,
} }
} }
/*
* Serial back end is available on Windows.
*/
if (!midsession || (protocol == PROT_SERIAL))
ser_setup_config_box(b, midsession, 0x1F, 0x0F);
/* /*
* $XAUTHORITY is not reliable on Windows, so we provide a * $XAUTHORITY is not reliable on Windows, so we provide a
* means to override it. * means to override it.

View File

@ -446,4 +446,13 @@ const BackendVtable serial_backend = {
.id = "serial", .id = "serial",
.displayname = "Serial", .displayname = "Serial",
.protocol = PROT_SERIAL, .protocol = PROT_SERIAL,
.serial_parity_mask = ((1 << SER_PAR_NONE) |
(1 << SER_PAR_ODD) |
(1 << SER_PAR_EVEN) |
(1 << SER_PAR_MARK) |
(1 << SER_PAR_SPACE)),
.serial_flow_mask = ((1 << SER_FLOW_NONE) |
(1 << SER_FLOW_XONXOFF) |
(1 << SER_FLOW_RTSCTS) |
(1 << SER_FLOW_DSRDTR)),
}; };