AmendHub

Download

jcs

/

subtext

/

logger.c

 

(View History)

jcs   logger: Add support for resizing window Latest amendment: 288 on 2022-11-18

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