A tiling window manager
at hints_fix 1093 lines 26 kB view raw
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}