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
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...
32 #include "shared/report.h"
33 #include "shared/LL.h"
38 #include "screenlist.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 */
47 char *server_msg_text
;
48 int server_msg_expire
= 0;
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
);
57 render_screen(Screen
*s
, long int timer
)
59 static Screen
*old_s
= NULL
;
62 debug(RPT_DEBUG
, "%s(screen=[%.40s], timer=%d) ==== START RENDERING ====", __FUNCTION__
, s
->id
, timer
);
73 /* Clear the LCD screen... */
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
;
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
) {
99 (tmp_state
& BACKLIGHT_ON
)
101 ) ? BACKLIGHT_ON
: BACKLIGHT_OFF
);
102 /* Backlight blink: check timer and flip backlight as appropriate */
104 else if (tmp_state
& BACKLIGHT_BLINK
) {
107 (tmp_state
& BACKLIGHT_ON
)
108 ^ ((timer
& 14) == 14)
109 ) ? BACKLIGHT_ON
: BACKLIGHT_OFF
);
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
);
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
;
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... */
148 debug(RPT_DEBUG
, "==== END RENDERING ====");
153 /* The following function is positively ghastly (as was mentioned above!) */
154 /* Best thing to do is to remove support for frames... but anyway... */
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 */
175 int /*fx = 0,*/ fy
= 0; /* Scrolling offset for the frame... */
177 int str_length
= BUFSIZE
-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))
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;
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 */
208 /* loop over all widgets */
210 char str
[BUFSIZE
]; /* scratch buffer */
211 Widget
*w
= (Widget
*) LL_Get(list
);
216 /* TODO: Make this cleaner and more flexible!*/
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
);
225 drivers_string(w
->x
+ left
, w
->y
+ top
- fy
, str
);
232 if ((w
->x
> 0) && (w
->y
> 0) &&
233 (w
->y
<= vis_height
+ fy
) && (w
->y
> fy
)) {
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
);
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.
257 case WID_VBAR
: /* FIXME: Vbars don't work in frames!*/
261 if ((w
->x
> 0) && (w
->y
> 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.
277 drivers_icon(w
->x
, w
->y
, w
->length
);
280 case WID_TITLE
: /* FIXME: Doesn't work quite right in frames...*/
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
);
295 } else /* Scroll the title, if it doesn't fit...*/
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
);
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
);
322 case WID_SCROLLER
: /* FIXME: doesn't work in frames...*/
329 if (w
->right
< w
->left
)
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?)
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
);
344 int necessaryTimeUnits
= 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;
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
362 strncat(str
, w
->text
, room
);
365 str
[screen_width
] = '\0';
367 /*debug(RPT_DEBUG, "scroller %s : %d", str, length-offset);*/
371 drivers_string(w
->left
, w
->top
, str
);
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
);
380 int effLength
= length
- screen_width
;
381 int necessaryTimeUnits
= 0;
384 necessaryTimeUnits
= effLength
* w
->speed
;
385 if (((timer
/ (effLength
* w
->speed
)) % 2) == 0) {
387 offset
= (timer
% (effLength
* w
->speed
))
391 offset
= (((timer
% (effLength
* w
->speed
))
392 - (effLength
* 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)))
401 offset
= (((timer
% (effLength
/ (w
->speed
* -1)))
402 * w
->speed
* -1) - effLength
+ 1) * -1;
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); */
414 drivers_string(w
->left
, w
->top
, str
);
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?) */
424 length
= strlen(w
->text
);
425 if (length
<= screen_width
) {
426 /* no scrolling required... */
427 drivers_string(w
->left
, w
->top
, w
->text
);
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
) {
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
);
441 int necessaryTimeUnits
= 0;
442 int effLines
= lines_required
- available_lines
+ 1;
445 /*debug(RPT_DEBUG, "length: %d sw: %d lines req: %d avail lines: %d effLines: %d ",length,screen_width,lines_required,available_lines,effLines);*/
447 necessaryTimeUnits
= effLines
* w
->speed
;
448 if (((timer
/ (effLines
* w
->speed
)) % 2) == 0) {
449 /*debug(RPT_DEBUG, "up ");*/
450 begin
= (timer
% (effLines
* w
->speed
))
453 /*debug(RPT_DEBUG, "down ");*/
454 begin
= (((timer
% (effLines
* w
->speed
))
455 - (effLines
* w
->speed
) + 1) / w
->speed
)
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)))
464 begin
= (((timer
% (effLines
/ (w
->speed
* -1)))
465 * w
->speed
* -1) - effLines
+ 1)
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", */
477 drivers_string(w
->left
, w
->top
+ (i
- begin
), str
);
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
);
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)) {
506 drivers_num(w
->x
+ left
, w
->y
);
513 } while (LL_Next(list
) == 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) {
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
;