jcs ratpoison hax
at master 729 lines 17 kB view raw
1/* Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 2 * Copyright (C) 2016 Mathieu OTHACEHE <m.othacehe@gmail.com> 3 * 4 * This file is part of ratpoison. 5 * 6 * ratpoison is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2, or (at your option) 9 * any later version. 10 * 11 * ratpoison is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this software; see the file COPYING. If not, write to 18 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, 19 * Boston, MA 02111-1307 USA 20 */ 21 22#include "ratpoison.h" 23#include <string.h> 24#include <X11/cursorfont.h> 25 26static void init_screen (rp_screen *s); 27 28int 29screen_width (rp_screen *s) 30{ 31 return s->width - defaults.padding_right - defaults.padding_left; 32} 33 34int 35screen_height (rp_screen *s) 36{ 37 return s->height - defaults.padding_bottom - defaults.padding_top; 38} 39 40int 41screen_left (rp_screen *s) 42{ 43 return s->left + defaults.padding_left; 44} 45 46int 47screen_right (rp_screen *s) 48{ 49 return screen_left (s) + screen_width (s); 50} 51 52int 53screen_top (rp_screen *s) 54{ 55 return s->top + defaults.padding_top; 56} 57 58int 59screen_bottom (rp_screen *s) 60{ 61 return screen_top (s) + screen_height (s); 62} 63 64/* Returns a pointer to a list of frames. */ 65struct list_head * 66screen_copy_frameset (rp_screen *s) 67{ 68 struct list_head *head; 69 rp_frame *cur; 70 71 /* Init our new list. */ 72 head = xmalloc (sizeof (struct list_head)); 73 INIT_LIST_HEAD (head); 74 75 /* Copy each frame to our new list. */ 76 list_for_each_entry (cur, &s->frames, node) 77 { 78 list_add_tail (&(frame_copy (cur))->node, head); 79 } 80 81 return head; 82} 83 84/* Set head as the frameset, deleting the existing one. */ 85void 86screen_restore_frameset (rp_screen *s, struct list_head *head) 87{ 88 frameset_free (&s->frames); 89 INIT_LIST_HEAD (&s->frames); 90 91 /* Hook in our new frameset. */ 92 list_splice (head, &s->frames); 93} 94 95 96/* Given a screen, free the frames' numbers from the numset. */ 97void 98screen_free_nums (rp_screen *s) 99{ 100 rp_frame *cur; 101 102 list_for_each_entry (cur, &s->frames, node) 103 { 104 numset_release (s->frames_numset, cur->number); 105 } 106} 107 108/* Given a list of frames, free them, but don't remove their numbers 109 from the numset. */ 110void 111frameset_free (struct list_head *head) 112{ 113 rp_frame *frame; 114 struct list_head *iter, *tmp; 115 116 list_for_each_safe_entry (frame, iter, tmp, head, node) 117 { 118 /* FIXME: what if frames has memory inside its struct 119 that needs to be freed? */ 120 free (frame); 121 } 122} 123 124rp_frame * 125screen_get_frame (rp_screen *s, int frame_num) 126{ 127 rp_frame *cur; 128 129 list_for_each_entry (cur, &s->frames, node) 130 { 131 if (cur->number == frame_num) 132 return cur; 133 } 134 135 return NULL; 136} 137 138rp_frame * 139screen_find_frame_by_frame (rp_screen *s, rp_frame *f) 140{ 141 rp_frame *cur; 142 143 list_for_each_entry (cur, &s->frames, node) 144 { 145 PRINT_DEBUG (("cur=%p f=%p\n", cur, f)); 146 if (cur == f) 147 return cur; 148 } 149 150 return NULL; 151} 152 153/* Given a root window, return the rp_screen struct */ 154rp_screen * 155find_screen (Window w) 156{ 157 rp_screen *cur; 158 159 list_for_each_entry (cur, &rp_screens, node) 160 { 161 if (cur->root == w) 162 return cur; 163 } 164 165 return NULL; 166} 167 168/* Given a window attr, return the rp_screen struct */ 169rp_screen * 170find_screen_by_attr (XWindowAttributes attr) 171{ 172 rp_screen *cur; 173 174 list_for_each_entry (cur, &rp_screens, node) 175 { 176 if (attr.x >= cur->left && 177 attr.x <= cur->left + cur->width && 178 attr.y >= cur->top && 179 attr.y <= cur->top + cur->height) 180 return cur; 181 } 182 183 return NULL; 184} 185 186/* Return 1 if w is a root window of any of the screens. */ 187int 188is_a_root_window (unsigned int w) 189{ 190 rp_screen *cur; 191 192 list_for_each_entry (cur, &rp_screens, node) 193 { 194 if (cur->root == w) 195 return 1; 196 } 197 198 return 0; 199} 200 201rp_screen * 202screen_number (int number) 203{ 204 rp_screen *cur; 205 206 list_for_each_entry (cur, &rp_screens, node) 207 { 208 if (cur->number == number) 209 return cur; 210 } 211 212 return NULL; 213} 214 215static int 216screen_cmp (void *priv UNUSED, struct list_head *a, struct list_head *b) 217{ 218 rp_screen *sc_a = container_of (a, typeof(*sc_a), node); 219 rp_screen *sc_b = container_of (b, typeof(*sc_b), node); 220 221 if (sc_a->left < sc_b->left) 222 return -1; 223 if (sc_a->left > sc_b->left) 224 return 1; 225 226 if (sc_a->top > sc_b->top) 227 return -1; 228 if (sc_a->top < sc_b->top) 229 return 1; 230 231 return 0; 232} 233 234void 235screen_sort (void) 236{ 237 return list_sort (NULL, &rp_screens, screen_cmp); 238} 239 240static void 241screen_set_numbers (void) 242{ 243 rp_screen *cur; 244 245 list_for_each_entry (cur, &rp_screens, node) 246 { 247 cur->number = numset_request (rp_glob_screen.numset); 248 } 249} 250 251static void 252screen_select_primary (void) 253{ 254 rp_screen *cur; 255 256 /* By default, take the first screen as current screen */ 257 list_first(cur, &rp_screens, node); 258 if (!rp_current_screen) 259 rp_current_screen = cur; 260 261 if (!rp_have_xrandr) 262 return; 263 264 list_for_each_entry (cur, &rp_screens, node) 265 { 266 if (xrandr_is_primary(cur)) { 267 rp_current_screen = cur; 268 PRINT_DEBUG(("Xrandr primary screen %d detected\n", 269 rp_current_screen->number)); 270 break; 271 } 272 } 273} 274 275static void 276init_global_screen (rp_global_screen *s) 277{ 278 int screen_num; 279 280 screen_num = DefaultScreen (dpy); 281 s->root = RootWindow (dpy, screen_num); 282 283 s->numset = numset_new (); 284 s->fg_color = BlackPixel (dpy, screen_num); 285 s->bg_color = WhitePixel (dpy, screen_num); 286 s->fw_color = BlackPixel (dpy, screen_num); 287 s->bw_color = BlackPixel (dpy, screen_num); 288} 289 290void 291init_screens (void) 292{ 293 int i; 294 int screen_count = 0; 295 int *rr_outputs = NULL; 296 rp_screen *screen; 297 298 /* Get the number of screens */ 299 if (rp_have_xrandr) 300 screen_count = xrandr_query_screen (&rr_outputs); 301 else 302 screen_count = ScreenCount (dpy); 303 304 /* Create our global frame numset */ 305 rp_frame_numset = numset_new(); 306 307 init_global_screen (&rp_glob_screen); 308 309 for (i = 0; i < screen_count; i++) 310 { 311 screen = xmalloc (sizeof(*screen)); 312 list_add (&screen->node, &rp_screens); 313 314 if (rp_have_xrandr) 315 xrandr_fill_screen (rr_outputs[i], screen); 316 else 317 xrandr_fill_screen (i, screen); 318 319 init_screen (screen); 320 } 321 322 screen_sort (); 323 screen_set_numbers (); 324 screen_select_primary (); 325 326 free (rr_outputs); 327} 328 329static void 330init_rat_cursor (rp_screen *s) 331{ 332 s->rat = XCreateFontCursor (dpy, XC_icon); 333} 334 335static void 336init_screen (rp_screen *s) 337{ 338 XGCValues gcv; 339 struct sbuf *buf; 340 char *colon; 341 int screen_num; 342 343 screen_num = DefaultScreen (dpy); 344 345 if (!rp_have_xrandr) 346 { 347 s->left = 0; 348 s->top = 0; 349 s->width = DisplayWidth (dpy, screen_num); 350 s->height = DisplayHeight (dpy, screen_num); 351 } 352 353 /* Select on some events on the root window, if this fails, then 354 there is already a WM running and the X Error handler will catch 355 it, terminating ratpoison. */ 356 XSelectInput (dpy, RootWindow (dpy, screen_num), 357 PropertyChangeMask | ColormapChangeMask 358 | SubstructureRedirectMask | SubstructureNotifyMask 359 | StructureNotifyMask); 360 XSync (dpy, False); 361 362 /* Set the numset for the frames to our global numset. */ 363 s->frames_numset = rp_frame_numset; 364 365 s->scratch_buffer = NULL; 366 367 /* Build the display string for each screen */ 368 buf = sbuf_new (0); 369 sbuf_printf (buf, "DISPLAY=%s", DisplayString (dpy)); 370 colon = strrchr (sbuf_get (buf), ':'); 371 if (colon) 372 { 373 char *dot; 374 375 dot = strrchr (sbuf_get (buf), '.'); 376 if (!dot || dot < colon) 377 { 378 /* no dot was found or it belongs to fqdn - append screen_num 379 to the end */ 380 sbuf_printf_concat (buf, ".%d", screen_num); 381 } 382 } 383 s->display_string = sbuf_free_struct (buf); 384 385 PRINT_DEBUG (("display string: %s\n", s->display_string)); 386 387 s->root = RootWindow (dpy, screen_num); 388 s->screen_num = screen_num; 389 s->def_cmap = DefaultColormap (dpy, screen_num); 390 391 init_rat_cursor (s); 392 393 /* Setup the GC for drawing the font. */ 394 gcv.foreground = rp_glob_screen.fg_color; 395 gcv.background = rp_glob_screen.bg_color; 396 gcv.function = GXcopy; 397 gcv.line_width = 1; 398 gcv.subwindow_mode = IncludeInferiors; 399 s->normal_gc = XCreateGC(dpy, s->root, 400 GCForeground | GCBackground | GCFunction 401 | GCLineWidth | GCSubwindowMode, 402 &gcv); 403 gcv.foreground = rp_glob_screen.bg_color; 404 gcv.background = rp_glob_screen.fg_color; 405 s->inverse_gc = XCreateGC(dpy, s->root, 406 GCForeground | GCBackground | GCFunction 407 | GCLineWidth | GCSubwindowMode, 408 &gcv); 409 410 /* Create the program bar window. */ 411 s->bar_is_raised = 0; 412 s->bar_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 413 defaults.bar_border_width, 414 rp_glob_screen.fg_color, rp_glob_screen.bg_color); 415 416 /* Setup the window that will receive all keystrokes once the prefix 417 key has been pressed. */ 418 s->key_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 0, 419 WhitePixel (dpy, screen_num), 420 BlackPixel (dpy, screen_num)); 421 XSelectInput (dpy, s->key_window, KeyPressMask | KeyReleaseMask); 422 423 /* Create the input window. */ 424 s->input_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 425 defaults.bar_border_width, 426 rp_glob_screen.fg_color, rp_glob_screen.bg_color); 427 XSelectInput (dpy, s->input_window, KeyPressMask | KeyReleaseMask); 428 429 /* Create the frame indicator window */ 430 s->frame_window = XCreateSimpleWindow (dpy, s->root, 1, 1, 1, 1, defaults.bar_border_width, 431 rp_glob_screen.fg_color, rp_glob_screen.bg_color); 432 433 /* Create the help window */ 434 s->help_window = XCreateSimpleWindow (dpy, s->root, s->left, s->top, s->width, 435 s->height, 0, rp_glob_screen.fg_color, rp_glob_screen.bg_color); 436 XSelectInput (dpy, s->help_window, KeyPressMask); 437 438 activate_screen(s); 439 440 XSync (dpy, 0); 441 442#ifdef USE_XFT_FONT 443 { 444 s->xft_font = XftFontOpenName (dpy, screen_num, DEFAULT_XFT_FONT); 445 if (!s->xft_font) 446 { 447 PRINT_ERROR(("Failed to open font\n")); 448 } 449 else 450 { 451 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num), 452 DefaultColormap (dpy, screen_num), 453 defaults.fgcolor_string, &s->xft_fg_color)) 454 { 455 PRINT_ERROR(("Failed to allocate font fg color\n")); 456 XftFontClose (dpy, s->xft_font); 457 s->xft_font = NULL; 458 } 459 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num), 460 DefaultColormap (dpy, screen_num), 461 defaults.bgcolor_string, &s->xft_bg_color)) 462 { 463 PRINT_ERROR(("Failed to allocate font fg color\n")); 464 XftFontClose (dpy, s->xft_font); 465 s->xft_font = NULL; 466 } 467 } 468 } 469#endif 470} 471 472void 473activate_screen (rp_screen *s) 474{ 475 /* Add netwm support. FIXME: I think this is busted. */ 476 XChangeProperty (dpy, RootWindow (dpy, s->screen_num), 477 _net_supported, XA_ATOM, 32, PropModeReplace, 478 (unsigned char*)&_net_wm_pid, 1); 479 480 /* set window manager name */ 481 XChangeProperty (dpy, RootWindow (dpy, s->screen_num), 482 _net_wm_name, xa_utf8_string, 8, PropModeReplace, 483 (unsigned char*)"ratpoison", 9); 484 XMapWindow (dpy, s->key_window); 485} 486 487void 488deactivate_screen (rp_screen *s) 489{ 490 /* Unmap its key window */ 491 XUnmapWindow (dpy, s->key_window); 492 493 /* delete everything so noone sees them while we are not there */ 494 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num), 495 _net_supported); 496 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num), 497 _net_wm_name); 498} 499 500static int 501is_rp_window_for_given_screen (Window w, rp_screen *s) 502{ 503 if (w != s->key_window && 504 w != s->bar_window && 505 w != s->input_window && 506 w != s->frame_window && 507 w != s->help_window) 508 return 0; 509 return 1; 510} 511 512int 513is_rp_window (Window w) 514{ 515 rp_screen *cur; 516 517 list_for_each_entry (cur, &rp_screens, node) 518 { 519 if (is_rp_window_for_given_screen (w, cur)) 520 return 1; 521 } 522 523 return 0; 524} 525 526char * 527screen_dump (rp_screen *screen) 528{ 529 char *tmp; 530 struct sbuf *s; 531 532 s = sbuf_new (0); 533 if (rp_have_xrandr) 534 sbuf_printf(s, "%s ", screen->xrandr.name); 535 536 sbuf_printf_concat (s, "%d %d %d %d %d %d", 537 screen->number, 538 screen->left, 539 screen->top, 540 screen->width, 541 screen->height, 542 (rp_current_screen == screen)?1:0 /* is current? */ 543 ); 544 545 /* Extract the string and return it, and don't forget to free s. */ 546 tmp = sbuf_get (s); 547 free (s); 548 return tmp; 549} 550 551int 552screen_count (void) 553{ 554 return list_size (&rp_screens); 555} 556 557rp_screen * 558screen_next (void) 559{ 560 return list_next_entry (rp_current_screen, &rp_screens, node); 561} 562 563rp_screen * 564screen_prev (void) 565{ 566 return list_prev_entry (rp_current_screen, &rp_screens, node); 567} 568 569static void 570screen_remove_current (void) 571{ 572 rp_screen *new_screen; 573 rp_frame *new_frame; 574 rp_window *cur_win; 575 int cur_frame; 576 577 cur_win = current_window (); 578 new_screen = screen_next (); 579 580 cur_frame = new_screen->current_frame; 581 new_frame = screen_get_frame (new_screen, cur_frame); 582 583 set_active_frame (new_frame, 1); 584 585 hide_window (cur_win); 586} 587 588void 589screen_update (rp_screen *s, int left, int top, int width, int height) 590{ 591 rp_frame *f; 592 int oldwidth, oldheight; 593 594 PRINT_DEBUG (("screen_update (left=%d, top=%d, width=%d, height=%d)\n", 595 left, top, width, height)); 596 597 if (s->width == width && 598 s->height == height && 599 s->left == left && 600 s->top == top) 601 /* nothing to do */ 602 return; 603 604 oldwidth = s->width; 605 oldheight = s->height; 606 607 s->left = left; 608 s->top = top; 609 s->width = width; 610 s->height = height; 611 612 XMoveResizeWindow (dpy, s->help_window, s->left, s->top, s->width, s->height); 613 614 list_for_each_entry (f, &s->frames, node) 615 { 616 f->x = (f->x*width)/oldwidth; 617 f->width = (f->width*width)/oldwidth; 618 f->y = (f->y*height)/oldheight; 619 f->height = (f->height*height)/oldheight; 620 maximize_all_windows_in_frame (f); 621 } 622} 623 624rp_screen * 625screen_add (int rr_output) 626{ 627 rp_screen *screen; 628 629 screen = xmalloc (sizeof(*screen)); 630 list_add (&screen->node, &rp_screens); 631 632 screen->number = numset_request (rp_glob_screen.numset); 633 634 xrandr_fill_screen (rr_output, screen); 635 init_screen (screen); 636 init_frame_list (screen); 637 638 if (screen_count () == 1) 639 { 640 rp_current_screen = screen; 641 change_windows_screen (NULL, rp_current_screen); 642 set_window_focus (rp_current_screen->key_window); 643 } 644 645 return screen; 646} 647 648void 649screen_del (rp_screen *s) 650{ 651 if (s == rp_current_screen) 652 { 653 if (screen_count () == 1) 654 { 655 hide_screen_windows (s); 656 rp_current_screen = NULL; 657 } 658 else 659 { 660 /* 661 * The deleted screen cannot be the current screen anymore, 662 * focus the next one. 663 */ 664 screen_remove_current (); 665 } 666 } 667 else 668 { 669 hide_screen_windows (s); 670 } 671 672 /* Affect window's screen backpointer to the new current screen */ 673 change_windows_screen (s, rp_current_screen); 674 675 numset_release (rp_glob_screen.numset, s->number); 676 677 screen_free (s); 678 679 list_del (&s->node); 680 free (s); 681} 682 683void 684screen_free (rp_screen *s) 685{ 686 rp_frame *frame; 687 struct list_head *iter, *tmp; 688 689 list_for_each_safe_entry (frame, iter, tmp, &s->frames, node) 690 { 691 frame_free (s, frame); 692 } 693 694 deactivate_screen(s); 695 696 XDestroyWindow (dpy, s->bar_window); 697 XDestroyWindow (dpy, s->key_window); 698 XDestroyWindow (dpy, s->input_window); 699 XDestroyWindow (dpy, s->frame_window); 700 XDestroyWindow (dpy, s->help_window); 701 702#ifdef USE_XFT_FONT 703 if (s->xft_font) 704 { 705 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num), 706 DefaultColormap (dpy, s->screen_num), &s->xft_fg_color); 707 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num), 708 DefaultColormap (dpy, s->screen_num), &s->xft_bg_color); 709 XftFontClose (dpy, s->xft_font); 710 } 711#endif 712 713 XFreeCursor (dpy, s->rat); 714 XFreeColormap (dpy, s->def_cmap); 715 XFreeGC (dpy, s->normal_gc); 716 XFreeGC (dpy, s->inverse_gc); 717 718 free (s->display_string); 719 free (s->xrandr.name); 720} 721 722void 723screen_free_final (void) 724{ 725 /* Relinquish our hold on the root window. */ 726 XSelectInput(dpy, RootWindow (dpy, DefaultScreen (dpy)), 0); 727 728 numset_free (rp_glob_screen.numset); 729}