jcs ratpoison hax
at jcs 740 lines 18 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 long val; 341 char *colon; 342 int screen_num; 343 344 screen_num = DefaultScreen (dpy); 345 346 if (!rp_have_xrandr) 347 { 348 s->left = 0; 349 s->top = 0; 350 s->width = DisplayWidth (dpy, screen_num); 351 s->height = DisplayHeight (dpy, screen_num); 352 } 353 354 /* Select on some events on the root window, if this fails, then 355 there is already a WM running and the X Error handler will catch 356 it, terminating ratpoison. */ 357 XSelectInput (dpy, RootWindow (dpy, screen_num), 358 PropertyChangeMask | ColormapChangeMask 359 | SubstructureRedirectMask | SubstructureNotifyMask 360 | StructureNotifyMask); 361 XSync (dpy, False); 362 363 /* Set the numset for the frames to our global numset. */ 364 s->frames_numset = rp_frame_numset; 365 366 s->scratch_buffer = NULL; 367 368 /* Build the display string for each screen */ 369 buf = sbuf_new (0); 370 sbuf_printf (buf, "DISPLAY=%s", DisplayString (dpy)); 371 colon = strrchr (sbuf_get (buf), ':'); 372 if (colon) 373 { 374 char *dot; 375 376 dot = strrchr (sbuf_get (buf), '.'); 377 if (!dot || dot < colon) 378 { 379 /* no dot was found or it belongs to fqdn - append screen_num 380 to the end */ 381 sbuf_printf_concat (buf, ".%d", screen_num); 382 } 383 } 384 s->display_string = sbuf_free_struct (buf); 385 386 PRINT_DEBUG (("display string: %s\n", s->display_string)); 387 388 s->root = RootWindow (dpy, screen_num); 389 s->screen_num = screen_num; 390 s->def_cmap = DefaultColormap (dpy, screen_num); 391 392 init_rat_cursor (s); 393 394 /* Setup the GC for drawing the font. */ 395 gcv.foreground = rp_glob_screen.fg_color; 396 gcv.background = rp_glob_screen.bg_color; 397 gcv.function = GXcopy; 398 gcv.line_width = 1; 399 gcv.subwindow_mode = IncludeInferiors; 400 s->normal_gc = XCreateGC(dpy, s->root, 401 GCForeground | GCBackground | GCFunction 402 | GCLineWidth | GCSubwindowMode, 403 &gcv); 404 gcv.foreground = rp_glob_screen.bg_color; 405 gcv.background = rp_glob_screen.fg_color; 406 s->inverse_gc = XCreateGC(dpy, s->root, 407 GCForeground | GCBackground | GCFunction 408 | GCLineWidth | GCSubwindowMode, 409 &gcv); 410 411 /* Create the program bar window. */ 412 s->bar_is_raised = 0; 413 s->bar_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 414 defaults.bar_border_width, 415 rp_glob_screen.fg_color, rp_glob_screen.bg_color); 416 val = _net_wm_window_type_dock; 417 XChangeProperty (dpy, s->bar_window, _net_wm_window_type, XA_ATOM, 32, 418 PropModeReplace, (unsigned char *)&val, 1); 419 420 /* Setup the window that will receive all keystrokes once the prefix 421 key has been pressed. */ 422 s->key_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 0, 423 WhitePixel (dpy, screen_num), 424 BlackPixel (dpy, screen_num)); 425 XSelectInput (dpy, s->key_window, KeyPressMask | KeyReleaseMask); 426 427 /* Create the input window. */ 428 s->input_window = XCreateSimpleWindow (dpy, s->root, 0, 0, 1, 1, 429 defaults.bar_border_width, 430 rp_glob_screen.fg_color, rp_glob_screen.bg_color); 431 XSelectInput (dpy, s->input_window, KeyPressMask | KeyReleaseMask); 432 XChangeProperty (dpy, s->input_window, _net_wm_window_type, XA_ATOM, 32, 433 PropModeReplace, (unsigned char *)&val, 1); 434 435 /* Create the frame indicator window */ 436 s->frame_window = XCreateSimpleWindow (dpy, s->root, 1, 1, 1, 1, defaults.bar_border_width, 437 rp_glob_screen.fg_color, rp_glob_screen.bg_color); 438 val = _net_wm_window_type_tooltip; 439 XChangeProperty (dpy, s->frame_window, _net_wm_window_type, XA_ATOM, 32, 440 PropModeReplace, (unsigned char *)&val, 1); 441 442 /* Create the help window */ 443 s->help_window = XCreateSimpleWindow (dpy, s->root, s->left, s->top, s->width, 444 s->height, 0, rp_glob_screen.fg_color, rp_glob_screen.bg_color); 445 XSelectInput (dpy, s->help_window, KeyPressMask); 446 447 activate_screen(s); 448 449 XSync (dpy, 0); 450 451#ifdef USE_XFT_FONT 452 { 453 s->xft_font = XftFontOpenName (dpy, screen_num, DEFAULT_XFT_FONT); 454 if (!s->xft_font) 455 { 456 PRINT_ERROR(("Failed to open font\n")); 457 } 458 else 459 { 460 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num), 461 DefaultColormap (dpy, screen_num), 462 defaults.fgcolor_string, &s->xft_fg_color)) 463 { 464 PRINT_ERROR(("Failed to allocate font fg color\n")); 465 XftFontClose (dpy, s->xft_font); 466 s->xft_font = NULL; 467 } 468 if (!XftColorAllocName (dpy, DefaultVisual (dpy, screen_num), 469 DefaultColormap (dpy, screen_num), 470 defaults.bgcolor_string, &s->xft_bg_color)) 471 { 472 PRINT_ERROR(("Failed to allocate font fg color\n")); 473 XftFontClose (dpy, s->xft_font); 474 s->xft_font = NULL; 475 } 476 } 477 } 478#endif 479} 480 481void 482activate_screen (rp_screen *s) 483{ 484 /* Add netwm support. FIXME: I think this is busted. */ 485 XChangeProperty (dpy, RootWindow (dpy, s->screen_num), 486 _net_supported, XA_ATOM, 32, PropModeReplace, 487 (unsigned char*)&_net_wm_pid, 1); 488 489 /* set window manager name */ 490 XChangeProperty (dpy, RootWindow (dpy, s->screen_num), 491 _net_wm_name, xa_utf8_string, 8, PropModeReplace, 492 (unsigned char*)"ratpoison", 9); 493 XMapWindow (dpy, s->key_window); 494} 495 496void 497deactivate_screen (rp_screen *s) 498{ 499 /* Unmap its key window */ 500 XUnmapWindow (dpy, s->key_window); 501 502 /* delete everything so noone sees them while we are not there */ 503 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num), 504 _net_supported); 505 XDeleteProperty (dpy, RootWindow (dpy, s->screen_num), 506 _net_wm_name); 507} 508 509static int 510is_rp_window_for_given_screen (Window w, rp_screen *s) 511{ 512 if (w != s->key_window && 513 w != s->bar_window && 514 w != s->input_window && 515 w != s->frame_window && 516 w != s->help_window) 517 return 0; 518 return 1; 519} 520 521int 522is_rp_window (Window w) 523{ 524 rp_screen *cur; 525 526 list_for_each_entry (cur, &rp_screens, node) 527 { 528 if (is_rp_window_for_given_screen (w, cur)) 529 return 1; 530 } 531 532 return 0; 533} 534 535char * 536screen_dump (rp_screen *screen) 537{ 538 char *tmp; 539 struct sbuf *s; 540 541 s = sbuf_new (0); 542 if (rp_have_xrandr) 543 sbuf_printf(s, "%s ", screen->xrandr.name); 544 545 sbuf_printf_concat (s, "%d %d %d %d %d %d", 546 screen->number, 547 screen->left, 548 screen->top, 549 screen->width, 550 screen->height, 551 (rp_current_screen == screen)?1:0 /* is current? */ 552 ); 553 554 /* Extract the string and return it, and don't forget to free s. */ 555 tmp = sbuf_get (s); 556 free (s); 557 return tmp; 558} 559 560int 561screen_count (void) 562{ 563 return list_size (&rp_screens); 564} 565 566rp_screen * 567screen_next (void) 568{ 569 return list_next_entry (rp_current_screen, &rp_screens, node); 570} 571 572rp_screen * 573screen_prev (void) 574{ 575 return list_prev_entry (rp_current_screen, &rp_screens, node); 576} 577 578static void 579screen_remove_current (void) 580{ 581 rp_screen *new_screen; 582 rp_frame *new_frame; 583 rp_window *cur_win; 584 int cur_frame; 585 586 cur_win = current_window (); 587 new_screen = screen_next (); 588 589 cur_frame = new_screen->current_frame; 590 new_frame = screen_get_frame (new_screen, cur_frame); 591 592 set_active_frame (new_frame, 1); 593 594 hide_window (cur_win); 595} 596 597void 598screen_update (rp_screen *s, int left, int top, int width, int height) 599{ 600 rp_frame *f; 601 int oldwidth, oldheight; 602 603 PRINT_DEBUG (("screen_update (left=%d, top=%d, width=%d, height=%d)\n", 604 left, top, width, height)); 605 606 if (s->width == width && 607 s->height == height && 608 s->left == left && 609 s->top == top) 610 /* nothing to do */ 611 return; 612 613 oldwidth = s->width; 614 oldheight = s->height; 615 616 s->left = left; 617 s->top = top; 618 s->width = width; 619 s->height = height; 620 621 XMoveResizeWindow (dpy, s->help_window, s->left, s->top, s->width, s->height); 622 if (defaults.bar_sticky) 623 hide_bar (s); 624 625 list_for_each_entry (f, &s->frames, node) 626 { 627 f->x = (f->x*width)/oldwidth; 628 f->width = (f->width*width)/oldwidth; 629 f->y = (f->y*height)/oldheight; 630 f->height = (f->height*height)/oldheight; 631 maximize_all_windows_in_frame (f); 632 } 633} 634 635rp_screen * 636screen_add (int rr_output) 637{ 638 rp_screen *screen; 639 640 screen = xmalloc (sizeof(*screen)); 641 list_add (&screen->node, &rp_screens); 642 643 screen->number = numset_request (rp_glob_screen.numset); 644 645 xrandr_fill_screen (rr_output, screen); 646 init_screen (screen); 647 init_frame_list (screen); 648 649 if (screen_count () == 1) 650 { 651 rp_current_screen = screen; 652 change_windows_screen (NULL, rp_current_screen); 653 set_window_focus (rp_current_screen->key_window); 654 } 655 656 return screen; 657} 658 659void 660screen_del (rp_screen *s) 661{ 662 if (s == rp_current_screen) 663 { 664 if (screen_count () == 1) 665 { 666 hide_screen_windows (s); 667 rp_current_screen = NULL; 668 } 669 else 670 { 671 /* 672 * The deleted screen cannot be the current screen anymore, 673 * focus the next one. 674 */ 675 screen_remove_current (); 676 } 677 } 678 else 679 { 680 hide_screen_windows (s); 681 } 682 683 /* Affect window's screen backpointer to the new current screen */ 684 change_windows_screen (s, rp_current_screen); 685 686 numset_release (rp_glob_screen.numset, s->number); 687 688 screen_free (s); 689 690 list_del (&s->node); 691 free (s); 692} 693 694void 695screen_free (rp_screen *s) 696{ 697 rp_frame *frame; 698 struct list_head *iter, *tmp; 699 700 list_for_each_safe_entry (frame, iter, tmp, &s->frames, node) 701 { 702 frame_free (s, frame); 703 } 704 705 deactivate_screen(s); 706 707 XDestroyWindow (dpy, s->bar_window); 708 XDestroyWindow (dpy, s->key_window); 709 XDestroyWindow (dpy, s->input_window); 710 XDestroyWindow (dpy, s->frame_window); 711 XDestroyWindow (dpy, s->help_window); 712 713#ifdef USE_XFT_FONT 714 if (s->xft_font) 715 { 716 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num), 717 DefaultColormap (dpy, s->screen_num), &s->xft_fg_color); 718 XftColorFree (dpy, DefaultVisual (dpy, s->screen_num), 719 DefaultColormap (dpy, s->screen_num), &s->xft_bg_color); 720 XftFontClose (dpy, s->xft_font); 721 } 722#endif 723 724 XFreeCursor (dpy, s->rat); 725 XFreeColormap (dpy, s->def_cmap); 726 XFreeGC (dpy, s->normal_gc); 727 XFreeGC (dpy, s->inverse_gc); 728 729 free (s->display_string); 730 free (s->xrandr.name); 731} 732 733void 734screen_free_final (void) 735{ 736 /* Relinquish our hold on the root window. */ 737 XSelectInput(dpy, RootWindow (dpy, DefaultScreen (dpy)), 0); 738 739 numset_free (rp_glob_screen.numset); 740}