1 /*
2 * Copyright (c) 2016-2020 Paul Mattes.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Paul Mattes, nor his contributors may be used to
14 * endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20 * EVENT SHALL PAUL MATTES, JEFF SPARKES OR GTRC BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * model.c
31 * Support for changing model and oversize at run-time.
32 */
33
34 #include "globals.h"
35 #include "appres.h"
36 #include "resources.h"
37
38 #include "ctlrc.h"
39 #include "popups.h"
40 #include "screen.h"
41 #include "telnet.h"
42 #include "toggles.h"
43 #include "utils.h"
44
45 #include "model.h"
46
47 static char *pending_model;
48 static char *pending_oversize;
49
50 /*
51 * Canonical representation of the model, returning color and extended
52 * state.
53 */
54 static char *
canonical_model_x(const char * res,int * model,bool * is_color,bool * is_extended)55 canonical_model_x(const char *res, int *model, bool *is_color,
56 bool *is_extended)
57 {
58 size_t sl;
59 char *digitp = NULL;
60 char *colorp = "9";
61 bool extended = false;
62
63 if (res == NULL) {
64 return NULL;
65 }
66 sl = strlen(res);
67 if ((sl != 1 && sl != 6 && sl != 8) ||
68 (sl == 1 &&
69 (digitp = strchr("2345", res[0])) == NULL) ||
70 (((sl == 6) || (sl == 8)) &&
71 (strncmp(res, "327", 3) ||
72 (colorp = strchr("89", res[3])) == NULL ||
73 res[4] != '-' ||
74 (digitp = strchr("2345", res[5])) == NULL)) ||
75 ((sl == 8) &&
76 (res[6] != '-' || strchr("Ee", res[7]) == NULL))) {
77 return NULL;
78 }
79 if (sl == 1 || sl == 8) {
80 extended = true;
81 }
82 *model = *digitp - '0';
83 *is_color = (*colorp == '9');
84 *is_extended = extended;
85 return xs_buffer("327%c-%c%s", *colorp, *digitp, extended? "-E": "");
86 }
87
88 /*
89 * Canonical representation of the model.
90 */
91 static char *
canonical_model(const char * res)92 canonical_model(const char *res)
93 {
94 int model;
95 bool color, extended;
96
97 return canonical_model_x(res, &model, &color, &extended);
98 }
99
100 /*
101 * Toggle the model.
102 */
103 static bool
toggle_model(const char * name _is_unused,const char * value)104 toggle_model(const char *name _is_unused, const char *value)
105 {
106 if (!model_can_change()) {
107 popup_an_error("Cannot change " ResModel);
108 return false;
109 }
110
111 Replace(pending_model, *value? NewString(value): NULL);
112 return true;
113 }
114
115 /*
116 * Toggle oversize.
117 */
118 static bool
toggle_oversize(const char * name _is_unused,const char * value)119 toggle_oversize(const char *name _is_unused, const char *value)
120 {
121 if (!model_can_change()) {
122 popup_an_error("Cannot change " ResOversize);
123 return false;
124 }
125
126 Replace(pending_oversize, NewString(value));
127 return true;
128 }
129
130 /*
131 * Done function for changing the model and oversize.
132 */
133 static bool
toggle_model_done(bool success)134 toggle_model_done(bool success)
135 {
136 unsigned ovr = 0, ovc = 0;
137 int model_number = model_num;
138 bool is_color = mode.m3279;
139 bool is_extended = mode.extended;
140 struct {
141 int model_num;
142 int rows;
143 int cols;
144 int ov_cols;
145 int ov_rows;
146 bool m3279;
147 bool alt;
148 bool extended;
149 } old;
150 bool oversize_was_pending = (pending_oversize != NULL);
151 bool res = true;
152
153 if (!success ||
154 (pending_model == NULL &&
155 pending_oversize == NULL)) {
156 goto done;
157 }
158
159 if (PCONNECTED) {
160 popup_an_error("Cannot change %s or %s while connected",
161 ResModel, ResOversize);
162 goto fail;
163 }
164
165 if (pending_model != NULL && !strcmp(pending_model, appres.model)) {
166 Replace(pending_model, NULL);
167 }
168 if (pending_oversize != NULL &&
169 appres.oversize != NULL &&
170 !strcmp(pending_oversize, appres.oversize)) {
171 Replace(pending_oversize, NULL);
172 }
173 if (pending_model == NULL && pending_oversize == NULL) {
174 goto done;
175 }
176
177 /* Reconcile simultaneous changes. */
178 if (pending_model != NULL) {
179 char *canon = canonical_model_x(pending_model, &model_number,
180 &is_color, &is_extended);
181
182 if (canon == NULL) {
183 popup_an_error("%s value must be 327{89}-{2345}[-E]",
184 ResModel);
185 goto fail;
186 }
187
188 Replace(pending_model, canon);
189 }
190
191 if (!is_extended) {
192 /* Without extended, no oversize. */
193 Replace(pending_oversize, NewString(""));
194 }
195
196 if (pending_oversize != NULL) {
197 if (*pending_oversize) {
198 char x, junk;
199 if (sscanf(pending_oversize, "%u%c%u%c", &ovc, &x, &ovr, &junk) != 3
200 || x != 'x') {
201 popup_an_error("%s value must be <cols>x<rows>",
202 ResOversize);
203 goto fail;
204 }
205 } else {
206 ovc = 0;
207 ovr = 0;
208 }
209 } else {
210 ovc = ov_cols;
211 ovr = ov_rows;
212 }
213
214 /* Save the current settings. */
215 old.model_num = model_num;
216 old.rows = ROWS;
217 old.cols = COLS;
218 old.ov_rows = ov_rows;
219 old.ov_cols = ov_cols;
220 old.m3279 = mode.m3279;
221 old.alt = screen_alt;
222 old.extended = mode.extended;
223
224 /* Change settings. */
225 mode.m3279 = is_color;
226 mode.extended = is_extended;
227 set_rows_cols(model_number, ovc, ovr);
228
229 if (model_num != model_number ||
230 ov_rows != (int)ovr ||
231 ov_cols != (int)ovc) {
232 /* Failed. Restore the old settings. */
233 mode.m3279 = old.m3279;
234 set_rows_cols(old.model_num, old.ov_cols, old.ov_rows);
235 ROWS = old.rows;
236 COLS = old.cols;
237 screen_alt = old.alt;
238 mode.extended = old.extended;
239 return false;
240 }
241
242 ROWS = maxROWS;
243 COLS = maxCOLS;
244 ctlr_reinit(MODEL_CHANGE);
245
246 /* Reset the screen state. */
247 screen_init();
248 ctlr_erase(true);
249
250 /* Report the new terminal name. */
251 if (appres.termname == NULL) {
252 st_changed(ST_TERMINAL_NAME, appres.termname != NULL);
253 }
254
255 if (pending_model != NULL) {
256 Replace(appres.model, pending_model);
257 }
258 pending_model = NULL;
259 if (pending_oversize != NULL) {
260 if (*pending_oversize) {
261 Replace(appres.oversize, pending_oversize);
262 pending_oversize = NULL;
263 } else {
264 bool force = !oversize_was_pending && appres.oversize != NULL;
265
266 Replace(appres.oversize, NULL);
267 if (force) {
268 /* Turning off extended killed oversize. */
269 force_toggle_notify(ResOversize, IA_NONE);
270 }
271 }
272 }
273
274 goto done;
275
276 fail:
277 res = false;
278
279 done:
280 Replace(pending_model, NULL);
281 Replace(pending_oversize, NULL);
282 return res;
283 }
284
285 /*
286 * Terminal name toggle.
287 */
288 static bool
toggle_terminal_name(const char * name _is_unused,const char * value)289 toggle_terminal_name(const char *name _is_unused, const char *value)
290 {
291 if (PCONNECTED) {
292 popup_an_error("%s cannot change while connected",
293 ResTermName);
294 return false;
295 }
296
297 appres.termname = clean_termname(*value? value: NULL);
298 net_set_default_termtype();
299 return true;
300 }
301
302 /*
303 * Toggle the NOP interval.
304 */
305 static bool
toggle_nop_seconds(const char * name _is_unused,const char * value)306 toggle_nop_seconds(const char *name _is_unused, const char *value)
307 {
308 unsigned long l;
309 char *end;
310 int secs;
311
312 if (!*value) {
313 appres.nop_seconds = 0;
314 net_nop_seconds();
315 return true;
316 }
317
318 l = strtoul(value, &end, 10);
319 secs = (int)l;
320 if (*end != '\0' || (unsigned long)secs != l || secs < 0) {
321 popup_an_error("Invalid %s value", ResNopSeconds);
322 return false;
323 }
324 appres.nop_seconds = secs;
325 net_nop_seconds();
326 return true;
327 }
328
329 /**
330 * Module registration.
331 */
332 void
model_register(void)333 model_register(void)
334 {
335 /* Register the toggles. */
336 register_extended_toggle(ResModel, toggle_model, toggle_model_done,
337 canonical_model, (void **)&appres.model, XRM_STRING);
338 register_extended_toggle(ResOversize, toggle_oversize, toggle_model_done,
339 NULL, (void **)&appres.oversize, XRM_STRING);
340 register_extended_toggle(ResTermName, toggle_terminal_name, NULL, NULL,
341 (void **)&appres.termname, XRM_STRING);
342 register_extended_toggle(ResNopSeconds, toggle_nop_seconds, NULL,
343 NULL, (void **)&appres.nop_seconds, XRM_INT);
344 }
345