AmendHub

Download

jcs

/

subtext

/

logger.c

 

(View History)

jcs   logger: Shorten 'No one connected' to '0 connected' Latest amendment: 475 on 2023-04-10

1 /*
2 * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
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 <stdio.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include "focusable.h"
21 #include "logger.h"
22 #include "subtext.h"
23 #include "user.h"
24 #include "util.h"
25
26 struct logger *logger = NULL;
27
28 void logger_layout(Rect *init_bounds);
29 void logger_key_down(struct focusable *focusable, EventRecord *event);
30 void logger_mouse_down(struct focusable *focusable, EventRecord *event);
31 void logger_resize(struct focusable *focusable, EventRecord *event);
32 void logger_update(struct focusable *focusable, EventRecord *event);
33 void logger_resume(struct focusable *focusable, EventRecord *event);
34 bool logger_quit(struct focusable *focusable);
35 void logger_flush_buffer(bool force);
36
37 void
38 logger_init(void)
39 {
40 struct focusable *focusable;
41 Rect bounds = { 0 };
42 short padding = 5;
43
44 logger = xmalloczero(sizeof(struct logger));
45 if (!logger)
46 panic("Can't allocate logger");
47 logger->buffered_logs = xmalloc(LOGGER_BUFFERED_LOG_SIZE);
48 if (!logger->buffered_logs)
49 panic("Can't allocate logger buffer");
50 logger->buffered_logs[0] = '\0';
51 logger->autoflush = true;
52
53 bounds.left = padding;
54 bounds.top = ((screenBits.bounds.bottom -
55 screenBits.bounds.top) / 3);
56 bounds.right = screenBits.bounds.right - padding - 1;
57 bounds.bottom = screenBits.bounds.bottom - padding - 1;
58
59 logger->win = NewWindow(0L, &bounds, "\p", false,
60 documentProc, (WindowPtr)-1L, false, 0);
61 if (!logger->win)
62 panic("Can't create logger window");
63 logger_update_title();
64
65 SetPort(logger->win);
66
67 TextFont(LOGGER_FONT);
68 TextSize(LOGGER_FONT_SIZE);
69
70 bounds.right -= bounds.left;
71 bounds.bottom -= bounds.top;
72 bounds.top = bounds.left = 0;
73 logger_layout(&bounds);
74
75 focusable = xmalloczero(sizeof(struct focusable));
76 if (!focusable)
77 panic("Can't create focusable");
78 focusable->win = logger->win;
79 focusable->cookie = logger;
80 focusable->mouse_down = logger_mouse_down;
81 focusable->resize = logger_resize;
82 focusable->update = logger_update;
83 focusable->quit = logger_quit;
84 focusable->resume = logger_resume;
85 if (!add_focusable(focusable))
86 panic("Can't add focusable");
87 logger->focusable = focusable;
88
89 DrawControls(logger->win);
90 DrawGrowIconOnly(logger->win);
91 }
92
93 void
94 logger_layout(Rect *init_bounds)
95 {
96 Rect bounds, inset_bounds, win_bounds;
97 bool init = (init_bounds != NULL);
98
99 if (init)
100 win_bounds = *init_bounds;
101 else
102 win_bounds = logger->win->portRect;
103
104 /* messages scrollbar */
105 bounds.top = -1;
106 bounds.right = win_bounds.right - win_bounds.left + 1;
107 bounds.bottom = win_bounds.bottom - win_bounds.top - 14;
108 bounds.left = bounds.right - SCROLLBAR_WIDTH;
109 if (init) {
110 logger->messages_scroller = NewControl(logger->win, &bounds,
111 "\p", true, 1, 1, 1, scrollBarProc, 0L);
112 if (!logger->messages_scroller)
113 panic("Can't create messages scroller");
114 } else
115 (*(logger->messages_scroller))->contrlRect = bounds;
116
117 /* messages */
118 bounds.right = (*(logger->messages_scroller))->contrlRect.left;
119 bounds.left = 0;
120 bounds.top = 0;
121 bounds.bottom = win_bounds.bottom - win_bounds.top;
122 if (init) {
123 inset_bounds = bounds;
124 InsetRect(&inset_bounds, 4, 4);
125 logger->messages_te = TEStylNew(&inset_bounds, &bounds);
126 if (logger->messages_te == NULL)
127 panic("Can't create logger TE");
128 (*(logger->messages_te))->caretHook = NullCaretHook;
129 TEActivate(logger->messages_te);
130 } else {
131 (*(logger->messages_te))->viewRect = bounds;
132 InsetRect(&bounds, 4, 4);
133 (*(logger->messages_te))->destRect = bounds;
134 TECalText(logger->messages_te);
135 TEUpdate(&(*(logger->messages_te))->viewRect, logger->messages_te);
136 }
137
138 DrawControls(logger->win);
139 DrawGrowIconOnly(logger->win);
140
141 if (!init)
142 logger_flush_buffer(true);
143 }
144
145 void
146 logger_resume(struct focusable *focusable, EventRecord *event)
147 {
148 show_focusable(focusable);
149 InvalRect(logger->win->visRgn);
150 }
151
152 bool
153 logger_quit(struct focusable *focusable)
154 {
155 destroy_focusable(focusable);
156 xfree(&logger->buffered_logs);
157 xfree(&logger);
158
159 return true;
160 }
161
162 void
163 logger_update(struct focusable *focusable, EventRecord *event)
164 {
165 GrafPtr old_port;
166 Rect r;
167 short what = -1;
168
169 GetPort(&old_port);
170 SetPort(logger->win);
171
172 if (event != NULL)
173 what = event->what;
174
175 switch (what) {
176 case -1:
177 case updateEvt:
178 TextFont(applFont);
179 TextSize(10);
180
181 EraseRect(&logger->win->portRect);
182
183 r = (*(logger->messages_te))->viewRect;
184 InsetRect(&r, -1, -1);
185 FrameRect(&r);
186
187 TEUpdate(&(*(logger->messages_te))->viewRect, logger->messages_te);
188 DrawControls(logger->win);
189 DrawGrowIconOnly(logger->win);
190 break;
191 case activateEvt:
192 if (event->modifiers & activeFlag) {
193 TEActivate(logger->messages_te);
194 } else {
195 TEDeactivate(logger->messages_te);
196 }
197 break;
198 }
199
200 SetPort(old_port);
201 }
202
203 void
204 logger_update_title(void)
205 {
206 static char logger_title[64];
207 short n, uprinted = 0;
208
209 snprintf(logger_title, sizeof(logger_title),
210 "%s | %luKB Free | %lu Call%s | ", db->config.name,
211 (FreeMem() / 1024),
212 session_today_tally.calls, session_today_tally.calls == 1 ? "" : "s");
213
214 for (n = 0; n < MAX_SESSIONS; n++) {
215 if (sessions[n] == NULL || !sessions[n]->logged_in)
216 continue;
217
218 if (uprinted++)
219 strlcat(logger_title, ", ", sizeof(logger_title));
220 else
221 strlcat(logger_title, "Logged in: ", sizeof(logger_title));
222
223 strlcat(logger_title,
224 sessions[n]->user ? sessions[n]->user->username : "guest",
225 sizeof(logger_title));
226 }
227
228 if (uprinted == 0)
229 strlcat(logger_title, "0 connected", sizeof(logger_title));
230
231 SetWTitle(logger->win, CtoPstr(logger_title));
232 }
233
234 void
235 logger_mouse_down(struct focusable *focusable, EventRecord *event)
236 {
237 Point p;
238 ControlHandle control;
239 Rect r;
240 int val, adj;
241
242 p = event->where;
243 GlobalToLocal(&p);
244
245 r = (*(logger->messages_te))->viewRect;
246 if (PtInRect(p, &r)) {
247 TEClick(p, ((event->modifiers & shiftKey) != 0),
248 logger->messages_te);
249 HLock(logger->messages_te);
250 if ((*(logger->messages_te))->selStart !=
251 (*(logger->messages_te))->selEnd)
252 TESetSelect(0, 0, logger->messages_te);
253 HUnlock(logger->messages_te);
254 return;
255 }
256
257 switch (FindControl(p, logger->win, &control)) {
258 case inUpButton:
259 case inDownButton:
260 case inPageUp:
261 case inPageDown:
262 if (control != logger->messages_scroller)
263 break;
264 SetTrackControlTE(logger->messages_te);
265 TrackControl(control, p, TrackMouseDownInControl);
266 break;
267 case inThumb:
268 val = GetCtlValue(control);
269 if (TrackControl(control, p, 0L) == 0)
270 break;
271 adj = val - GetCtlValue(control);
272 if (adj != 0) {
273 val -= adj;
274 if (control == logger->messages_scroller)
275 TEScroll(0, adj * TEGetHeight(0, 0, logger->messages_te),
276 logger->messages_te);
277 SetCtlValue(control, val);
278 }
279 break;
280 }
281 }
282
283 void
284 logger_resize(struct focusable *focusable, EventRecord *event)
285 {
286 Rect bounds;
287 long newsize, width, height;
288
289 bounds.left = 100;
290 bounds.top = 100;
291 bounds.right = screenBits.bounds.right;
292 bounds.bottom = screenBits.bounds.bottom;
293
294 newsize = GrowWindow(focusable->win, event->where, &bounds);
295
296 height = HiWord(newsize);
297 width = LoWord(newsize);
298 SizeWindow(focusable->win, width, height, true);
299 logger_layout(NULL);
300 }
301
302 size_t
303 logger_printf(const char *format, ...)
304 {
305 va_list va;
306 size_t len;
307
308 if (!logger)
309 return 0;
310
311 va_start(va, format);
312 len = logger_vprintf(format, va);
313 va_end(va);
314
315 return len;
316 }
317
318 size_t
319 logger_vprintf(const char *format, va_list ap)
320 {
321 static char buf[600];
322 ssize_t len;
323 time_t now = Time;
324
325 if (!logger)
326 return 0;
327
328 blanker_unblank();
329
330 len = strftime(buf, sizeof(buf), "\r[%H:%M:%S] ", localtime(&now));
331 len += vsnprintf(buf + len, sizeof(buf) - len, format, ap);
332 if (len > sizeof(buf)) {
333 buf[sizeof(buf) - 1] = ']';
334 buf[sizeof(buf) - 2] = '…';
335 buf[sizeof(buf) - 3] = '[';
336 len = sizeof(buf);
337 }
338
339 while (buf[len - 1] == '\r') {
340 buf[len - 1] = '\0';
341 len--;
342 }
343
344 if (logger->buffered_logs_len + len > LOGGER_BUFFERED_LOG_SIZE)
345 logger_flush_buffer(false);
346
347 len = strlcat(logger->buffered_logs, buf, LOGGER_BUFFERED_LOG_SIZE);
348 logger->buffered_logs_len = MIN(len, LOGGER_BUFFERED_LOG_SIZE);
349
350 if (logger->autoflush)
351 logger_flush_buffer(false);
352
353 return len;
354 }
355
356 void
357 logger_flush_buffer(bool force)
358 {
359 RgnHandle savergn;
360 Rect zerorect = { 0, 0, 0, 0 };
361 GrafPtr old_port;
362 short line_height = 0, new_lines = 1, n;
363 char *buf;
364
365 if (!logger || (logger->buffered_logs_len == 0 && !force))
366 return;
367
368 for (n = 0; n < logger->buffered_logs_len; n++) {
369 if (logger->buffered_logs[n] == '\r')
370 new_lines++;
371 }
372
373 line_height = LOGGER_FONT_SIZE + 3;
374
375 GetPort(&old_port);
376 SetPort(logger->win);
377
378 /* check for TE overflow */
379
380 HLock(logger->messages_te);
381
382 /* too many lines */
383 if ((unsigned long)(*(logger->messages_te))->nLines >=
384 (nitems((*(logger->messages_te))->lineStarts) - new_lines))
385 goto te_overflow;
386
387 /* too many characters */
388 if ((unsigned long)(*(logger->messages_te))->teLength >=
389 (SHRT_MAX - logger->buffered_logs_len))
390 goto te_overflow;
391
392 /* destRect of all lines is too tall */
393 if ((unsigned long)(*(logger->messages_te))->nLines * line_height >=
394 (SHRT_MAX - new_lines))
395 goto te_overflow;
396
397 goto no_overflow;
398
399 te_overflow:
400 savergn = NewRgn();
401 GetClip(savergn);
402 /* create an empty clip region so all TE updates are hidden */
403 ClipRect(&zerorect);
404
405 /* select some lines at the start, delete them */
406 TESetSelect(0, (*(logger->messages_te))->lineStarts[new_lines * 2],
407 logger->messages_te);
408 TEDelete(logger->messages_te);
409
410 /* scroll up, causing a repaint */
411 TEPinScroll(0, SHRT_MAX, logger->messages_te);
412
413 /* then scroll back down to what it looked like before we did anything */
414 TEPinScroll(0, -SHRT_MAX, logger->messages_te);
415
416 /* resume normal drawing */
417 SetClip(savergn);
418 DisposeRgn(savergn);
419
420 no_overflow:
421 TESetSelect(SHRT_MAX, SHRT_MAX, logger->messages_te);
422
423 buf = logger->buffered_logs;
424 if ((*(logger->messages_te))->teLength == 0) {
425 /* skip leading \r */
426 logger->buffered_logs_len--;
427 buf++;
428 }
429
430 TEInsert(buf, logger->buffered_logs_len, logger->messages_te);
431
432 if ((unsigned long)(*(logger->messages_te))->nLines >=
433 nitems((*(logger->messages_te))->lineStarts))
434 warn("te overflow!");
435
436 TEPinScroll(0, -SHRT_MAX, logger->messages_te);
437 SetCtlValue(logger->messages_scroller,
438 GetCtlMax(logger->messages_scroller));
439 UpdateScrollbarForTE(logger->win, logger->messages_scroller,
440 logger->messages_te, false);
441 HUnlock(logger->messages_te);
442
443 SetPort(old_port);
444
445 logger->buffered_logs_len = 0;
446 logger->buffered_logs[0] = '\0';
447 }