A tiling window manager
1/*
2 * Functionality for a bar listing the windows currently managed.
3 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA.
18 */
19
20#include <X11/X.h>
21#include <X11/Xlib.h>
22#include <X11/Xutil.h>
23#include <X11/Xft/Xft.h>
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <errno.h>
30#include <err.h>
31#include <sys/time.h>
32
33#include "sdorfehs.h"
34
35/* Possible values for bar_is_raised status. */
36#define BAR_IS_HIDDEN 0
37#define BAR_IS_WINDOW_LIST 1
38#define BAR_IS_VSCREEN_LIST 2
39#define BAR_IS_MESSAGE 3
40#define BAR_IS_STICKY 4
41
42#define BAR_IS_RAISED(s) (s->bar_is_raised != BAR_IS_HIDDEN && \
43 s->bar_is_raised != BAR_IS_STICKY)
44
45/* A copy of the last message displayed in the message bar. */
46static char *last_msg = NULL;
47static int last_mark_start = 0;
48static int last_mark_end = 0;
49
50/* Buffers for sticky bar */
51static struct sbuf *bar_buf = NULL;
52static struct sbuf *bar_line = NULL;
53struct list_head bar_chunks;
54static char *last_bar_line;
55static char *last_bar_window_fmt;
56static char bar_tmp_line[256];
57static Pixmap bar_pm;
58
59struct bar_chunk {
60 char *text;
61 int length;
62 char *color;
63 char *font;
64 char *clickcmd;
65 int text_x;
66 int text_width;
67 struct list_head node;
68};
69
70static void draw_partial_string(rp_screen *s, char *msg, int len, int x_offset,
71 int y_offset, int style, char *color);
72static void marked_message_internal(char *msg, int mark_start, int mark_end,
73 int bar_type);
74
75/* Reset the alarm to auto-hide the bar in BAR_TIMEOUT seconds. */
76void
77bar_reset_alarm(void)
78{
79 struct timeval timeout;
80 struct itimerval alarmtimer;
81
82 memset(&timeout, 0, sizeof(timeout));
83 memset(&alarmtimer, 0, sizeof(alarmtimer));
84
85 timeout.tv_sec = (time_t)(defaults.bar_timeout);
86 alarmtimer.it_value = timeout;
87 setitimer(ITIMER_REAL, &alarmtimer, NULL);
88
89 alarm_signalled = 0;
90}
91
92static int
93bar_time_left(void)
94{
95 struct itimerval left;
96
97 getitimer(ITIMER_REAL, &left);
98 return left.it_value.tv_sec > 0;
99}
100
101int
102bar_mkfifo(void)
103{
104 char *config_dir;
105
106 config_dir = get_config_dir();
107 rp_glob_screen.bar_fifo_path = xsprintf("%s/bar", config_dir);
108 free(config_dir);
109
110 unlink(rp_glob_screen.bar_fifo_path);
111
112 if (mkfifo(rp_glob_screen.bar_fifo_path, S_IRUSR|S_IWUSR) == -1) {
113 warn("failed creating bar FIFO at %s",
114 rp_glob_screen.bar_fifo_path);
115 return 0;
116 }
117
118 return bar_open_fifo();
119}
120
121void
122init_bar(void)
123{
124 rp_screen *primary = screen_primary();
125
126 bar_buf = sbuf_new(sizeof(bar_tmp_line));
127 bar_line = sbuf_new(sizeof(bar_tmp_line));
128 bar_pm = XCreatePixmap(dpy, primary->bar_window, primary->width,
129 FONT_HEIGHT(rp_current_screen) * 2,
130 DefaultDepth(dpy, DefaultScreen(dpy)));
131
132 INIT_LIST_HEAD(&bar_chunks);
133}
134
135/* Hide the bar from sight. */
136void
137hide_bar(rp_screen *s, int force)
138{
139 if (!s->full_screen_win && defaults.bar_sticky && !force) {
140 redraw_sticky_bar_text(0);
141
142 if (s == screen_primary())
143 return;
144 /* otherwise, we need to hide this secondary screen's bar */
145 }
146
147 s->bar_is_raised = BAR_IS_HIDDEN;
148 XUnmapWindow(dpy, s->bar_window);
149
150 /* Possibly restore colormap. */
151 if (current_window()) {
152 XUninstallColormap(dpy, s->def_cmap);
153 XInstallColormap(dpy, current_window()->colormap);
154 }
155}
156
157/* Show window listing in bar. */
158void
159show_bar(rp_screen *s, char *fmt)
160{
161 s->bar_is_raised = BAR_IS_WINDOW_LIST;
162 XMapRaised(dpy, s->bar_window);
163 update_window_names(s, fmt);
164
165 /* Switch to the default colormap */
166 if (current_window())
167 XUninstallColormap(dpy, current_window()->colormap);
168 XInstallColormap(dpy, s->def_cmap);
169
170 raise_utility_windows();
171
172 bar_reset_alarm();
173}
174
175/* Show vscreen listing in bar. */
176void
177show_vscreen_bar(rp_screen *s)
178{
179 s->bar_is_raised = BAR_IS_VSCREEN_LIST;
180 XMapRaised(dpy, s->bar_window);
181 update_vscreen_names(s);
182
183 /* Switch to the default colormap */
184 if (current_window())
185 XUninstallColormap(dpy, current_window()->colormap);
186 XInstallColormap(dpy, s->def_cmap);
187
188 raise_utility_windows();
189
190 bar_reset_alarm();
191}
192
193int
194bar_x(rp_screen *s, int width)
195{
196 int x = 0;
197
198 switch (defaults.bar_location) {
199 case NorthWestGravity:
200 case WestGravity:
201 case SouthWestGravity:
202 x = s->left +
203 (defaults.bar_in_padding ? 0 : defaults.padding_left);
204 break;
205 case NorthGravity:
206 case CenterGravity:
207 case SouthGravity:
208 x = s->left +
209 (s->width - width - defaults.bar_border_width * 2) / 2 -
210 (defaults.bar_in_padding ? 0 : defaults.padding_left);
211 break;
212 case NorthEastGravity:
213 case EastGravity:
214 case SouthEastGravity:
215 x = s->left + s->width - width -
216 (defaults.bar_border_width * 2) -
217 (defaults.bar_in_padding ? 0 : defaults.padding_right);
218 break;
219 }
220
221 return x;
222}
223
224int
225bar_y(rp_screen *s, int height)
226{
227 int y = 0;
228
229 switch (defaults.bar_location) {
230 case NorthEastGravity:
231 case NorthGravity:
232 case NorthWestGravity:
233 y = s->top +
234 (defaults.bar_in_padding ? 0 : defaults.padding_top);
235 break;
236 case EastGravity:
237 case CenterGravity:
238 case WestGravity:
239 y = s->top + (s->height - height -
240 defaults.bar_border_width * 2) / 2 -
241 (defaults.bar_in_padding ? 0 : defaults.padding_top);
242 break;
243 case SouthEastGravity:
244 case SouthGravity:
245 case SouthWestGravity:
246 y = s->top + (s->height - height -
247 defaults.bar_border_width * 2) -
248 (defaults.bar_in_padding ? 0 : defaults.padding_top);
249 break;
250 }
251
252 return y;
253}
254
255int
256sticky_bar_height(rp_screen *s)
257{
258 if (defaults.bar_sticky)
259 return FONT_HEIGHT(s) +
260 (defaults.bar_border_width * 2) +
261 (defaults.bar_y_padding * 2);
262
263 return 0;
264}
265
266void
267update_bar(rp_screen *s)
268{
269 switch (s->bar_is_raised) {
270 case BAR_IS_WINDOW_LIST:
271 update_window_names(s, defaults.window_fmt);
272 break;
273 case BAR_IS_VSCREEN_LIST:
274 update_vscreen_names(s);
275 break;
276 case BAR_IS_STICKY:
277 hide_bar(s, 0);
278 break;
279 }
280}
281
282/*
283 * To avoid having to parse the bar line text and redraw text over and over
284 * again, text is drawn on a pixmap in two lines. When the window format text
285 * changes (such as when the current window changes) or the bar line text
286 * changes, each line that is different is redrawn. Then the two lines are
287 * copied into the actual bar window side by side.
288 */
289void
290redraw_sticky_bar_text(int force)
291{
292 rp_screen *s = screen_primary();
293 struct list_head *iter, *tmp;
294 struct bar_chunk *chunk;
295 struct sbuf *tbuf, *curcmd, *curtxt;
296 char *tline, *font, *color, *clickcmd;
297 int diff = 0, len, cmd = 0, skip = 0, xftx = 0, x;
298 int width, height;
299
300 if (!force && (s->full_screen_win || !defaults.bar_sticky ||
301 bar_time_left()))
302 return;
303
304 /*
305 * If we were showing a message or window list before, make sure we
306 * clear it all.
307 */
308 if (s->bar_is_raised != BAR_IS_STICKY)
309 force = 1;
310
311 width = s->width - (defaults.bar_border_width * 2);
312 if (!defaults.bar_in_padding)
313 width -= defaults.padding_right + defaults.padding_left;
314 height = FONT_HEIGHT(s) + (defaults.bar_y_padding * 2);
315 XMoveResizeWindow(dpy, s->bar_window, bar_x(s, width), bar_y(s, height),
316 width, height);
317
318 if (force) {
319 XFreePixmap(dpy, bar_pm);
320 XClearWindow(dpy, s->bar_window);
321 bar_pm = XCreatePixmap(dpy, s->bar_window, width,
322 FONT_HEIGHT(s) * 2, DefaultDepth(dpy, DefaultScreen(dpy)));
323 }
324
325 s->bar_is_raised = BAR_IS_STICKY;
326 XMapRaised(dpy, s->bar_window);
327
328 /* check window title for changes */
329 tbuf = sbuf_new(0);
330 get_current_window_in_fmt(defaults.sticky_fmt, tbuf);
331 diff = (last_bar_window_fmt == NULL ||
332 strcmp(last_bar_window_fmt, sbuf_get(tbuf)) != 0);
333
334 if (diff || force) {
335 PRINT_DEBUG(("redrawing window format in bar\n"));
336
337 if (last_bar_window_fmt != NULL)
338 free(last_bar_window_fmt);
339 last_bar_window_fmt = xstrdup(sbuf_get(tbuf));
340
341 XFillRectangle(dpy, bar_pm, s->inverse_gc, 0, 0, s->width,
342 FONT_HEIGHT(s));
343
344 rp_draw_string(s, bar_pm, STYLE_NORMAL, 0, FONT_ASCENT(s),
345 last_bar_window_fmt, strlen(last_bar_window_fmt),
346 NULL, NULL);
347 }
348 sbuf_free(tbuf);
349
350 XCopyArea(dpy, bar_pm, s->bar_window, s->inverse_gc,
351 0, 0, (s->width / 2) - (defaults.bar_x_padding * 2), FONT_HEIGHT(s),
352 defaults.bar_x_padding, defaults.bar_y_padding);
353
354 /* Repeat for bar text line */
355
356 diff = (last_bar_line == NULL ||
357 strcmp(last_bar_line, sbuf_get(bar_line)) != 0);
358 if (!diff && !force)
359 goto redraw_bar_text;
360
361 PRINT_DEBUG(("recalculating bar chunks\n"));
362
363 if (last_bar_line)
364 free(last_bar_line);
365 last_bar_line = xstrdup(sbuf_get(bar_line));
366 len = strlen(last_bar_line);
367
368 curcmd = sbuf_new(0);
369 curtxt = sbuf_new(0);
370
371 list_for_each_safe_entry(chunk, iter, tmp, &bar_chunks, node) {
372 free(chunk->text);
373 if (chunk->color)
374 free(chunk->color);
375 if (chunk->font)
376 free(chunk->font);
377 if (chunk->clickcmd)
378 free(chunk->clickcmd);
379 list_del(&chunk->node);
380 free(chunk);
381 }
382
383 color = font = clickcmd = NULL;
384 for (x = 0; x < len; x++) {
385 if (last_bar_line[x] == '\\' && !skip) {
386 skip = 1;
387 continue;
388 }
389
390 if (cmd) {
391 if (last_bar_line[x] == ')' && !skip) {
392 tline = sbuf_get(curcmd);
393 if (strncmp(tline, "fg(", 3) == 0) {
394 /* ^fg(green)text^fg() */
395 if (color)
396 free(color);
397 color = NULL;
398 if (strlen(tline) > 3)
399 color = xstrdup(tline + 3);
400 } else if (strncmp(tline, "fn(", 3) == 0) {
401 /* ^fn(courier)text^fn() */
402 if (font)
403 free(font);
404 font = NULL;
405 if (strlen(tline) > 3)
406 font = xstrdup(tline + 3);
407 } else if (strncmp(tline, "ca(", 3) == 0) {
408 /* ^ca(1,some command)*^ca() */
409 if (clickcmd)
410 free(clickcmd);
411 clickcmd = NULL;
412 if (strlen(tline) > 4)
413 clickcmd = xstrdup(tline + 3);
414 } else {
415 PRINT_DEBUG(("unsupported bar command "
416 "\"%s\", ignoring\n", tline));
417 }
418 sbuf_clear(curcmd);
419 cmd = 0;
420 } else
421 sbuf_nconcat(curcmd, last_bar_line + x, 1);
422 } else if (last_bar_line[x] == '^' && !skip) {
423 cmd = 1;
424 chunk = xmalloc(sizeof(struct bar_chunk));
425 memset(chunk, 0, sizeof(struct bar_chunk));
426 chunk->text = xstrdup(sbuf_get(curtxt));
427 chunk->length = strlen(chunk->text);
428 chunk->color = color ? xstrdup(color) : NULL;
429 chunk->font = font ? xstrdup(font) : NULL;
430 chunk->clickcmd = clickcmd ? xstrdup(clickcmd) : NULL;
431 list_add_tail(&chunk->node, &bar_chunks);
432 sbuf_clear(curtxt);
433 } else {
434 sbuf_nconcat(curtxt, last_bar_line + x, 1);
435 }
436
437 skip = 0;
438 }
439 tline = sbuf_get(curtxt);
440 if (strlen(tline)) {
441 chunk = xmalloc(sizeof(struct bar_chunk));
442 memset(chunk, 0, sizeof(struct bar_chunk));
443 chunk->text = xstrdup(sbuf_get(curtxt));
444 chunk->length = strlen(chunk->text);
445 chunk->color = color ? xstrdup(color) : NULL;
446 chunk->font = font ? xstrdup(font) : NULL;
447 chunk->clickcmd = clickcmd ? xstrdup(clickcmd) : NULL;
448 list_add_tail(&chunk->node, &bar_chunks);
449 }
450 sbuf_free(curcmd);
451 sbuf_free(curtxt);
452 if (color)
453 free(color);
454 if (font)
455 free(font);
456 if (clickcmd)
457 free(clickcmd);
458
459redraw_bar_text:
460 XFillRectangle(dpy, bar_pm, s->inverse_gc, 0, FONT_HEIGHT(s), s->width,
461 FONT_HEIGHT(s));
462 xftx = 0;
463 list_for_each_entry(chunk, &bar_chunks, node) {
464 rp_draw_string(s, bar_pm, STYLE_NORMAL,
465 (width / 2) + xftx,
466 FONT_HEIGHT(s) + FONT_ASCENT(s),
467 chunk->text, chunk->length,
468 chunk->font ? chunk->font : NULL,
469 chunk->color ? chunk->color : NULL);
470
471 xftx += (chunk->text_width = rp_text_width(s, chunk->text,
472 chunk->length, chunk->font));
473 }
474
475 /* update each chunk's text_x relative to its final location */
476 x = 0;
477 list_for_each_entry(chunk, &bar_chunks, node) {
478 chunk->text_x = width - xftx - defaults.bar_x_padding + x;
479 x += chunk->text_width;
480 }
481
482 /* we can only copy up to half of the bar width */
483 if (xftx > (width / 2) - (defaults.bar_x_padding * 2))
484 xftx = (width / 2) - (defaults.bar_x_padding * 2);
485
486 XCopyArea(dpy, bar_pm, s->bar_window, s->inverse_gc,
487 xftx + (defaults.bar_x_padding * 2), FONT_HEIGHT(s),
488 (width / 2) - (defaults.bar_x_padding * 2), FONT_HEIGHT(s),
489 (width / 2) + defaults.bar_x_padding, defaults.bar_y_padding);
490
491 /* Our XMapRaise may have covered one */
492 raise_utility_windows();
493}
494
495void
496bar_handle_click(rp_screen *s, XButtonEvent *e)
497{
498 struct bar_chunk *chunk;
499 char *cmd, *actcmd;
500 int btn, len;
501
502 PRINT_DEBUG(("bar click at %d,%d button %d\n", e->x, e->y, e->button));
503
504 list_for_each_entry(chunk, &bar_chunks, node) {
505 PRINT_DEBUG(("chunk: text_x:%d text_width:%d text:%s\n",
506 chunk->text_x, chunk->text_width, chunk->text));
507
508 if (!chunk->clickcmd)
509 continue;
510
511 if (e->x < chunk->text_x ||
512 e->x > (chunk->text_x + chunk->text_width))
513 continue;
514
515 /* 1,somecmd,2,someothercmd */
516 cmd = chunk->clickcmd;
517 PRINT_DEBUG(("chunk: parsing btns/cmds:%s\n", cmd));
518
519 while (cmd != NULL && cmd[0] != '\0') {
520 len = strlen(cmd);
521 actcmd = xmalloc(len);
522 memset(actcmd, 0, len);
523 if (sscanf(cmd, "%d,%[^,]%n", &btn, actcmd, &len) != 2) {
524 PRINT_DEBUG(("chunk: invalid format\n"));
525 free(actcmd);
526 break;
527 }
528
529 PRINT_DEBUG(("chunk: btn:%d cmd:%s\n", btn, actcmd));
530
531 if (e->button == btn) {
532 PRINT_DEBUG(("executing bar click action %s\n",
533 actcmd));
534 spawn(actcmd, current_frame(rp_current_vscreen));
535 free(actcmd);
536 break;
537 }
538
539 cmd += len;
540 free(actcmd);
541 if (cmd[0] != ',')
542 break;
543 cmd++;
544 }
545 }
546}
547
548/*
549 * Note that we use marked_message_internal to avoid resetting the alarm.
550 */
551void
552update_window_names(rp_screen *s, char *fmt)
553{
554 struct sbuf *bar_buffer;
555 int mark_start = 0;
556 int mark_end = 0;
557 char *delimiter;
558
559 bar_buffer = sbuf_new(0);
560
561 if (s->bar_is_raised == BAR_IS_STICKY) {
562 redraw_sticky_bar_text(0);
563 } else if (s->bar_is_raised == BAR_IS_WINDOW_LIST) {
564 delimiter = (defaults.window_list_style == STYLE_ROW) ?
565 " " : "\n";
566
567 get_window_list(fmt, delimiter, bar_buffer, &mark_start,
568 &mark_end);
569 marked_message(sbuf_get(bar_buffer), mark_start, mark_end,
570 BAR_IS_WINDOW_LIST);
571 }
572
573 sbuf_free(bar_buffer);
574}
575
576/*
577 * Note that we use marked_message_internal to avoid resetting the alarm.
578 */
579void
580update_vscreen_names(rp_screen *s)
581{
582 struct sbuf *bar_buffer;
583 int mark_start = 0;
584 int mark_end = 0;
585 char *delimiter;
586
587 if (s->bar_is_raised != BAR_IS_VSCREEN_LIST)
588 return;
589
590 delimiter = (defaults.window_list_style == STYLE_ROW) ? " " : "\n";
591
592 bar_buffer = sbuf_new(0);
593
594 get_vscreen_list(s, delimiter, bar_buffer, &mark_start, &mark_end);
595 marked_message_internal(sbuf_get(bar_buffer), mark_start, mark_end,
596 BAR_IS_VSCREEN_LIST);
597
598 sbuf_free(bar_buffer);
599}
600
601void
602message(char *s)
603{
604 marked_message(s, 0, 0, BAR_IS_MESSAGE);
605}
606
607void
608marked_message_printf(int mark_start, int mark_end, char *fmt,...)
609{
610 char *buffer;
611 va_list ap;
612
613 va_start(ap, fmt);
614 buffer = xvsprintf(fmt, ap);
615 va_end(ap);
616
617 marked_message(buffer, mark_start, mark_end, BAR_IS_MESSAGE);
618 free(buffer);
619}
620
621static int
622count_lines(char *msg, int len)
623{
624 int ret = 1;
625 int i;
626
627 if (len < 1)
628 return 1;
629
630 for (i = 0; i < len; i++) {
631 if (msg[i] == '\n')
632 ret++;
633 }
634
635 return ret;
636}
637
638
639static int
640max_line_length(char *msg)
641{
642 rp_screen *s = screen_primary();
643 size_t i;
644 size_t start;
645 int ret = 0;
646
647 /* Count each line and keep the length of the longest one. */
648 for (start = 0, i = 0; i <= strlen(msg); i++) {
649 if (msg[i] == '\n' || msg[i] == '\0') {
650 int current_width;
651
652 /* Check if this line is the longest so far. */
653 current_width = rp_text_width(s, msg + start,
654 i - start, NULL);
655 if (current_width > ret) {
656 ret = current_width;
657 }
658 /* Update the start of the new line. */
659 start = i + 1;
660 }
661 }
662
663 return ret;
664}
665
666static int
667pos_in_line(char *msg, int pos)
668{
669 int ret;
670 int i;
671
672 if (pos <= 0)
673 return 0;
674
675 /*
676 * Go backwards until we hit the beginning of the string or a new line.
677 */
678 ret = 0;
679 for (i = pos - 1; i >= 0; ret++, i--) {
680 if (msg[i] == '\n')
681 break;
682 }
683
684 return ret;
685}
686
687static int
688line_beginning(char *msg, int pos)
689{
690 int ret = 0;
691 int i;
692
693 if (pos <= 0)
694 return 0;
695
696 /*
697 * Go backwards until we hit a new line or the beginning of the string.
698 */
699 for (i = pos - 1; i >= 0; --i) {
700 if (msg[i] == '\n') {
701 ret = i + 1;
702 break;
703 }
704 }
705
706 return ret;
707}
708
709static void
710draw_partial_string(rp_screen *s, char *msg, int len, int x_offset,
711 int y_offset, int style, char *color)
712{
713 rp_draw_string(s, s->bar_window, style,
714 defaults.bar_x_padding + x_offset,
715 defaults.bar_y_padding + FONT_ASCENT(s) + y_offset * FONT_HEIGHT(s),
716 msg, len + 1, NULL, color);
717}
718
719#define REASON_NONE 0x00
720#define REASON_STYLE 0x01
721#define REASON_NEWLINE 0x02
722static void
723draw_string(rp_screen *s, char *msg, int mark_start, int mark_end)
724{
725 int i, start;
726 int x_offset, y_offset; /* Base coordinates where to print. */
727 int print_reason = REASON_NONE; /* Should we print something? */
728 int style = STYLE_NORMAL, next_style = STYLE_NORMAL;
729 int msg_len, part_len;
730
731 start = 0;
732 x_offset = y_offset = 0;
733 msg_len = strlen(msg);
734
735 /* Walk through the string, print each part. */
736 for (i = 0; i < msg_len; ++i) {
737 /* Should we ignore style hints? */
738 if (mark_start != mark_end) {
739 if (i == mark_start) {
740 next_style = STYLE_INVERSE;
741 if (i > start)
742 print_reason |= REASON_STYLE;
743 } else if (i == mark_end) {
744 next_style = STYLE_NORMAL;
745 if (i > start)
746 print_reason |= REASON_STYLE;
747 }
748 }
749 if (msg[i] == '\n')
750 print_reason |= REASON_NEWLINE;
751
752 if (print_reason != REASON_NONE) {
753 /* Strip the trailing newline if necessary. */
754 part_len = i - start -
755 ((print_reason & REASON_NEWLINE) ? 1 : 0);
756
757 draw_partial_string(s, msg + start, part_len,
758 x_offset, y_offset, style, NULL);
759
760 /* Adjust coordinates. */
761 if (print_reason & REASON_NEWLINE) {
762 x_offset = 0;
763 y_offset++;
764 /* Skip newline. */
765 start = i + 1;
766 } else {
767 x_offset += rp_text_width(s, msg + start,
768 part_len, NULL);
769 start = i;
770 }
771
772 print_reason = REASON_NONE;
773 }
774 style = next_style;
775 }
776
777 part_len = i - start - 1;
778
779 /* Print the last line. */
780 draw_partial_string(s, msg + start, part_len, x_offset, y_offset,
781 style, NULL);
782
783 XSync(dpy, False);
784}
785#undef REASON_NONE
786#undef REASON_STYLE
787#undef REASON_NEWLINE
788
789/*
790 * Move the marks if they are outside the string or if the start is after the
791 * end.
792 */
793static void
794correct_mark(int msg_len, int *mark_start, int *mark_end)
795{
796 /* Make sure the marks are inside the string. */
797 if (*mark_start < 0)
798 *mark_start = 0;
799
800 if (*mark_end < 0)
801 *mark_end = 0;
802
803 if (*mark_start > msg_len)
804 *mark_start = msg_len;
805
806 if (*mark_end > msg_len)
807 *mark_end = msg_len;
808
809 /* Make sure the marks aren't reversed. */
810 if (*mark_start > *mark_end) {
811 int tmp;
812 tmp = *mark_start;
813 *mark_start = *mark_end;
814 *mark_end = tmp;
815 }
816}
817
818/* Raise the bar and put it in the right spot */
819static void
820prepare_bar(rp_screen *s, int width, int height, int bar_type)
821{
822 if (defaults.bar_sticky)
823 width = s->width - (defaults.bar_border_width * 2);
824 else
825 width = width < s->width ? width : s->width;
826 if (!defaults.bar_in_padding)
827 width -= defaults.padding_right + defaults.padding_left;
828 height = height < s->height ? height : s->height;
829 XMoveResizeWindow(dpy, s->bar_window, bar_x(s, width), bar_y(s, height),
830 width, height);
831
832 /* Map the bar if needed */
833 if (!BAR_IS_RAISED(s)) {
834 s->bar_is_raised = bar_type;
835 XMapRaised(dpy, s->bar_window);
836
837 /* Switch to the default colormap */
838 if (current_window())
839 XUninstallColormap(dpy, current_window()->colormap);
840 XInstallColormap(dpy, s->def_cmap);
841 }
842 XRaiseWindow(dpy, s->bar_window);
843 XClearWindow(dpy, s->bar_window);
844
845 raise_utility_windows();
846
847 XSync(dpy, False);
848}
849
850static void
851get_mark_box(char *msg, size_t mark_start, size_t mark_end, int *x, int *y,
852 int *width, int *height)
853{
854 rp_screen *s = rp_current_screen;
855 int start, end;
856 int mark_end_is_new_line = 0;
857 int start_line;
858 int end_line;
859 int start_pos_in_line;
860 int end_pos_in_line;
861 int start_line_beginning;
862 int end_line_beginning;
863
864 /*
865 * If the mark_end is on a new line or the end of the string, then back
866 * it up one character.
867 */
868 if (msg[mark_end - 1] == '\n' || mark_end == strlen(msg)) {
869 mark_end--;
870 mark_end_is_new_line = 1;
871 }
872 start_line = count_lines(msg, mark_start);
873 end_line = count_lines(msg, mark_end);
874
875 start_pos_in_line = pos_in_line(msg, mark_start);
876 end_pos_in_line = pos_in_line(msg, mark_end);
877
878 start_line_beginning = line_beginning(msg, mark_start);
879 end_line_beginning = line_beginning(msg, mark_end);
880
881 PRINT_DEBUG(("start_line = %d, end_line = %d\n", start_line, end_line));
882 PRINT_DEBUG(("start_line_beginning = %d, end_line_beginning = %d\n",
883 start_line_beginning, end_line_beginning));
884
885 if (mark_start == 0 || start_pos_in_line == 0)
886 start = 0;
887 else
888 start = rp_text_width(s, &msg[start_line_beginning],
889 start_pos_in_line, NULL) + defaults.bar_x_padding;
890
891 end = rp_text_width(s, &msg[end_line_beginning],
892 end_pos_in_line, NULL) + defaults.bar_x_padding * 2;
893
894 if (mark_end != strlen(msg))
895 end -= defaults.bar_x_padding;
896
897 /*
898 * A little hack to highlight to the end of the line, if the mark_end
899 * is at the end of a line.
900 */
901 if (mark_end_is_new_line) {
902 *width = max_line_length(msg) + defaults.bar_x_padding * 2;
903 } else {
904 *width = end - start;
905 }
906
907 *x = start;
908 *y = (start_line - 1) * FONT_HEIGHT(s) + defaults.bar_y_padding;
909 *height = (end_line - start_line + 1) * FONT_HEIGHT(s);
910}
911
912static void
913draw_box(rp_screen *s, int x, int y, int width, int height)
914{
915 XGCValues lgv;
916 GC lgc;
917 unsigned long mask;
918
919 lgv.foreground = rp_glob_screen.fg_color;
920 mask = GCForeground;
921 lgc = XCreateGC(dpy, s->root, mask, &lgv);
922
923 XFillRectangle(dpy, s->bar_window, lgc, x, y, width, height);
924 XFreeGC(dpy, lgc);
925}
926
927static void
928draw_mark(rp_screen *s, char *msg, int mark_start, int mark_end)
929{
930 int x, y, width, height;
931
932 /* when this happens, there is no mark. */
933 if (mark_end == 0 || mark_start == mark_end)
934 return;
935
936 get_mark_box(msg, mark_start, mark_end,
937 &x, &y, &width, &height);
938 draw_box(s, x, y, width, height);
939}
940
941static void
942update_last_message(char *msg, int mark_start, int mark_end)
943{
944 free(last_msg);
945 last_msg = xstrdup(msg);
946 last_mark_start = mark_start;
947 last_mark_end = mark_end;
948}
949
950void
951marked_message(char *msg, int mark_start, int mark_end, int bar_type)
952{
953 /* Schedule the bar to be hidden after some amount of time. */
954 bar_reset_alarm();
955 marked_message_internal(msg, mark_start, mark_end, bar_type);
956}
957
958static void
959marked_message_internal(char *msg, int mark_start, int mark_end, int bar_type)
960{
961 rp_screen *s;
962 int num_lines;
963 int width;
964 int height;
965
966 if (bar_type == BAR_IS_STICKY)
967 s = screen_primary();
968 else
969 s = rp_current_screen;
970
971 PRINT_DEBUG(("msg = %s\n", msg ? msg : "NULL"));
972 PRINT_DEBUG(("mark_start = %d, mark_end = %d\n", mark_start, mark_end));
973
974 /* Calculate the width and height of the window. */
975 num_lines = count_lines(msg, strlen(msg));
976 width = defaults.bar_x_padding * 2 + max_line_length(msg);
977 if (!defaults.bar_in_padding)
978 width -= defaults.padding_right + defaults.padding_left;
979 height = FONT_HEIGHT(s) * num_lines + defaults.bar_y_padding * 2;
980
981 prepare_bar(s, width, height, bar_type);
982
983 /* Draw the mark over the designated part of the string. */
984 correct_mark(strlen(msg), &mark_start, &mark_end);
985 draw_mark(s, msg, mark_start, mark_end);
986
987 draw_string(s, msg, mark_start, mark_end);
988
989 /* Keep a record of the message. */
990 update_last_message(msg, mark_start, mark_end);
991
992 if (bar_type != BAR_IS_STICKY && bar_time_left())
993 bar_reset_alarm();
994}
995
996/*
997 * Use this just to update the bar. show_last_message will draw it and leave it
998 * up for a period of time.
999 */
1000void
1001redraw_last_message(void)
1002{
1003 char *msg;
1004
1005 if (last_msg == NULL)
1006 return;
1007
1008 /*
1009 * A little kludge to avoid last_msg in marked_message from being
1010 * strdup'd right after freeing the pointer. Note: in this case
1011 * marked_message's msg arg would have been the same as last_msg.
1012 */
1013 msg = xstrdup(last_msg);
1014 marked_message_internal(msg, last_mark_start, last_mark_end,
1015 BAR_IS_MESSAGE);
1016 free(msg);
1017}
1018
1019void
1020show_last_message(void)
1021{
1022 redraw_last_message();
1023 bar_reset_alarm();
1024}
1025
1026/* Free any memory associated with the bar. */
1027void
1028free_bar(void)
1029{
1030 free(last_msg);
1031 last_msg = NULL;
1032}
1033
1034int
1035bar_open_fifo(void)
1036{
1037 rp_glob_screen.bar_fifo_fd = open(rp_glob_screen.bar_fifo_path,
1038 O_RDONLY|O_NONBLOCK|O_CLOEXEC);
1039 if (rp_glob_screen.bar_fifo_fd == -1) {
1040 warn("failed opening newly-created bar FIFO at %s",
1041 rp_glob_screen.bar_fifo_path);
1042 rp_glob_screen.bar_fifo_fd = -1;
1043 return -1;
1044 }
1045
1046 return 0;
1047}
1048
1049void
1050bar_read_fifo(void)
1051{
1052 ssize_t ret;
1053 int x, start;
1054
1055 PRINT_DEBUG(("bar FIFO data to read\n"));
1056
1057 for (;;) {
1058 memset(bar_tmp_line, 0, sizeof(bar_tmp_line));
1059 ret = read(rp_glob_screen.bar_fifo_fd, &bar_tmp_line,
1060 sizeof(bar_tmp_line));
1061 if (ret < 1) {
1062 if (ret == 0)
1063 PRINT_DEBUG(("FIFO %d closed, re-opening\n",
1064 rp_glob_screen.bar_fifo_fd));
1065 else if (ret == -1 && errno != EAGAIN)
1066 PRINT_DEBUG(("error reading bar FIFO: %s\n",
1067 strerror(errno)));
1068
1069 close(rp_glob_screen.bar_fifo_fd);
1070 bar_open_fifo();
1071 break;
1072 }
1073
1074 for (x = 0, start = 0; x < ret; x++) {
1075 if (bar_tmp_line[x] == '\0') {
1076 sbuf_nconcat(bar_buf, bar_tmp_line + start,
1077 x - start);
1078 start = x + 1;
1079 break;
1080 } else if (bar_tmp_line[x] == '\n') {
1081 sbuf_nconcat(bar_buf, bar_tmp_line + start,
1082 x - start);
1083 sbuf_copy(bar_line, sbuf_get(bar_buf));
1084 redraw_sticky_bar_text(0);
1085 sbuf_clear(bar_buf);
1086 start = x + 1;
1087 }
1088 }
1089
1090 if (x == ret)
1091 sbuf_nconcat(bar_buf, bar_tmp_line + start, x - start);
1092 }
1093}