jcs ratpoison hax
at jcs 710 lines 17 kB view raw
1/* Functionality for a bar listing the windows currently managed. 2 * 3 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 4 * 5 * This file is part of ratpoison. 6 * 7 * ratpoison is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2, or (at your option) 10 * any later version. 11 * 12 * ratpoison is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this software; see the file COPYING. If not, write to 19 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 20 * Boston, MA 02111-1307 USA 21 */ 22 23#include <X11/X.h> 24#include <X11/Xlib.h> 25#include <X11/Xutil.h> 26 27#ifdef USE_XFT_FONT 28#include <X11/Xft/Xft.h> 29#endif 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <unistd.h> 35 36#include "ratpoison.h" 37 38/* Possible values for bar_is_raised status. */ 39#define BAR_IS_HIDDEN 0 40#define BAR_IS_WINDOW_LIST 1 41#define BAR_IS_GROUP_LIST 2 42#define BAR_IS_MESSAGE 3 43 44/* A copy of the last message displayed in the message bar. */ 45static char *last_msg = NULL; 46static int last_mark_start = 0; 47static int last_mark_end = 0; 48 49static void marked_message_internal (char *msg, int mark_start, int mark_end); 50 51/* Reset the alarm to auto-hide the bar in BAR_TIMEOUT seconds. */ 52static void 53reset_alarm (void) 54{ 55 alarm (defaults.bar_timeout); 56 alarm_signalled = 0; 57} 58 59/* Hide the bar from sight. */ 60int 61hide_bar (rp_screen *s) 62{ 63 if (s->bar_is_raised) 64 { 65 if (defaults.bar_sticky) 66 { 67 /* Something just wanted the current message gone. */ 68 s->bar_is_raised = BAR_IS_WINDOW_LIST; 69 show_bar(s, defaults.window_fmt); 70 } 71 else 72 { 73 s->bar_is_raised = 0; 74 XUnmapWindow (dpy, s->bar_window); 75 76 /* Possibly restore colormap. */ 77 if (current_window()) 78 { 79 XUninstallColormap (dpy, s->def_cmap); 80 XInstallColormap (dpy, current_window()->colormap); 81 } 82 } 83 return 1; 84 } 85 86 return 0; 87} 88 89/* Show window listing in bar. */ 90int 91show_bar (rp_screen *s, char *fmt) 92{ 93 if (!s->bar_is_raised) 94 { 95 s->bar_is_raised = BAR_IS_WINDOW_LIST; 96 if (defaults.bar_sticky) 97 XMapWindow (dpy, s->bar_window); 98 else 99 XMapRaised (dpy, s->bar_window); 100 101 update_window_names (s, fmt); 102 103 /* Switch to the default colormap */ 104 if (current_window()) 105 XUninstallColormap (dpy, current_window()->colormap); 106 XInstallColormap (dpy, s->def_cmap); 107 108 reset_alarm(); 109 return 1; 110 } 111 112 /* If the bar is raised we still need to display the window 113 names. */ 114 update_window_names (s, fmt); 115 return 0; 116} 117 118/* Show group listing in bar. */ 119int 120show_group_bar (rp_screen *s) 121{ 122 if (!s->bar_is_raised) 123 { 124 s->bar_is_raised = BAR_IS_GROUP_LIST; 125 XMapRaised (dpy, s->bar_window); 126 update_group_names (s); 127 128 /* Switch to the default colormap */ 129 if (current_window()) 130 XUninstallColormap (dpy, current_window()->colormap); 131 XInstallColormap (dpy, s->def_cmap); 132 133 reset_alarm(); 134 return 1; 135 } 136 137 /* If the bar is raised we still need to display the window 138 names. */ 139 update_group_names (s); 140 return 0; 141} 142 143int 144bar_x (rp_screen *s, int width) 145{ 146 int x = 0; 147 148 switch (defaults.bar_location) 149 { 150 case NorthWestGravity: 151 case WestGravity: 152 case SouthWestGravity: 153 x = s->left + (defaults.bar_in_padding ? 0 : defaults.padding_left); 154 if (defaults.bar_sticky && defaults.bar_in_padding) 155 x -= defaults.bar_sticky_bleed; 156 break; 157 case NorthGravity: 158 case CenterGravity: 159 case SouthGravity: 160 x = s->left + (s->width - width - defaults.bar_border_width * 2) / 2 161 - (defaults.bar_in_padding ? 0 : defaults.padding_left); 162 break; 163 case NorthEastGravity: 164 case EastGravity: 165 case SouthEastGravity: 166 x = s->left + s->width - width - defaults.bar_border_width * 2 167 - (defaults.bar_in_padding ? 0 : defaults.padding_right); 168 break; 169 } 170 171 return x; 172} 173 174int 175bar_y (rp_screen *s, int height) 176{ 177 int y = 0; 178 179 switch (defaults.bar_location) 180 { 181 case NorthEastGravity: 182 case NorthGravity: 183 case NorthWestGravity: 184 y = s->top + (defaults.bar_in_padding ? 0 : defaults.padding_top); 185 break; 186 case EastGravity: 187 case CenterGravity: 188 case WestGravity: 189 y = s->top + (s->height - height 190 - defaults.bar_border_width * 2) / 2 191 - (defaults.bar_in_padding ? 0 : defaults.padding_top); 192 break; 193 case SouthEastGravity: 194 case SouthGravity: 195 case SouthWestGravity: 196 y = s->top + (s->height - height 197 - defaults.bar_border_width * 2) 198 - (defaults.bar_in_padding ? 0 : defaults.padding_top); 199 break; 200 } 201 202 return y; 203} 204 205void 206update_bar (rp_screen *s) 207{ 208 if (s->bar_is_raised == BAR_IS_WINDOW_LIST) { 209 update_window_names (s, defaults.window_fmt); 210 return; 211 } 212 213 if (s->bar_is_raised == BAR_IS_GROUP_LIST) { 214 update_group_names (s); 215 return; 216 } 217 218 if (s->bar_is_raised == BAR_IS_HIDDEN) 219 return; 220 221 redraw_last_message(); 222} 223 224/* Note that we use marked_message_internal to avoid resetting the 225 alarm. */ 226void 227update_window_names (rp_screen *s, char *fmt) 228{ 229 struct sbuf *bar_buffer; 230 int mark_start = 0; 231 int mark_end = 0; 232 char *delimiter; 233 234 if (s->bar_is_raised != BAR_IS_WINDOW_LIST) return; 235 236 delimiter = (defaults.window_list_style == STYLE_ROW) ? " " : "\n"; 237 238 bar_buffer = sbuf_new (0); 239 240 get_window_list (fmt, delimiter, bar_buffer, &mark_start, &mark_end); 241 marked_message (sbuf_get (bar_buffer), mark_start, mark_end); 242 243 sbuf_free (bar_buffer); 244} 245 246/* Note that we use marked_message_internal to avoid resetting the 247 alarm. */ 248void 249update_group_names (rp_screen *s) 250{ 251 struct sbuf *bar_buffer; 252 int mark_start = 0; 253 int mark_end = 0; 254 char *delimiter; 255 256 if (s->bar_is_raised != BAR_IS_GROUP_LIST) return; 257 258 delimiter = (defaults.window_list_style == STYLE_ROW) ? " " : "\n"; 259 260 bar_buffer = sbuf_new (0); 261 262 get_group_list (delimiter, bar_buffer, &mark_start, &mark_end); 263 marked_message_internal (sbuf_get (bar_buffer), mark_start, mark_end); 264 265 sbuf_free (bar_buffer); 266} 267 268void 269message (char *s) 270{ 271 marked_message (s, 0, 0); 272} 273 274void 275marked_message_printf (int mark_start, int mark_end, char *fmt, ...) 276{ 277 char *buffer; 278 va_list ap; 279 280 va_start (ap, fmt); 281 buffer = xvsprintf (fmt, ap); 282 va_end (ap); 283 284 marked_message (buffer, mark_start, mark_end); 285 free (buffer); 286} 287 288static int 289count_lines (char* msg, int len) 290{ 291 int ret = 1; 292 int i; 293 294 if (len < 1) 295 return 1; 296 297 for(i=0; i<len; i++) 298 { 299 if (msg[i] == '\n') ret++; 300 } 301 302 return ret; 303} 304 305 306static int 307max_line_length (char* msg) 308{ 309 rp_screen *s = rp_current_screen; 310 size_t i; 311 size_t start; 312 int ret = 0; 313 314 /* Count each line and keep the length of the longest one. */ 315 for(start=0, i=0; i <= strlen(msg); i++) 316 { 317 if(msg[i] == '\n' || msg[i] == '\0') 318 { 319 int current_width; 320 321 /* Check if this line is the longest so far. */ 322 current_width = rp_text_width (s, msg + start, i - start); 323 if(current_width > ret) 324 { 325 ret = current_width; 326 } 327 328 /* Update the start of the new line. */ 329 start = i + 1; 330 } 331 } 332 333 return ret; 334} 335 336static int 337pos_in_line (char* msg, int pos) 338{ 339 int ret; 340 int i; 341 342 if(pos <= 0) 343 return 0; 344 345 /* Go backwards until we hit the beginning of the string or a new 346 line. */ 347 ret = 0; 348 for(i=pos-1; i>=0; ret++, i--) 349 { 350 if(msg[i]=='\n') 351 break; 352 } 353 354 return ret; 355} 356 357static int 358line_beginning (char* msg, int pos) 359{ 360 int ret = 0; 361 int i; 362 363 if(pos <= 0) 364 return 0; 365 366 /* Go backwards until we hit a new line or the beginning of the 367 string. */ 368 for(i=pos-1; i>=0; --i) 369 { 370 if (msg[i]=='\n') 371 { 372 ret = i + 1; 373 break; 374 } 375 } 376 377 return ret; 378} 379 380static void 381draw_partial_string (rp_screen *s, char *msg, int len, 382 int x_offset, int y_offset, int style) 383{ 384 rp_draw_string (s, s->bar_window, style, 385 defaults.bar_x_padding + x_offset + 386 (defaults.bar_sticky ? defaults.bar_sticky_bleed : 0), 387 defaults.bar_y_padding + FONT_ASCENT(s) 388 + y_offset * FONT_HEIGHT (s), 389 msg, len + 1); 390} 391 392#define REASON_NONE 0x00 393#define REASON_STYLE 0x01 394#define REASON_NEWLINE 0x02 395static void 396draw_string (rp_screen *s, char *msg, int mark_start, int mark_end) 397{ 398 int i, start; 399 int x_offset, y_offset; /* Base coordinates where to print. */ 400 int print_reason = REASON_NONE; /* Should we print something? */ 401 int style = STYLE_NORMAL, next_style = STYLE_NORMAL; 402 int msg_len, part_len; 403 404 start = 0; 405 x_offset = y_offset = 0; 406 msg_len = strlen (msg); 407 408 /* Walk through the string, print each part. */ 409 for (i = 0; i < msg_len; ++i) 410 { 411 412 /* Should we ignore style hints? */ 413 if (mark_start != mark_end) 414 { 415 if (i == mark_start) 416 { 417 next_style = STYLE_INVERSE; 418 if (i > start) 419 print_reason |= REASON_STYLE; 420 } 421 else if (i == mark_end) 422 { 423 next_style = STYLE_NORMAL; 424 if (i > start) 425 print_reason |= REASON_STYLE; 426 } 427 } 428 429 if (msg[i] == '\n') 430 print_reason |= REASON_NEWLINE; 431 432 if (print_reason != REASON_NONE) 433 { 434 /* Strip the trailing newline if necessary. */ 435 part_len = i - start - ((print_reason & REASON_NEWLINE) ? 1 : 0); 436 437 draw_partial_string (s, msg + start, part_len, 438 x_offset, y_offset, style); 439 440 /* Adjust coordinates. */ 441 if (print_reason & REASON_NEWLINE) 442 { 443 x_offset = 0; 444 y_offset++; 445 /* Skip newline. */ 446 start = i + 1; 447 } 448 else 449 { 450 x_offset += rp_text_width (s, msg + start, part_len); 451 start = i; 452 } 453 454 print_reason = REASON_NONE; 455 } 456 style = next_style; 457 } 458 459 part_len = i - start - 1; 460 461 /* Print the last line. */ 462 draw_partial_string (s, msg + start, part_len, x_offset, y_offset, style); 463 464 XSync (dpy, False); 465} 466#undef REASON_NONE 467#undef REASON_STYLE 468#undef REASON_NEWLINE 469 470/* Move the marks if they are outside the string or if the start is 471 after the end. */ 472static void 473correct_mark (int msg_len, int *mark_start, int *mark_end) 474{ 475 /* Make sure the marks are inside the string. */ 476 if (*mark_start < 0) 477 *mark_start = 0; 478 479 if (*mark_end < 0) 480 *mark_end = 0; 481 482 if (*mark_start > msg_len) 483 *mark_start = msg_len; 484 485 if (*mark_end > msg_len) 486 *mark_end = msg_len; 487 488 /* Make sure the marks aren't reversed. */ 489 if (*mark_start > *mark_end) 490 { 491 int tmp; 492 tmp = *mark_start; 493 *mark_start = *mark_end; 494 *mark_end = tmp; 495 } 496 497} 498 499/* Raise the bar and put it in the right spot */ 500static void 501prepare_bar (rp_screen *s, int width, int height, int multiline) 502{ 503 if (defaults.bar_sticky) 504 width = s->width + (defaults.bar_sticky_bleed * 2); 505 else 506 width = width < s->width ? width : s->width; 507 height = height < s->height ? height : s->height; 508 XMoveResizeWindow (dpy, s->bar_window, 509 bar_x (s, width), bar_y (s, height), 510 width, height); 511 512 /* Map the bar if needed */ 513 if (!s->bar_is_raised) 514 { 515 s->bar_is_raised = BAR_IS_MESSAGE; 516 if (defaults.bar_sticky && !multiline) 517 XMapWindow (dpy, s->bar_window); 518 else 519 XMapRaised (dpy, s->bar_window); 520 521 /* Switch to the default colormap */ 522 if (current_window()) 523 XUninstallColormap (dpy, current_window()->colormap); 524 XInstallColormap (dpy, s->def_cmap); 525 } 526 527 if (multiline || !defaults.bar_sticky) 528 XRaiseWindow (dpy, s->bar_window); 529 else if (defaults.bar_sticky) 530 XLowerWindow (dpy, s->bar_window); 531 XClearWindow (dpy, s->bar_window); 532 XSync (dpy, False); 533} 534 535static void 536get_mark_box (char *msg, size_t mark_start, size_t mark_end, 537 int *x, int *y, int *width, int *height) 538{ 539 rp_screen *s = rp_current_screen; 540 int start, end; 541 int mark_end_is_new_line = 0; 542 int start_line; 543 int end_line; 544 int start_pos_in_line; 545 int end_pos_in_line; 546 int start_line_beginning; 547 int end_line_beginning; 548 549 /* If the mark_end is on a new line or the end of the string, then 550 back it up one character. */ 551 if (msg[mark_end-1] == '\n' || mark_end == strlen (msg)) 552 { 553 mark_end--; 554 mark_end_is_new_line = 1; 555 } 556 557 start_line = count_lines(msg, mark_start); 558 end_line = count_lines(msg, mark_end); 559 560 start_pos_in_line = pos_in_line(msg, mark_start); 561 end_pos_in_line = pos_in_line(msg, mark_end); 562 563 start_line_beginning = line_beginning(msg, mark_start); 564 end_line_beginning = line_beginning(msg, mark_end); 565 566 PRINT_DEBUG (("start_line = %d, end_line = %d\n", start_line, end_line)); 567 PRINT_DEBUG (("start_line_beginning = %d, end_line_beginning = %d\n", 568 start_line_beginning, end_line_beginning)); 569 570 if (mark_start == 0 || start_pos_in_line == 0) 571 start = 0; 572 else 573 start = rp_text_width (s, &msg[start_line_beginning], 574 start_pos_in_line) + defaults.bar_x_padding; 575 576 end = rp_text_width (s, &msg[end_line_beginning], 577 end_pos_in_line) + defaults.bar_x_padding * 2; 578 579 if (mark_end != strlen (msg)) 580 end -= defaults.bar_x_padding; 581 582 /* A little hack to highlight to the end of the line, if the 583 mark_end is at the end of a line. */ 584 if (mark_end_is_new_line) 585 { 586 *width = max_line_length(msg) + defaults.bar_x_padding * 2; 587 } 588 else 589 { 590 *width = end - start; 591 } 592 593 *x = start; 594 *y = (start_line - 1) * FONT_HEIGHT (s) + defaults.bar_y_padding; 595 *height = (end_line - start_line + 1) * FONT_HEIGHT (s); 596} 597 598static void 599draw_box (rp_screen *s, int x, int y, int width, int height) 600{ 601 XGCValues lgv; 602 GC lgc; 603 unsigned long mask; 604 605 lgv.foreground = rp_glob_screen.fg_color; 606 mask = GCForeground; 607 lgc = XCreateGC(dpy, s->root, mask, &lgv); 608 609 XFillRectangle (dpy, s->bar_window, lgc, 610 x, y, width, height); 611 XFreeGC (dpy, lgc); 612} 613 614static void 615draw_mark (rp_screen *s, char *msg, int mark_start, int mark_end) 616{ 617 int x, y, width, height; 618 619 /* when this happens, there is no mark. */ 620 if (mark_end == 0 || mark_start == mark_end) 621 return; 622 623 get_mark_box (msg, mark_start, mark_end, 624 &x, &y, &width, &height); 625 draw_box (s, x, y, width, height); 626} 627 628static void 629update_last_message (char *msg, int mark_start, int mark_end) 630{ 631 free (last_msg); 632 last_msg = xstrdup (msg); 633 last_mark_start = mark_start; 634 last_mark_end = mark_end; 635} 636 637void 638marked_message (char *msg, int mark_start, int mark_end) 639{ 640 /* Schedule the bar to be hidden after some amount of time. */ 641 reset_alarm (); 642 marked_message_internal (msg, mark_start, mark_end); 643} 644 645static void 646marked_message_internal (char *msg, int mark_start, int mark_end) 647{ 648 rp_screen *s = rp_current_screen; 649 int num_lines; 650 int width; 651 int height; 652 653 PRINT_DEBUG (("msg = %s\n", msg?msg:"NULL")); 654 PRINT_DEBUG (("mark_start = %d, mark_end = %d\n", mark_start, mark_end)); 655 656 /* Calculate the width and height of the window. */ 657 num_lines = count_lines (msg, strlen(msg)); 658 width = defaults.bar_x_padding * 2 + max_line_length(msg); 659 height = FONT_HEIGHT (s) * num_lines + defaults.bar_y_padding * 2; 660 661 prepare_bar (s, width, height, num_lines > 1 ? 1 : 0); 662 663 if (defaults.bar_sticky) 664 /* Sticky bar is only showing the current window title, don't mark it */ 665 mark_start = mark_end = -1; 666 else 667 { 668 /* Draw the mark over the designated part of the string. */ 669 correct_mark (strlen (msg), &mark_start, &mark_end); 670 draw_mark (s, msg, mark_start, mark_end); 671 } 672 673 draw_string (s, msg, mark_start, mark_end); 674 675 /* Keep a record of the message. */ 676 update_last_message (msg, mark_start, mark_end); 677} 678 679/* Use this just to update the bar. show_last_message will draw it and 680 leave it up for a period of time. */ 681void 682redraw_last_message (void) 683{ 684 char *msg; 685 686 if (last_msg == NULL) return; 687 688 /* A little kludge to avoid last_msg in marked_message from being 689 strdup'd right after freeing the pointer. Note: in this case 690 marked_message's msg arg would have been the same as 691 last_msg. */ 692 msg = xstrdup (last_msg); 693 marked_message_internal (msg, last_mark_start, last_mark_end); 694 free (msg); 695} 696 697void 698show_last_message (void) 699{ 700 redraw_last_message(); 701 reset_alarm(); 702} 703 704/* Free any memory associated with the bar. */ 705void 706free_bar (void) 707{ 708 free (last_msg); 709 last_msg = NULL; 710}