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