AmendHub

Download

vkoskiv

/

MacNTP

/

cdev.c

 

(View History)

vkoskiv   Display MacNTP errors in cdev Latest amendment: 19 on 2023-09-20

1 /*
2 * Copyright (c) 2023 Valtteri Koskivuori <vkoskiv@gmail.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "rsrcid.h"
18 #include <OSUtils.h>
19 #include <GestaltEqu.h>
20
21 /*
22 A basic cdev skeleton + implementation in pure C
23 For some reason, Symantec decided to embrace OOP for all their
24 cdev examples, and my copy didn't ship with the needed libraries
25 to build with those language extensions, so I wrote this C impl from
26 scratch.
27 It works, there are many bugs, but it does the job.
28 */
29
30 #define ntp1_box_id 1
31 #define ntp2_box_id 2
32 #define utc_box_id 3
33 #define err_title_id 9
34 #define err_box_id 10
35 #define apply_btn_id 4
36
37 struct control {
38 short type;
39 Handle handle;
40 Rect rect;
41 };
42
43 struct state {
44 StringHandle ntp1;
45 StringHandle ntp2;
46 StringHandle utc;
47 StringHandle err;
48 DialogPtr dialog;
49 struct control ntp1_textbox;
50 struct control ntp2_textbox;
51 struct control utc_textbox;
52 struct control err_textbox;
53 struct control err_title;
54 };
55
56 // Prototypes
57 void got_cdev_error(void);
58 void got_memory_error(void);
59 void got_resource_error(void);
60
61 int is_number(char c);
62 int validate_ntp_url(Str255 utc_str);
63 int validate_utc(Str255 utc_str);
64
65 int utcstr_to_mins(Str255 utc, long *out);
66
67 void *got_initDev(void *storage, DialogPtr dialog, short nitems);
68 void *got_hitDev(void *storage, short item);
69 void *got_closeDev(void *storage, short item);
70 void *got_nulDev(void *storage);
71 void *got_updateDev(void *storage);
72 void *got_activDev(void *storage);
73 void *got_deActivDev(void *storage);
74 void *got_keyEvtDev(void *storage, EventRecord *event, short item, short nitems, DialogPtr d);
75 void *got_keyEvent(void *storage, unsigned char c);
76 void *got_cmdKeyEvent(void *storage, unsigned char c, short item, short nitems, DialogPtr d);
77 void *got_macDev(void *storage);
78 void *got_undoDev(void *storage);
79 void *got_cutDev(void *storage);
80 void *got_copyDev(void *storage);
81 void *got_pasteDev(void *storage);
82 void *got_clearDev(void *storage);
83
84 // Impls
85
86 void got_cdev_error(void) {
87 SysBeep(30);
88 SysBeep(30);
89 }
90 void got_memory_error(void) {
91 SysBeep(30);
92 SysBeep(30);
93 }
94 void got_resource_error(void) {
95 SysBeep(30);
96 SysBeep(30);
97 }
98
99 void *got_initDev(void *storage, DialogPtr dialog, short nitems) {
100 struct state *s;
101 storage = NewHandle(sizeof(struct state));
102 if (!storage) {
103 SysBeep(1);
104 SysBeep(1);
105 SysBeep(1);
106 }
107 s = *(struct state **)storage;
108 s->ntp1 = GetString(NTP1_STR_ID);
109 s->ntp2 = GetString(NTP2_STR_ID);
110 s->utc = GetString(UTC_STR_ID);
111 s->err = GetString(ERR_STR_ID);
112 s->dialog = dialog;
113 if (!s->ntp1 || !s->ntp2 || !s->utc || !s->err) {
114 SysBeep(1);
115 SysBeep(1);
116 SysBeep(1);
117 }
118 SetHandleSize(s->ntp1, 256);
119 SetHandleSize(s->ntp2, 256);
120 SetHandleSize(s->utc, 256);
121 SetHandleSize(s->err, 256);
122 HLock(s->ntp1);
123 HLock(s->ntp2);
124 HLock(s->utc);
125 HLock(s->err);
126 // Grab our textboxes. The number is the one in our DITL
127 // but the dialog manager wants the full range num, so add nitems
128 GetDItem(s->dialog,
129 ntp1_box_id + nitems,
130 &s->ntp1_textbox.type,
131 &s->ntp1_textbox.handle,
132 &s->ntp1_textbox.rect);
133 HLock(s->ntp1_textbox.handle);
134 SetIText(s->ntp1_textbox.handle, *s->ntp1);
135 ShowDItem(s->dialog, ntp1_box_id);
136 InvalRect(&s->ntp1_textbox.rect);
137 SelIText(s->dialog, ntp1_box_id + nitems, 0, 32767);
138
139 GetDItem(s->dialog,
140 ntp2_box_id + nitems,
141 &s->ntp2_textbox.type,
142 &s->ntp2_textbox.handle,
143 &s->ntp2_textbox.rect);
144 HLock(s->ntp2_textbox.handle);
145 SetIText(s->ntp2_textbox.handle, *s->ntp2);
146 ShowDItem(s->dialog, ntp2_box_id);
147 InvalRect(&s->ntp2_textbox.rect);
148
149 GetDItem(s->dialog,
150 utc_box_id + nitems,
151 &s->utc_textbox.type,
152 &s->utc_textbox.handle,
153 &s->utc_textbox.rect);
154 HLock(s->utc_textbox.handle);
155 SetIText(s->utc_textbox.handle, *s->utc);
156 ShowDItem(s->dialog, utc_box_id);
157 InvalRect(&s->utc_textbox.rect);
158
159 GetDItem(s->dialog,
160 err_box_id + nitems,
161 &s->err_textbox.type,
162 &s->err_textbox.handle,
163 &s->err_textbox.rect);
164 HLock(s->err_textbox.handle);
165
166 GetDItem(s->dialog,
167 err_title_id + nitems,
168 &s->err_title.type,
169 &s->err_title.handle,
170 &s->err_title.rect);
171 HLock(s->err_title.handle);
172
173 if (*s->err[0]) {
174 SetIText(s->err_textbox.handle, *s->err);
175 ShowDItem(s->dialog, err_box_id);
176 InvalRect(&s->err_textbox.rect);
177 } else {
178 HideDItem(s->dialog, err_box_id + nitems);
179 HideDItem(s->dialog, err_title_id + nitems);
180 }
181
182 return storage;
183 }
184
185 int validate_ntp_url(Str255 utc_str) {
186 // TODO
187 return true;
188 }
189
190 int is_number(char c) {
191 return c >> 4 == 3 && (c & 0x0F) < 0xA;
192 }
193
194 int validate_utc(Str255 utc_str) {
195 // Str255 is a pointer to a pascal string, where
196 // the first byte is the string length.
197 char scratch[6];
198 short strlen;
199 char *str;
200 strlen = utc_str[0];
201 str = (char *)&utc_str[1];
202 if (strlen != 6) return false;
203 if (str[0] != '+' && str[0] != '-') return false;
204 if (!is_number(str[1]) || !is_number(str[2])) return false;
205 if (str[3] != ':') return false;
206 if (!is_number(str[4]) || !is_number(str[5])) return false;
207
208 return true;
209 }
210
211 int utcstr_to_mins(Str255 utc, long *out) {
212 long minutes;
213 if (!validate_utc(utc)) return false;
214 minutes = 0;
215 minutes += ((utc[2] - 0x30) * 10) * 60;
216 minutes += (utc[3] - 0x30) * 60;
217 minutes += (utc[5] - 0x30) * 10;
218 minutes += (utc[6] - 0x30);
219 if (utc[1] == '-')
220 minutes = -minutes;
221 if (out) *out = minutes;
222 return true;
223 }
224
225 void *got_hitDev(void *storage, short item) {
226 Str255 ntp1_text;
227 Str255 ntp2_text;
228 Str255 utc_text;
229 long old_utc_offset;
230 long new_utc_offset;
231 long diff_secs;
232 unsigned long systime;
233 int updated;
234 struct state *s = *(struct state **)storage;
235
236 updated = false;
237 if (item == apply_btn_id) {
238 GetIText(s->ntp1_textbox.handle, ntp1_text);
239 GetIText(s->ntp2_textbox.handle, ntp2_text);
240 GetIText(s->utc_textbox.handle, utc_text);
241 if (!validate_ntp_url(ntp1_text)) return storage;
242 if (!validate_ntp_url(ntp2_text)) return storage;
243 if (!validate_utc(utc_text)) {
244 SysBeep(1);
245 SysBeep(1);
246 return storage;
247 }
248 // All good to go, let's store these settings.
249 /*
250 Steps:
251 - Compare and update NTP timeservers, if need be.
252 - Get current UTC offset from rsrc
253 - Compute new UTC offset from text input
254 - If the value has changed, compute the offset and update
255 system clock.
256 */
257
258 if (!EqualString(s->ntp1, ntp1_text, true, true)) {
259 SetString(s->ntp1, ntp1_text);
260 ChangedResource(s->ntp1);
261 ReleaseResource(s->ntp1);
262 updated = true;
263 }
264
265 if (!EqualString(s->ntp2, ntp2_text, true, true)) {
266 SetString(s->ntp2, ntp2_text);
267 ChangedResource(s->ntp2);
268 ReleaseResource(s->ntp2);
269 updated = true;
270 }
271
272 // Update the utc offset, and tweak system clock if need be
273 if (!EqualString(s->utc, utc_text, true, true)) {
274 if (!utcstr_to_mins(utc_text, &new_utc_offset)) return storage;
275 if (!utcstr_to_mins(*s->utc, &old_utc_offset)) return storage;
276 SetString(s->utc, utc_text);
277 ChangedResource(s->utc);
278 ReleaseResource(s->utc);
279 updated = true;
280 diff_secs = (new_utc_offset - old_utc_offset) * 60;
281 ReadDateTime((unsigned long *)&systime);
282 systime += diff_secs;
283 SetDateTime(systime);
284 }
285
286 if (updated) {
287 UpdateResFile(CurResFile());
288 if (ResError()) {
289 //DebugStr("\pUpdateResFile failed");
290 }
291 }
292 }
293 return storage;
294 }
295 void *got_closeDev(void *storage, short item) {
296 struct state *s = *(struct state **)storage;
297 HideDItem(s->dialog, ntp1_box_id);
298 HideDItem(s->dialog, ntp2_box_id);
299 HideDItem(s->dialog, utc_box_id);
300 DisposHandle(s->ntp1);
301 DisposHandle(s->ntp2);
302 DisposHandle(s->utc);
303 DisposHandle(s->err);
304 DisposHandle(storage);
305 return storage;
306 }
307 void *got_nulDev(void *storage) {
308 return storage;
309 }
310 void *got_updateDev(void *storage) {
311 struct state *s = *(struct state **)storage;
312 return storage;
313 }
314 void *got_activDev(void *storage) {
315 return storage;
316 }
317 void *got_deActivDev(void *storage) {
318 return storage;
319 }
320 void *got_keyEvtDev(void *storage, EventRecord *event, short item, short nitems, DialogPtr d) {
321 if (!(event->modifiers & cmdKey))
322 return got_keyEvent(storage, (unsigned char)event->message);
323 else if (event->message != autoKey) {
324 event->what = nullEvent;
325 return got_cmdKeyEvent(storage, (unsigned char)event->message, item, nitems, d);
326 }
327 //FIXME: Can we reach this even?
328 SysBeep(30);
329 return storage;
330 }
331
332 void *got_keyEvent(void *storage, unsigned char c) {
333 return storage;
334 }
335
336 // I guess we can get actual undoDev, cutDev, etc
337 // events from the system, but we want to handle
338 // the key events as well. I assume that the
339 // system events are from the menu, and key events
340 // come from the keyboard.
341 // I guess Apple just did this for compatibility with
342 // existing cdevs, so I'll just patch these in here.
343 void *got_cmdKeyEvent(void *storage, unsigned char c, short item, short nitems, DialogPtr d) {
344 struct state *s = *(struct state **)storage;
345 switch (c) {
346 case 'z': case 'Z':
347 return got_undoDev(storage);
348 case 'x': case 'X':
349 return got_cutDev(storage);
350 case 'c': case 'C':
351 return got_copyDev(storage);
352 case 'v': case 'V':
353 return got_pasteDev(storage);
354 case 'a': case 'A':
355 //GetDItem(d, item - nitems, NULL, NULL, NULL);
356 // I tried a few permutations, but this call crashes the system every
357 // time and I can't be bothered to figure out why.
358 //SelIText(d, item - nitems, 0, 32767);
359 return storage;
360 }
361 SysBeep(30);
362 return storage;
363 }
364
365 void *got_macDev(void *storage) {
366 long answer;
367 OSErr gestalt_err;
368 SysBeep(1);
369 gestalt_err = Gestalt(gestaltVersion, &answer);
370 if (gestalt_err) {
371 // Gestalt not available, it was introduced in 6.0.4
372 // And MacTCP in 6.0.3, so we're just going to assume
373 // the user has at least 6.0.8
374 return (void *)0;
375 }
376
377 if (!Gestalt('mtcp', nil)) {
378 // Good, MacTCP is available.
379 return (void *)1;
380 }
381 return (void *)0;
382 }
383 void *got_undoDev(void *storage) {
384 return storage;
385 }
386 void *got_cutDev(void *storage) {
387 struct state *s = *(struct state **)storage;
388 DlgCut(s->dialog);
389 return storage;
390 }
391 void *got_copyDev(void *storage) {
392 struct state *s = *(struct state **)storage;
393 DlgCopy(s->dialog);
394 return storage;
395 }
396 void *got_pasteDev(void *storage) {
397 struct state *s = *(struct state **)storage;
398 DlgPaste(s->dialog);
399 return storage;
400 }
401 void *got_clearDev(void *storage) {
402 return storage;
403 }
404
405 pascal void *main(
406 short msg,
407 short item,
408 short nitems,
409 short dialog_id,
410 EventRecord *event,
411 void *storage,
412 DialogPtr dp) {
413
414 // Check for errors. We have to return the error value to ACK, I guess.
415 switch ((long)storage) {
416 case -1: // cdev error
417 got_cdev_error();
418 return storage;
419 case 0: // memory error
420 got_memory_error();
421 return storage;
422 case 1: // resource error
423 got_resource_error();
424 return storage;
425 }
426
427 // respond to message
428 switch (msg) {
429 case initDev: return got_initDev(storage, dp, nitems);
430 case hitDev: return got_hitDev(storage, item - nitems);
431 case closeDev: return got_closeDev(storage, item - nitems);
432 case nulDev: return got_nulDev(storage);
433 case updateDev: return got_updateDev(storage);
434 case activDev: return got_activDev(storage);
435 case deactivDev: return got_deActivDev(storage);
436 case keyEvtDev: return got_keyEvtDev(storage, event, item, nitems, dp);
437 case macDev: return got_macDev(storage);
438 case undoDev: return got_undoDev(storage);
439 case cutDev: return got_cutDev(storage);
440 case copyDev: return got_copyDev(storage);
441 case pasteDev: return got_pasteDev(storage);
442 case clearDev: return got_clearDev(storage);
443 }
444 return 0;
445 }