| 1 | /* |
| 2 | * wincfg.c - the Windows-specific parts of the PuTTY configuration |
| 3 | * box. |
| 4 | */ |
| 5 | |
| 6 | #include <assert.h> |
| 7 | #include <stdlib.h> |
| 8 | |
| 9 | #include "putty.h" |
| 10 | #include "dialog.h" |
| 11 | #include "storage.h" |
| 12 | |
| 13 | static void about_handler(union control *ctrl, void *dlg, |
| 14 | void *data, int event) |
| 15 | { |
| 16 | HWND *hwndp = (HWND *)ctrl->generic.context.p; |
| 17 | |
| 18 | if (event == EVENT_ACTION) { |
| 19 | modal_about_box(*hwndp); |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | static void help_handler(union control *ctrl, void *dlg, |
| 24 | void *data, int event) |
| 25 | { |
| 26 | HWND *hwndp = (HWND *)ctrl->generic.context.p; |
| 27 | |
| 28 | if (event == EVENT_ACTION) { |
| 29 | show_help(*hwndp); |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | static void variable_pitch_handler(union control *ctrl, void *dlg, |
| 34 | void *data, int event) |
| 35 | { |
| 36 | if (event == EVENT_REFRESH) { |
| 37 | dlg_checkbox_set(ctrl, dlg, !dlg_get_fixed_pitch_flag(dlg)); |
| 38 | } else if (event == EVENT_VALCHANGE) { |
| 39 | dlg_set_fixed_pitch_flag(dlg, !dlg_checkbox_get(ctrl, dlg)); |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | void win_setup_config_box(struct controlbox *b, HWND *hwndp, int has_help, |
| 44 | int midsession, int protocol) |
| 45 | { |
| 46 | struct controlset *s; |
| 47 | union control *c; |
| 48 | char *str; |
| 49 | |
| 50 | if (!midsession) { |
| 51 | /* |
| 52 | * Add the About and Help buttons to the standard panel. |
| 53 | */ |
| 54 | s = ctrl_getset(b, "", "", ""); |
| 55 | c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help), |
| 56 | about_handler, P(hwndp)); |
| 57 | c->generic.column = 0; |
| 58 | if (has_help) { |
| 59 | c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help), |
| 60 | help_handler, P(hwndp)); |
| 61 | c->generic.column = 1; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | /* |
| 66 | * Full-screen mode is a Windows peculiarity; hence |
| 67 | * scrollbar_in_fullscreen is as well. |
| 68 | */ |
| 69 | s = ctrl_getset(b, "Window", "scrollback", |
| 70 | "Control the scrollback in the window"); |
| 71 | ctrl_checkbox(s, "Display scrollbar in full screen mode", 'i', |
| 72 | HELPCTX(window_scrollback), |
| 73 | dlg_stdcheckbox_handler, |
| 74 | I(offsetof(Config,scrollbar_in_fullscreen))); |
| 75 | /* |
| 76 | * Really this wants to go just after `Display scrollbar'. See |
| 77 | * if we can find that control, and do some shuffling. |
| 78 | */ |
| 79 | { |
| 80 | int i; |
| 81 | for (i = 0; i < s->ncontrols; i++) { |
| 82 | c = s->ctrls[i]; |
| 83 | if (c->generic.type == CTRL_CHECKBOX && |
| 84 | c->generic.context.i == offsetof(Config,scrollbar)) { |
| 85 | /* |
| 86 | * Control i is the scrollbar checkbox. |
| 87 | * Control s->ncontrols-1 is the scrollbar-in-FS one. |
| 88 | */ |
| 89 | if (i < s->ncontrols-2) { |
| 90 | c = s->ctrls[s->ncontrols-1]; |
| 91 | memmove(s->ctrls+i+2, s->ctrls+i+1, |
| 92 | (s->ncontrols-i-2)*sizeof(union control *)); |
| 93 | s->ctrls[i+1] = c; |
| 94 | } |
| 95 | break; |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | * Windows has the AltGr key, which has various Windows- |
| 102 | * specific options. |
| 103 | */ |
| 104 | s = ctrl_getset(b, "Terminal/Keyboard", "features", |
| 105 | "Enable extra keyboard features:"); |
| 106 | ctrl_checkbox(s, "AltGr acts as Compose key", 't', |
| 107 | HELPCTX(keyboard_compose), |
| 108 | dlg_stdcheckbox_handler, I(offsetof(Config,compose_key))); |
| 109 | ctrl_checkbox(s, "Control-Alt is different from AltGr", 'd', |
| 110 | HELPCTX(keyboard_ctrlalt), |
| 111 | dlg_stdcheckbox_handler, I(offsetof(Config,ctrlaltkeys))); |
| 112 | |
| 113 | /* |
| 114 | * Windows allows an arbitrary .WAV to be played as a bell, and |
| 115 | * also the use of the PC speaker. For this we must search the |
| 116 | * existing controlset for the radio-button set controlling the |
| 117 | * `beep' option, and add extra buttons to it. |
| 118 | * |
| 119 | * Note that although this _looks_ like a hideous hack, it's |
| 120 | * actually all above board. The well-defined interface to the |
| 121 | * per-platform dialog box code is the _data structures_ `union |
| 122 | * control', `struct controlset' and so on; so code like this |
| 123 | * that reaches into those data structures and changes bits of |
| 124 | * them is perfectly legitimate and crosses no boundaries. All |
| 125 | * the ctrl_* routines that create most of the controls are |
| 126 | * convenient shortcuts provided on the cross-platform side of |
| 127 | * the interface, and template creation code is under no actual |
| 128 | * obligation to use them. |
| 129 | */ |
| 130 | s = ctrl_getset(b, "Terminal/Bell", "style", "Set the style of bell"); |
| 131 | { |
| 132 | int i; |
| 133 | for (i = 0; i < s->ncontrols; i++) { |
| 134 | c = s->ctrls[i]; |
| 135 | if (c->generic.type == CTRL_RADIO && |
| 136 | c->generic.context.i == offsetof(Config, beep)) { |
| 137 | assert(c->generic.handler == dlg_stdradiobutton_handler); |
| 138 | c->radio.nbuttons += 2; |
| 139 | c->radio.buttons = |
| 140 | sresize(c->radio.buttons, c->radio.nbuttons, char *); |
| 141 | c->radio.buttons[c->radio.nbuttons-1] = |
| 142 | dupstr("Play a custom sound file"); |
| 143 | c->radio.buttons[c->radio.nbuttons-2] = |
| 144 | dupstr("Beep using the PC speaker"); |
| 145 | c->radio.buttondata = |
| 146 | sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); |
| 147 | c->radio.buttondata[c->radio.nbuttons-1] = I(BELL_WAVEFILE); |
| 148 | c->radio.buttondata[c->radio.nbuttons-2] = I(BELL_PCSPEAKER); |
| 149 | if (c->radio.shortcuts) { |
| 150 | c->radio.shortcuts = |
| 151 | sresize(c->radio.shortcuts, c->radio.nbuttons, char); |
| 152 | c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT; |
| 153 | c->radio.shortcuts[c->radio.nbuttons-2] = NO_SHORTCUT; |
| 154 | } |
| 155 | break; |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | ctrl_filesel(s, "Custom sound file to play as a bell:", NO_SHORTCUT, |
| 160 | FILTER_WAVE_FILES, FALSE, "Select bell sound file", |
| 161 | HELPCTX(bell_style), |
| 162 | dlg_stdfilesel_handler, I(offsetof(Config, bell_wavefile))); |
| 163 | |
| 164 | /* |
| 165 | * While we've got this box open, taskbar flashing on a bell is |
| 166 | * also Windows-specific. |
| 167 | */ |
| 168 | ctrl_radiobuttons(s, "Taskbar/caption indication on bell:", 'i', 3, |
| 169 | HELPCTX(bell_taskbar), |
| 170 | dlg_stdradiobutton_handler, |
| 171 | I(offsetof(Config, beep_ind)), |
| 172 | "Disabled", I(B_IND_DISABLED), |
| 173 | "Flashing", I(B_IND_FLASH), |
| 174 | "Steady", I(B_IND_STEADY), NULL); |
| 175 | |
| 176 | /* |
| 177 | * The sunken-edge border is a Windows GUI feature. |
| 178 | */ |
| 179 | s = ctrl_getset(b, "Window/Appearance", "border", |
| 180 | "Adjust the window border"); |
| 181 | ctrl_checkbox(s, "Sunken-edge border (slightly thicker)", 's', |
| 182 | HELPCTX(appearance_border), |
| 183 | dlg_stdcheckbox_handler, I(offsetof(Config,sunken_edge))); |
| 184 | |
| 185 | /* |
| 186 | * Configurable font quality settings for Windows. |
| 187 | */ |
| 188 | s = ctrl_getset(b, "Window/Appearance", "font", |
| 189 | "Font settings"); |
| 190 | ctrl_checkbox(s, "Allow selection of variable-pitch fonts", NO_SHORTCUT, |
| 191 | HELPCTX(appearance_font), variable_pitch_handler, I(0)); |
| 192 | ctrl_radiobuttons(s, "Font quality:", 'q', 2, |
| 193 | HELPCTX(appearance_font), |
| 194 | dlg_stdradiobutton_handler, |
| 195 | I(offsetof(Config, font_quality)), |
| 196 | "Antialiased", I(FQ_ANTIALIASED), |
| 197 | "Non-Antialiased", I(FQ_NONANTIALIASED), |
| 198 | "ClearType", I(FQ_CLEARTYPE), |
| 199 | "Default", I(FQ_DEFAULT), NULL); |
| 200 | |
| 201 | /* |
| 202 | * Cyrillic Lock is a horrid misfeature even on Windows, and |
| 203 | * the least we can do is ensure it never makes it to any other |
| 204 | * platform (at least unless someone fixes it!). |
| 205 | */ |
| 206 | s = ctrl_getset(b, "Window/Translation", "tweaks", NULL); |
| 207 | ctrl_checkbox(s, "Caps Lock acts as Cyrillic switch", 's', |
| 208 | HELPCTX(translation_cyrillic), |
| 209 | dlg_stdcheckbox_handler, |
| 210 | I(offsetof(Config,xlat_capslockcyr))); |
| 211 | |
| 212 | /* |
| 213 | * On Windows we can use but not enumerate translation tables |
| 214 | * from the operating system. Briefly document this. |
| 215 | */ |
| 216 | s = ctrl_getset(b, "Window/Translation", "trans", |
| 217 | "Character set translation on received data"); |
| 218 | ctrl_text(s, "(Codepages supported by Windows but not listed here, " |
| 219 | "such as CP866 on many systems, can be entered manually)", |
| 220 | HELPCTX(translation_codepage)); |
| 221 | |
| 222 | /* |
| 223 | * Windows has the weird OEM font mode, which gives us some |
| 224 | * additional options when working with line-drawing |
| 225 | * characters. |
| 226 | */ |
| 227 | str = dupprintf("Adjust how %s displays line drawing characters", appname); |
| 228 | s = ctrl_getset(b, "Window/Translation", "linedraw", str); |
| 229 | sfree(str); |
| 230 | { |
| 231 | int i; |
| 232 | for (i = 0; i < s->ncontrols; i++) { |
| 233 | c = s->ctrls[i]; |
| 234 | if (c->generic.type == CTRL_RADIO && |
| 235 | c->generic.context.i == offsetof(Config, vtmode)) { |
| 236 | assert(c->generic.handler == dlg_stdradiobutton_handler); |
| 237 | c->radio.nbuttons += 3; |
| 238 | c->radio.buttons = |
| 239 | sresize(c->radio.buttons, c->radio.nbuttons, char *); |
| 240 | c->radio.buttons[c->radio.nbuttons-3] = |
| 241 | dupstr("Font has XWindows encoding"); |
| 242 | c->radio.buttons[c->radio.nbuttons-2] = |
| 243 | dupstr("Use font in both ANSI and OEM modes"); |
| 244 | c->radio.buttons[c->radio.nbuttons-1] = |
| 245 | dupstr("Use font in OEM mode only"); |
| 246 | c->radio.buttondata = |
| 247 | sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); |
| 248 | c->radio.buttondata[c->radio.nbuttons-3] = I(VT_XWINDOWS); |
| 249 | c->radio.buttondata[c->radio.nbuttons-2] = I(VT_OEMANSI); |
| 250 | c->radio.buttondata[c->radio.nbuttons-1] = I(VT_OEMONLY); |
| 251 | if (!c->radio.shortcuts) { |
| 252 | int j; |
| 253 | c->radio.shortcuts = snewn(c->radio.nbuttons, char); |
| 254 | for (j = 0; j < c->radio.nbuttons; j++) |
| 255 | c->radio.shortcuts[j] = NO_SHORTCUT; |
| 256 | } else { |
| 257 | c->radio.shortcuts = sresize(c->radio.shortcuts, |
| 258 | c->radio.nbuttons, char); |
| 259 | } |
| 260 | c->radio.shortcuts[c->radio.nbuttons-3] = 'x'; |
| 261 | c->radio.shortcuts[c->radio.nbuttons-2] = 'b'; |
| 262 | c->radio.shortcuts[c->radio.nbuttons-1] = 'e'; |
| 263 | break; |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | /* |
| 269 | * RTF paste is Windows-specific. |
| 270 | */ |
| 271 | s = ctrl_getset(b, "Window/Selection", "format", |
| 272 | "Formatting of pasted characters"); |
| 273 | ctrl_checkbox(s, "Paste to clipboard in RTF as well as plain text", 'f', |
| 274 | HELPCTX(selection_rtf), |
| 275 | dlg_stdcheckbox_handler, I(offsetof(Config,rtf_paste))); |
| 276 | |
| 277 | /* |
| 278 | * Windows often has no middle button, so we supply a selection |
| 279 | * mode in which the more critical Paste action is available on |
| 280 | * the right button instead. |
| 281 | */ |
| 282 | s = ctrl_getset(b, "Window/Selection", "mouse", |
| 283 | "Control use of mouse"); |
| 284 | ctrl_radiobuttons(s, "Action of mouse buttons:", 'm', 1, |
| 285 | HELPCTX(selection_buttons), |
| 286 | dlg_stdradiobutton_handler, |
| 287 | I(offsetof(Config, mouse_is_xterm)), |
| 288 | "Windows (Middle extends, Right brings up menu)", I(2), |
| 289 | "Compromise (Middle extends, Right pastes)", I(0), |
| 290 | "xterm (Right extends, Middle pastes)", I(1), NULL); |
| 291 | /* |
| 292 | * This really ought to go at the _top_ of its box, not the |
| 293 | * bottom, so we'll just do some shuffling now we've set it |
| 294 | * up... |
| 295 | */ |
| 296 | c = s->ctrls[s->ncontrols-1]; /* this should be the new control */ |
| 297 | memmove(s->ctrls+1, s->ctrls, (s->ncontrols-1)*sizeof(union control *)); |
| 298 | s->ctrls[0] = c; |
| 299 | |
| 300 | /* |
| 301 | * Logical palettes don't even make sense anywhere except Windows. |
| 302 | */ |
| 303 | s = ctrl_getset(b, "Window/Colours", "general", |
| 304 | "General options for colour usage"); |
| 305 | ctrl_checkbox(s, "Attempt to use logical palettes", 'l', |
| 306 | HELPCTX(colours_logpal), |
| 307 | dlg_stdcheckbox_handler, I(offsetof(Config,try_palette))); |
| 308 | ctrl_checkbox(s, "Use system colours", 's', |
| 309 | HELPCTX(colours_system), |
| 310 | dlg_stdcheckbox_handler, I(offsetof(Config,system_colour))); |
| 311 | |
| 312 | |
| 313 | /* |
| 314 | * Resize-by-changing-font is a Windows insanity. |
| 315 | */ |
| 316 | s = ctrl_getset(b, "Window", "size", "Set the size of the window"); |
| 317 | ctrl_radiobuttons(s, "When window is resized:", 'z', 1, |
| 318 | HELPCTX(window_resize), |
| 319 | dlg_stdradiobutton_handler, |
| 320 | I(offsetof(Config, resize_action)), |
| 321 | "Change the number of rows and columns", I(RESIZE_TERM), |
| 322 | "Change the size of the font", I(RESIZE_FONT), |
| 323 | "Change font size only when maximised", I(RESIZE_EITHER), |
| 324 | "Forbid resizing completely", I(RESIZE_DISABLED), NULL); |
| 325 | |
| 326 | /* |
| 327 | * Most of the Window/Behaviour stuff is there to mimic Windows |
| 328 | * conventions which PuTTY can optionally disregard. Hence, |
| 329 | * most of these options are Windows-specific. |
| 330 | */ |
| 331 | s = ctrl_getset(b, "Window/Behaviour", "main", NULL); |
| 332 | ctrl_checkbox(s, "Window closes on ALT-F4", '4', |
| 333 | HELPCTX(behaviour_altf4), |
| 334 | dlg_stdcheckbox_handler, I(offsetof(Config,alt_f4))); |
| 335 | ctrl_checkbox(s, "System menu appears on ALT-Space", 'y', |
| 336 | HELPCTX(behaviour_altspace), |
| 337 | dlg_stdcheckbox_handler, I(offsetof(Config,alt_space))); |
| 338 | ctrl_checkbox(s, "System menu appears on ALT alone", 'l', |
| 339 | HELPCTX(behaviour_altonly), |
| 340 | dlg_stdcheckbox_handler, I(offsetof(Config,alt_only))); |
| 341 | ctrl_checkbox(s, "Ensure window is always on top", 'e', |
| 342 | HELPCTX(behaviour_alwaysontop), |
| 343 | dlg_stdcheckbox_handler, I(offsetof(Config,alwaysontop))); |
| 344 | ctrl_checkbox(s, "Full screen on Alt-Enter", 'f', |
| 345 | HELPCTX(behaviour_altenter), |
| 346 | dlg_stdcheckbox_handler, |
| 347 | I(offsetof(Config,fullscreenonaltenter))); |
| 348 | |
| 349 | /* |
| 350 | * Windows supports a local-command proxy. This also means we |
| 351 | * must adjust the text on the `Telnet command' control. |
| 352 | */ |
| 353 | if (!midsession) { |
| 354 | int i; |
| 355 | s = ctrl_getset(b, "Connection/Proxy", "basics", NULL); |
| 356 | for (i = 0; i < s->ncontrols; i++) { |
| 357 | c = s->ctrls[i]; |
| 358 | if (c->generic.type == CTRL_RADIO && |
| 359 | c->generic.context.i == offsetof(Config, proxy_type)) { |
| 360 | assert(c->generic.handler == dlg_stdradiobutton_handler); |
| 361 | c->radio.nbuttons++; |
| 362 | c->radio.buttons = |
| 363 | sresize(c->radio.buttons, c->radio.nbuttons, char *); |
| 364 | c->radio.buttons[c->radio.nbuttons-1] = |
| 365 | dupstr("Local"); |
| 366 | c->radio.buttondata = |
| 367 | sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); |
| 368 | c->radio.buttondata[c->radio.nbuttons-1] = I(PROXY_CMD); |
| 369 | break; |
| 370 | } |
| 371 | } |
| 372 | |
| 373 | for (i = 0; i < s->ncontrols; i++) { |
| 374 | c = s->ctrls[i]; |
| 375 | if (c->generic.type == CTRL_EDITBOX && |
| 376 | c->generic.context.i == |
| 377 | offsetof(Config, proxy_telnet_command)) { |
| 378 | assert(c->generic.handler == dlg_stdeditbox_handler); |
| 379 | sfree(c->generic.label); |
| 380 | c->generic.label = dupstr("Telnet command, or local" |
| 381 | " proxy command"); |
| 382 | break; |
| 383 | } |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | /* |
| 388 | * Serial back end is available on Windows. |
| 389 | */ |
| 390 | if (!midsession || (protocol == PROT_SERIAL)) |
| 391 | ser_setup_config_box(b, midsession, 0x1F, 0x0F); |
| 392 | |
| 393 | /* |
| 394 | * $XAUTHORITY is not reliable on Windows, so we provide a |
| 395 | * means to override it. |
| 396 | */ |
| 397 | if (!midsession && backend_from_proto(PROT_SSH)) { |
| 398 | s = ctrl_getset(b, "Connection/SSH/X11", "x11", "X11 forwarding"); |
| 399 | ctrl_filesel(s, "X authority file for local display", 't', |
| 400 | NULL, FALSE, "Select X authority file", |
| 401 | HELPCTX(ssh_tunnels_xauthority), |
| 402 | dlg_stdfilesel_handler, I(offsetof(Config, xauthfile))); |
| 403 | } |
| 404 | } |