AmendHub

Download

thecloud

/

SimpleSpooler

/

Logging.c

 

(View History)

thecloud   Simple Spooler 0.1 release Latest amendment: 1 on 2026-07-29

1 /*
2 * Logging.c
3 *
4 * Utility functions for managing a log window
5 * and writing formatted messages to the log.
6 *
7 * Written by: Ken McLeod, 2026-01-16
8 * Last update: 2026-01-31
9 *
10 * This software is provided as-is, with no warranties expressed or implied,
11 * under the terms of the BSD 2-Clause License. See separate "LICENSE" file.
12 */
13
14 #include <Types.h>
15 #include <Memory.h>
16 #include <CursorCtl.h>
17 #include <AppleTalk.h>
18 #include <Packages.h>
19 #include <Events.h>
20 #include <SysEqu.h>
21 #include <Controls.h>
22 #include <Desk.h>
23 #include <Devices.h>
24 #include <Errors.h>
25 #include <Fonts.h>
26 #include <Menus.h>
27 #include <ToolUtils.h>
28 #include <Events.h>
29 #include <Quickdraw.h>
30 #include <Resources.h>
31 #include <OSEvents.h>
32 #include <Scrap.h>
33 #include <StdIO.h>
34 #include <String.h>
35 #include "Logging.h"
36
37 extern char *PAPFunctionNames[];
38
39 WindowPtr TextWindow = 0L;
40 TEHandle TextTE = 0L;
41 ControlHandle TextScrollBar = 0L;
42 WindowRecord WRec;
43 Boolean WindowActive = false;
44 int gLogLevel = 0;
45
46
47 int LogLevel(void)
48 {
49 return gLogLevel;
50 }
51
52 void SetLogLevel(int newLevel)
53 {
54 gLogLevel = newLevel;
55 }
56
57 Boolean InitLogWindow(int logWindowID, WindowPtr *window,
58 TEHandle *textTE, ControlHandle *scrollBar)
59 {
60 Rect box, bounds;
61 TextWindow = GetNewWindow(logWindowID, &WRec, (WindowPtr) -1L);
62 *window = TextWindow;
63 if (!TextWindow) { return false; }
64 SetPort(TextWindow);
65 TextSize(9);
66 TextFont(1);
67 box = TextWindow->portRect;
68 box.left = 5;
69 box.bottom -= 16;
70 box.right -= 16;
71 bounds.top = box.top;
72 bounds.left = 5;
73 bounds.right = box.right;
74 bounds.bottom = 2000;
75 TextTE = TENew(&bounds,&box);
76 *textTE = TextTE;
77 if (!TextTE) { return(false); }
78 box = TextWindow->portRect;
79 box.top--;
80 box.right -= 15;
81 bounds.top = box.top - 1;
82 bounds.left = box.right;
83 bounds.right = bounds.left + 16;
84 bounds.bottom = box.bottom - 14;
85 TextScrollBar = NewControl(TextWindow,&bounds,"\p",1,1,1,1,scrollBarProc,(long)TextTE);
86 *scrollBar = TextScrollBar;
87 if (!TextScrollBar) { return false; }
88 BeginUpdate(TextWindow);
89 EndUpdate(TextWindow);
90 DrawControls(TextWindow);
91 DrawGrowIcon(TextWindow);
92 TEActivate(TextTE);
93 WindowActive = true;
94 AdjustTextScrollBar();
95
96 return true;
97 }
98
99 void UnloadLogWindow(void)
100 {
101 if (TextWindow) {
102 CloseWindow(TextWindow);
103 }
104 }
105
106 void UpdateLogWindow(void)
107 {
108 GrafPtr savePort;
109 Rect box;
110
111 GetPort(&savePort);
112 SetPort(TextWindow);
113 BeginUpdate(TextWindow);
114 box = TextWindow->portRect;
115 EraseRect(&box);
116 TEUpdate(&box,TextTE);
117 DrawControls(TextWindow);
118 EndUpdate(TextWindow);
119 DrawGrowIcon(TextWindow);
120 SetPort(savePort);
121 }
122
123 void ActivateLogWindow(Boolean active)
124 {
125 WindowActive = active;
126 if (WindowActive) {
127 TEActivate(TextTE);
128 } else {
129 TEDeactivate(TextTE);
130 }
131 HiliteControl(TextScrollBar,(WindowActive) ? 0 : 255);
132 }
133
134 void GrowLogWindow(short h, short v)
135 {
136 MoveControl(TextScrollBar,h - 15,0);
137 SizeControl(TextScrollBar,16,v - 15);
138 HLock((Handle)TextTE);
139 (**TextTE).viewRect.right = h - 15;
140 (**TextTE).viewRect.bottom = v - 15;
141 (**TextTE).destRect.right = h - 15;
142 HUnlock((Handle)TextTE);
143 TECalText(TextTE);
144 AdjustTextScrollBar();
145 }
146
147 void ShowLogWindow(Rect *r)
148 {
149 Rect newRect;
150 //%%% should constrain r to be within qd.screenBits.bounds here
151 MoveWindow(TextWindow, r->left, r->top, true);
152 SizeWindow(TextWindow, r->right - r->left, r->bottom - r->top, true);
153 newRect = ((GrafPtr)TextWindow)->portRect;
154 GrowLogWindow(newRect.right,newRect.bottom);
155 ShowWindow(TextWindow);
156 }
157
158 pascal void CtlAction(ControlHandle theControl, short part)
159 {
160 TEHandle TE;
161 short newValue, value, v, max;
162
163 TE = (TEHandle) GetCRefCon(theControl);
164 max = GetCtlMax(theControl);
165 HLock((Handle)TE);
166 v = (**TE).lineHeight;
167 HUnlock((Handle)TE);
168 newValue = value = GetCtlValue(theControl);
169 if (part == inPageUp) {
170 newValue -= 12;
171 if (newValue < 1)
172 newValue = 1;
173 SetCtlValue(theControl,newValue);
174 }
175 if (part == inPageDown) {
176 newValue += 12;
177 if (newValue > max)
178 newValue = max;
179 SetCtlValue(theControl,newValue);
180 }
181 if (part == inUpButton) {
182 newValue--;
183 if (newValue < 1)
184 newValue = 1;
185 SetCtlValue(theControl,newValue);
186 }
187 if (part == inDownButton) {
188 newValue++;
189 if (newValue > max)
190 newValue = max;
191 SetCtlValue(theControl,newValue);
192 }
193 if (value != newValue)
194 TEScroll(0,(value - newValue) * v,TE);
195 }
196
197 void HandleMouseInText(EventRecord *myEvent)
198 {
199 short where;
200 ControlHandle whichControl;
201 short value, oldValue;
202 short v;
203 Rect box;
204
205 SetPort(TextWindow);
206 GlobalToLocal(&myEvent->where);
207 where = FindControl(myEvent->where,TextWindow,&whichControl);
208 switch (where) {
209 case inUpButton:
210 case inDownButton:
211 case inPageUp:
212 case inPageDown:
213 TrackControl(whichControl,myEvent->where,(ProcPtr)CtlAction);
214 break;
215 case inThumb:
216 HLock((Handle)TextTE);
217 v = (**(TextTE)).lineHeight;
218 HUnlock((Handle)TextTE);
219 oldValue = GetCtlValue(whichControl);
220 if (TrackControl(whichControl,myEvent->where,0L)) {
221 value = GetCtlValue(whichControl);
222 if (value != oldValue) {
223 TEScroll(0,(oldValue - value) * v,TextTE);
224 }
225 }
226 break;
227 case 0:
228 HLock((Handle)TextTE);
229 box = (**(TextTE)).viewRect;
230 HUnlock((Handle)TextTE);
231 if (PtInRect(myEvent->where,&box))
232 TEClick(myEvent->where,(myEvent->modifiers & shiftKey),TextTE);
233 break;
234 default:
235 break;
236 }
237 }
238
239 void AdjustTextScrollBar(void)
240 {
241 short lines;
242 short v;
243 short top, bottom;
244
245 HLock((Handle)TextTE);
246 lines = (**TextTE).nLines;
247 v = (**TextTE).lineHeight;
248 top = (**TextTE).viewRect.top;
249 bottom = (**TextTE).viewRect.bottom;
250 HUnlock((Handle)TextTE);
251 bottom -= top + 10;
252 lines -= bottom / v;
253 if (lines < 1)
254 lines = 1;
255 if (lines > 1)
256 lines++;
257 SetCtlMax(TextScrollBar,lines);
258 if (lines > 1 && WindowActive)
259 HiliteControl(TextScrollBar,0);
260 else
261 HiliteControl(TextScrollBar,255);
262 }
263
264 void Message(int level, char *mess)
265 {
266 long len;
267 GrafPtr savePort;
268 char st[40];
269 unsigned long t;
270 int index, line;
271
272 if ((level & 0x7FFF) > gLogLevel) {
273 return;
274 }
275 /* always insert new text at end */
276 HLock((Handle)TextTE);
277 index = (**TextTE).teLength;
278 HUnlock((Handle)TextTE);
279 TESetSelect(index,index,TextTE);
280
281 GetDateTime(&t);
282 GetPort(&savePort);
283 SetPort(TextWindow);
284 IUTimeString((long) t, true,st);
285 while (st[0] < 14) {
286 st[0]++;
287 st[st[0]] = ' ';
288 }
289 len = st[0];
290 TEInsert(&st[1],len,TextTE);
291 IUDateString((long) t, shortDate ,st);
292 while (st[0] < 14) {
293 st[0]++;
294 st[st[0]] = ' ';
295 }
296 len = st[0];
297 TEInsert(&st[1],len,TextTE);
298 /* if the high bit of level is set, add a log level prefix string */
299 if (level & 0x8000) {
300 switch (level & 0x7FFF) {
301 case 0: BlockMove("\pERROR: ",st,8); break;
302 case 1: BlockMove("\pWARNING: ",st,10); break;
303 case 2: BlockMove("\pINFO: ",st,7); break;
304 case 3: BlockMove("\pDEBUG: ",st,8); break;
305 default: st[0] = 0; break;
306 }
307 len = st[0];
308 TEInsert(&st[1],len,TextTE);
309 }
310 len = (long)mess[0] & 0x000000FF;
311 TEInsert(&mess[1],len,TextTE);
312 TEKey(0x0D,TextTE); /* add a carriage return */
313 TECalText(TextTE);
314
315 /* Eventually we will run out of memory if we keep adding text,
316 * so after we reach a hardcoded limit, remove a chunk of lines
317 * from the top of the text edit record to make room for new text.
318 * %%% TBD: limit should be calculated dynamically based on free memory
319 */
320 index = 0;
321 HLock((Handle)TextTE);
322 len = (**TextTE).teLength;
323 if (len > 16384) {
324 line = (**TextTE).nLines / 4; /* cut first 25% of the lines */
325 index = (**TextTE).lineStarts[line-1];
326 TESetSelect(0,index,TextTE);
327 TEDelete(TextTE);
328 index = (**TextTE).teLength;
329 TESetSelect(index,index,TextTE);
330 TEScroll(0,(**TextTE).lineHeight * (line-2),TextTE);
331 }
332 HUnlock((Handle)TextTE);
333 AdjustTextScrollBar();
334 CtlAction(TextScrollBar,inDownButton); /* adjust scroll down one line */
335 SetPort(savePort);
336 }
337
338 void MessageWithIntValue(int level, char *mess, long int value)
339 {
340 if ((level & 0x7FFF) > gLogLevel) {
341 return;
342 } else {
343 int len, vlen;
344 char val[16];
345 char tmp[256];
346
347 BlockMove(mess, tmp, mess[0]+1);
348 tmp[tmp[0]+1] = ' ';
349 tmp[0] = tmp[0]+1;
350 sprintf(val, "%ld", value);
351 vlen = (int)(strlen(val) & 0xFF);
352 len = 255 - (1 + mess[0] + 1);
353 if (vlen < len) { len = vlen; }
354 BlockMove(val, &tmp[tmp[0]+1], len);
355 tmp[0] = tmp[0]+len;
356 Message(level,tmp);
357 }
358 }
359
360 void MessageWithQuotedString(int level, char *mess, char *str)
361 {
362 if ((level & 0x7FFF) > gLogLevel) {
363 return;
364 } else {
365 int len;
366 char tmp[256];
367
368 BlockMove(mess, tmp, mess[0]+1);
369 tmp[0] = mess[0];
370 len = 255 - (1 + mess[0] + 1 + 1 + 1);
371 if (str[0] < len) { len = str[0]; }
372 BlockMove(" “",&tmp[tmp[0]+1],2);
373 tmp[0] += 2;
374 BlockMove(&str[1], &tmp[tmp[0]+1], len);
375 tmp[0] += len;
376 BlockMove("”", &tmp[tmp[0]+1], 1);
377 tmp[0] += 1;
378 Message(level,tmp);
379 }
380 }
381
382 void MessageWithDataPointer(int level, char *mess, char *data, int dataLen)
383 {
384 if ((level & 0x7FFF) > gLogLevel) {
385 return;
386 } else {
387 int index, len;
388 char tmp[256];
389
390 BlockMove(mess, tmp, mess[0]+1);
391 tmp[tmp[0]+1] = ' ';
392 tmp[0] = tmp[0]+1;
393 len = 255 - (1 + mess[0] + 1);
394 if (dataLen < len) { len = dataLen; }
395 BlockMove(data, &tmp[tmp[0]+1], len);
396 tmp[0] = tmp[0]+len;
397 for (index = mess[0]+2; index < tmp[0]+1; index++) {
398 if (tmp[index] < 0x20 || tmp[index] > 0x7E) {
399 tmp[index] = '.'; /* change non-printing ascii chars to a dot */
400 }
401 }
402 Message(level,tmp);
403 }
404 }
405
406 void MessageWithNetAddr(int level, char *mess, short net, char node, char socket)
407 {
408 if ((level & 0x7FFF) > gLogLevel) {
409 return;
410 } else {
411 int len;
412 char val[10];
413 char tmp[256];
414
415 len = 255 - (1 + 1 + 3 + 1 + 3 + 1 + 3);
416 if (mess[0] < len) { len = mess[0]; }
417 BlockMove(mess, tmp, len+1);
418 tmp[tmp[0]+1] = ' ';
419 tmp[0] = tmp[0]+1;
420 sprintf(val, "%u", (unsigned int)net);
421 len = (int)(strlen(val) & 0xFFFF);
422 BlockMove(val, &tmp[tmp[0]+1], len);
423 tmp[0] = tmp[0]+len;
424 tmp[tmp[0]+1] = '.';
425 tmp[0] = tmp[0]+1;
426 sprintf(val, "%d", (int)node & 0xFF);
427 len = (int)(strlen(val) & 0xFF);
428 BlockMove(val, &tmp[tmp[0]+1], len);
429 tmp[0] = tmp[0]+len;
430 tmp[tmp[0]+1] = ':';
431 tmp[0] = tmp[0]+1;
432 sprintf(val, "%d", (int)socket & 0xFF);
433 len = (int)(strlen(val) & 0xFF);
434 BlockMove(val, &tmp[tmp[0]+1], len);
435 tmp[0] = tmp[0]+len;
436 Message(level,tmp);
437 }
438 }
439
440 void MessageReceivedFromNetAddr(int level, int funcID, Boolean isResp, short net, char node, char socket)
441 {
442 if ((level & 0x7FFF) > gLogLevel) {
443 return;
444 } else {
445 int len;
446 char tmp[256];
447
448 BlockMove("\pGot ", tmp, 10L);
449 len = (int)(strlen(PAPFunctionNames[funcID]) & 0xFFFF);
450 BlockMove(&PAPFunctionNames[funcID][0], &tmp[tmp[0]+1], len);
451 tmp[0] = tmp[0]+len;
452 len = (isResp) ? 14 : 13;
453 BlockMove((isResp) ? " response from" : " request from", &tmp[tmp[0]+1], len);
454 tmp[0] = tmp[0]+len;
455 MessageWithNetAddr(level,tmp, net, node, socket);
456 }
457 }
458
459 void MessageSentToNetAddr(int level, int funcID, Boolean isResp, short net, char node, char socket)
460 {
461 if ((level & 0x7FFF) > gLogLevel) {
462 return;
463 } else {
464 int len;
465 char tmp[256];
466
467 BlockMove("\pSent ", tmp, 6L);
468 len = (int)(strlen(PAPFunctionNames[funcID]) & 0xFFFF);
469 BlockMove(&PAPFunctionNames[funcID][0], &tmp[tmp[0]+1], len);
470 tmp[0] = tmp[0]+len;
471 len = (isResp) ? 12 : 11;
472 BlockMove((isResp) ? " response to" : " request to", &tmp[tmp[0]+1], len);
473 tmp[0] = tmp[0]+len;
474 MessageWithNetAddr(level,tmp, net, node, socket);
475 }
476 }