Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / render.c
blob7392e8602fc5d0c5e6fed05f7edad3805abc2b0e
1 /*
2 * render.c
3 * This file is part of LCDd, the lcdproc server.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 1999, William Ferrell, Scott Scriven
9 * 2001, Joris Robijn
12 * Draws screens on the LCD.
14 * This needs to be greatly expanded and redone for greater flexibility.
15 * For example, it should support multiple screen sizes, more flexible
16 * widgets, and multiple simultaneous screens.
18 * This will probably take a while to do. :(
20 * THIS FILE IS MESSY! Anyone care to rewrite it nicely? Please?? :)
22 * NOTE: (from David Douthitt) Multiple screen sizes? Multiple simultaneous
23 * screens? Horrors of horrors... next thing you know it'll be making coffee...
24 * Better believe it'll take a while to do...
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
32 #include "shared/report.h"
33 #include "shared/LL.h"
35 #include "drivers.h"
37 #include "screen.h"
38 #include "screenlist.h"
39 #include "widget.h"
40 #include "render.h"
42 int heartbeat = HEARTBEAT_OPEN;
43 static int heartbeat_fallback = HEARTBEAT_ON; /* If no heartbeat setting has been set at all */
44 int backlight = BACKLIGHT_OPEN;
45 static int backlight_fallback = BACKLIGHT_ON; /* If no backlight setting has been set at all */
46 int output_state = 0;
47 char *server_msg_text;
48 int server_msg_expire = 0;
50 static int reset;
52 #define BUFSIZE 1024
54 static int render_frame(LinkedList *list, char fscroll, int left, int top, int right, int bottom, int fwid, int fhgt, int fspeed, long int timer);
56 int
57 render_screen(Screen *s, long int timer)
59 static Screen *old_s = NULL;
60 int tmp_state = 0;
62 debug(RPT_DEBUG, "%s(screen=[%.40s], timer=%d) ==== START RENDERING ====", __FUNCTION__, s->id, timer);
64 reset = 1;
66 if (!s)
67 return -1;
69 if (s == old_s)
70 reset = 0;
71 old_s = s;
73 /* Clear the LCD screen... */
74 drivers_clear();
76 /* FIXME drivers_backlight --
78 * If the screen's backlight isn't set (default) then we
79 * inherit the backlight state from the parent client. This allows
80 * the client to override it's childrens settings.
81 * The server can also override the clients and screens settings.
83 if (backlight != BACKLIGHT_OPEN) {
84 tmp_state = backlight;
85 } else if (s->client && s->client->backlight != BACKLIGHT_OPEN) {
86 tmp_state = s->client->backlight;
87 } else if (s->backlight != BACKLIGHT_OPEN) {
88 tmp_state = s->backlight;
89 } else {
90 tmp_state = backlight_fallback;
93 /* Set up backlight to the correct state... */
94 /* NOTE: dirty stripping of other options... */
95 /* Backlight flash: check timer and flip backlight as appropriate */
96 if (tmp_state & BACKLIGHT_FLASH) {
97 drivers_backlight(
99 (tmp_state & BACKLIGHT_ON)
100 ^ ((timer & 7) == 7)
101 ) ? BACKLIGHT_ON : BACKLIGHT_OFF);
102 /* Backlight blink: check timer and flip backlight as appropriate */
104 else if (tmp_state & BACKLIGHT_BLINK) {
105 drivers_backlight(
107 (tmp_state & BACKLIGHT_ON)
108 ^ ((timer & 14) == 14)
109 ) ? BACKLIGHT_ON : BACKLIGHT_OFF);
110 } else {
111 /* Simple: Only send lowest bit then...*/
112 drivers_backlight(tmp_state & BACKLIGHT_ON);
115 /* Output ports from LCD - outputs depend on the current screen */
116 drivers_output(output_state);
118 /* Draw a frame... */
119 render_frame(s->widgetlist, 'v', 0, 0, display_props->width, display_props->height, s->width, s->height, (((s->duration / s->height) < 1) ? 1 : (s->duration / s->height)), timer);
121 /* Set the cursor */
122 drivers_cursor(s->cursor_x, s->cursor_y, s->cursor);
124 if (heartbeat != HEARTBEAT_OPEN) {
125 tmp_state = heartbeat;
126 } else if (s->client && s->client->heartbeat != HEARTBEAT_OPEN) {
127 tmp_state = s->client->heartbeat;
128 } else if (s->heartbeat != HEARTBEAT_OPEN) {
129 tmp_state = s->heartbeat;
130 } else {
131 tmp_state = heartbeat_fallback;
133 drivers_heartbeat(tmp_state);
135 /* If there is an server message that is not expired, display it */
136 if (server_msg_expire > 0) {
137 drivers_string(display_props->width - strlen(server_msg_text) + 1,
138 display_props->height, server_msg_text);
139 server_msg_expire --;
140 if (server_msg_expire == 0) {
141 free(server_msg_text);
145 /* flush display out, frame and all... */
146 drivers_flush();
148 debug(RPT_DEBUG, "==== END RENDERING ====");
149 return 0;
153 /* The following function is positively ghastly (as was mentioned above!) */
154 /* Best thing to do is to remove support for frames... but anyway... */
155 /* */
156 static int
157 render_frame(LinkedList *list,
158 char fscroll, /* direction of scrolling */
159 int left, /* left edge of frame */
160 int top, /* top edge of frame */
161 int right, /* right edge of frame */
162 int bottom, /* bottom edge of frame */
163 int fwid, /* frame width? */
164 int fhgt, /* frame height? */
165 int fspeed, /* speed of scrolling... */
166 long int timer) /* ? */
169 #define VerticalScrolling (fscroll == 'v')
170 #define HorizontalScrolling (fscroll == 'h')
172 int vis_width = right - left; /* width of visible frame area */
173 int vis_height = bottom - top; /* height of visible frame area */
174 int x, y;
175 int /*fx = 0,*/ fy = 0; /* Scrolling offset for the frame... */
176 int length, speed;
177 int str_length = BUFSIZE-1;
178 int reset = 1;
180 debug(RPT_DEBUG, "%s(list=%p, fscroll='%c', left=%d, top=%d, "
181 "right=%d, bottom=%d, fwid=%d, fhgt=%d, fspeed=%d, timer=%d)",
182 __FUNCTION__, list, fscroll, left,top, right, bottom,
183 fwid, fhgt, fspeed, timer);
185 /* return on no data or illegal height */
186 if (!list || (fhgt <= 0))
187 return -1;
189 if (VerticalScrolling) {
190 // FIXME: timer may be negative (this should be changed generally)
191 // only set offset !=0 when fspeed is != 0 and there is something to scroll
192 if (fspeed && (fhgt > vis_height)) {
193 int fy_max = fhgt - vis_height + 1;
195 fy = (fspeed > 0)
196 ? (timer / fspeed) % fy_max
197 : (-fspeed * timer) % fy_max;
199 fy = max(fy, 0); // safeguard against negative values
201 } else if (HorizontalScrolling) {
202 /* TODO: Frames don't scroll horizontally yet! */
205 /* reset widget list */
206 LL_Rewind(list);
208 /* loop over all widgets */
209 do {
210 char str[BUFSIZE]; /* scratch buffer */
211 Widget *w = (Widget *) LL_Get(list);
213 if (!w)
214 return -1;
216 /* TODO: Make this cleaner and more flexible!*/
217 switch (w->type) {
218 case WID_STRING:
219 if ((w->x > 0) && (w->y > 0) && (w->text) &&
220 (w->y <= vis_height + fy) && (w->y > fy)) {
221 w->x = min(w->x, vis_width);
222 str_length = min(vis_width - w->x + 1, BUFSIZE - 1);
223 strncpy(str, w->text, str_length);
224 str[str_length] = 0;
225 drivers_string(w->x + left, w->y + top - fy, str);
227 break;
228 case WID_HBAR:
229 if (reset) {
230 reset = 0;
232 if ((w->x > 0) && (w->y > 0) &&
233 (w->y <= vis_height + fy) && (w->y > fy)) {
234 if (w->length > 0) {
235 if ((w->length / display_props->cellwidth) < vis_width - w->x + 1) {
236 /*was: drivers_hbar(w->x + left, w->y + top - fy, w->length); */
237 /* improvised len and promille */
238 int full_len = display_props->width - w->x - left + 1;
239 int promille = (long) 1000 * w->length / (display_props->cellwidth * full_len);
240 drivers_hbar(w->x + left, w->y + top - fy, full_len, promille, BAR_PATTERN_FILLED);
242 else {
243 /*was: drivers_hbar(w->x + left, w->y + top - fy, wid * display_props->cellwidth); */
244 /* Improvised len and promille while we have the old widget language */
245 int full_len = (display_props->width - w->x - left + 1);
246 drivers_hbar(w->x + left, w->y + top - fy, full_len, 1000, BAR_PATTERN_FILLED);
248 } else if (w->length < 0) {
249 /* TODO: Rearrange stuff to get left-extending
250 * hbars to draw correctly...
251 * .. er, this'll require driver modifications,
252 * so I'll leave it out for now.
256 break;
257 case WID_VBAR: /* FIXME: Vbars don't work in frames!*/
258 if (reset) {
259 reset = 0;
261 if ((w->x > 0) && (w->y > 0)) {
262 if (w->length > 0) {
263 /* Improvised len and promille while we have the old widget language */
264 int full_len = display_props->height;
265 int promille = (long) 1000 * w->length / display_props->cellheight / full_len;
266 drivers_vbar(w->x, display_props->height, full_len, promille, BAR_PATTERN_FILLED);
267 } else if (w->length < 0) {
268 /* TODO: Rearrange stuff to get down-extending
269 * vbars to draw correctly...
270 * .. er, this'll require driver modifications,
271 * so I'll leave it out for now.
275 break;
276 case WID_ICON:
277 drivers_icon(w->x, w->y, w->length);
279 break;
280 case WID_TITLE: /* FIXME: Doesn't work quite right in frames...*/
281 if (!w->text)
282 break;
283 if (vis_width < 8)
284 break;
286 drivers_icon(w->x + left, w->y + top, ICON_BLOCK_FILLED);
287 drivers_icon(w->x + left + 1, w->y + top, ICON_BLOCK_FILLED);
289 length = strlen(w->text);
290 length = min(length, BUFSIZE - 1);
291 if (length <= vis_width - 6) {
292 strncpy(str, w->text, length);
293 str[length] = 0;
294 x = length + 5;
295 } else /* Scroll the title, if it doesn't fit...*/
297 speed = 1;
298 x = timer / speed;
299 y = x / length;
301 x %= length;
302 x = max(x, 0);
303 if (x > length - (vis_width - 6))
304 x = length - (vis_width - 6);
306 if (y & 1) /* Scrolling backwards...*/
307 x = (length - (vis_width - 6)) - x;
308 str_length = abs(vis_width - 6);
309 str_length = min(str_length, BUFSIZE -1);
310 strncpy(str, w->text + x, str_length);
311 str[str_length] = 0;
312 x = vis_width - 1;
315 drivers_string(w->x + 3 + left, w->y + top, str);
317 for (; x<=vis_width; x++) {
318 drivers_icon(w->x + x - 1 + left, w->y + top, ICON_BLOCK_FILLED);
321 break;
322 case WID_SCROLLER: /* FIXME: doesn't work in frames...*/
324 int offset;
325 int screen_width;
327 if (!w->text)
328 break;
329 if (w->right < w->left)
330 break;
331 /*debug(RPT_DEBUG, "%s: %s %d",__FUNCTION__,w->text,timer);*/
332 screen_width = abs(w->right - w->left + 1);
333 screen_width = min(screen_width, BUFSIZE -1);
334 switch (w->length) { /* actually, direction...*/
335 /* FIXED: Horz scrollers don't show the
336 * last letter in the string... (1-off error?)
338 case 'm': // Marquee
339 length = strlen(w->text);
340 if (length <= screen_width) {
341 /* it fits within the box, just render it */
342 drivers_string(w->left, w->top, w->text);
343 } else {
344 int necessaryTimeUnits = 0;
346 if (w->speed > 0) {
347 necessaryTimeUnits = length * w->speed;
348 offset = (timer % (length * w->speed)) / w->speed;
349 } else if (w->speed < 0) {
350 necessaryTimeUnits = length / (w->speed * -1);
351 offset = (timer % (length / (w->speed * -1))) * w->speed * -1;
352 } else {
353 offset = 0;
355 if (offset <= length) {
356 int room = screen_width - (length - offset);
358 strncpy(str, &w->text[offset], screen_width);
360 // if there's more room, restart at the beginning
361 if (room > 0) {
362 strncat(str, w->text, room);
365 str[screen_width] = '\0';
367 /*debug(RPT_DEBUG, "scroller %s : %d", str, length-offset);*/
368 } else {
369 str[0] = '\0';
371 drivers_string(w->left, w->top, str);
373 break;
374 case 'h':
375 length = strlen(w->text) + 1;
376 if (length <= screen_width) {
377 /* it fits within the box, just render it */
378 drivers_string(w->left, w->top, w->text);
379 } else {
380 int effLength = length - screen_width;
381 int necessaryTimeUnits = 0;
383 if (w->speed > 0) {
384 necessaryTimeUnits = effLength * w->speed;
385 if (((timer / (effLength * w->speed)) % 2) == 0) {
386 /*wiggle one way*/
387 offset = (timer % (effLength * w->speed))
388 / w->speed;
389 } else {
390 /*wiggle the other*/
391 offset = (((timer % (effLength * w->speed))
392 - (effLength * w->speed) + 1)
393 / w->speed) * -1;
395 } else if (w->speed < 0) {
396 necessaryTimeUnits = effLength / (w->speed * -1);
397 if (((timer / (effLength / (w->speed * -1))) % 2) == 0) {
398 offset = (timer % (effLength / (w->speed * -1)))
399 * w->speed * -1;
400 } else {
401 offset = (((timer % (effLength / (w->speed * -1)))
402 * w->speed * -1) - effLength + 1) * -1;
404 } else {
405 offset = 0;
407 if (offset <= length) {
408 strncpy(str, &((w->text)[offset]), screen_width);
409 str[screen_width] = '\0';
410 /*debug(RPT_DEBUG, "scroller %s : %d", str, length-offset); */
411 } else {
412 str[0] = '\0';
414 drivers_string(w->left, w->top, str);
416 break;
417 /* FIXME: Vert scrollers don't always seem to scroll */
418 /* back up after hitting the bottom. They jump back to */
419 /* the top instead... (nevermind?) */
420 case 'v':
422 int i = 0;
424 length = strlen(w->text);
425 if (length <= screen_width) {
426 /* no scrolling required... */
427 drivers_string(w->left, w->top, w->text);
428 } else {
429 int lines_required = (length / screen_width)
430 + (length % screen_width ? 1 : 0);
431 int available_lines = (w->bottom - w->top + 1);
433 if (lines_required <= available_lines) {
434 /* easy...*/
435 for (i = 0; i < lines_required; i++) {
436 strncpy(str, &((w->text)[i * screen_width]), screen_width);
437 str[screen_width] = '\0';
438 drivers_string (w->left, w->top + i, str);
440 } else {
441 int necessaryTimeUnits = 0;
442 int effLines = lines_required - available_lines + 1;
443 int begin = 0;
445 /*debug(RPT_DEBUG, "length: %d sw: %d lines req: %d avail lines: %d effLines: %d ",length,screen_width,lines_required,available_lines,effLines);*/
446 if (w->speed > 0) {
447 necessaryTimeUnits = effLines * w->speed;
448 if (((timer / (effLines * w->speed)) % 2) == 0) {
449 /*debug(RPT_DEBUG, "up ");*/
450 begin = (timer % (effLines * w->speed))
451 / w->speed;
452 } else {
453 /*debug(RPT_DEBUG, "down ");*/
454 begin = (((timer % (effLines * w->speed))
455 - (effLines * w->speed) + 1) / w->speed)
456 * -1;
458 } else if (w->speed < 0) {
459 necessaryTimeUnits = effLines / (w->speed * -1);
460 if (((timer / (effLines / (w->speed * -1))) % 2) == 0) {
461 begin = (timer % (effLines / (w->speed * -1)))
462 * w->speed * -1;
463 } else {
464 begin = (((timer % (effLines / (w->speed * -1)))
465 * w->speed * -1) - effLines + 1)
466 * -1;
468 } else {
469 begin = 0;
471 /*debug(RPT_DEBUG, "rendering begin: %d timer: %d effLines: %d",begin,timer,effLines); */
472 for (i = begin; i < begin + available_lines; i++) {
473 strncpy(str, &((w->text)[i * (screen_width)]), screen_width);
474 str[screen_width] = '\0';
475 /*debug(RPT_DEBUG, "rendering: '%s' of %s", */
476 /*str,w->text); */
477 drivers_string(w->left, w->top + (i - begin), str);
481 break;
484 break;
486 case WID_FRAME:
488 /* FIXME: doesn't handle nested frames quite right!
489 * doesn't handle scrolling in nested frames at all...
491 int new_left = left + w->left - 1;
492 int new_top = top + w->top - 1;
493 int new_right = min(left + w->right, right);
494 int new_bottom = min(top + w->bottom, bottom);
496 if ((new_left < right) && (new_top < bottom)) /* Render only if it's visible...*/
497 render_frame(w->frame_screen->widgetlist, w->length, new_left, new_top, new_right, new_bottom, w->width, w->height, w->speed, timer);
499 break;
500 case WID_NUM: /* FIXME: doesn't work in frames...*/
501 /* NOTE: y=10 means COLON (:)*/
502 if ((w->x > 0) && (w->y >= 0) && (w->y <= 10)) {
503 if (reset) {
504 reset = 0;
506 drivers_num(w->x + left, w->y);
508 break;
509 case WID_NONE:
510 default:
511 break;
513 } while (LL_Next(list) == 0);
515 return 0;
519 server_msg(const char *text, int expire)
521 debug(RPT_DEBUG, "%s(text=\"%.40s\", expire=%d)", __FUNCTION__, text, expire);
523 if (strlen(text) > 15 || expire <= 0) {
524 return -1;
527 /* Still a message active ? */
529 if (server_msg_expire > 0) {
530 free(server_msg_text);
533 /* Store new message */
534 server_msg_text = malloc(strlen(text) + 3);
535 strcpy(server_msg_text, "| ");
536 strcat(server_msg_text, text);
538 server_msg_expire = expire;
540 return 0;