jcs ratpoison hax
at jcs 622 lines 15 kB view raw
1/* functions for handling the window list 2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 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 <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25 26#include "ratpoison.h" 27 28LIST_HEAD(rp_unmapped_window); 29LIST_HEAD(rp_mapped_window); 30 31struct numset *rp_window_numset; 32 33static void set_active_window_body (rp_window *win, int force); 34 35/* Get the mouse position relative to the the specified window */ 36static void 37get_mouse_position (rp_window *win, int *mouse_x, int *mouse_y) 38{ 39 Window root_win, child_win; 40 int root_x, root_y; 41 unsigned int mask; 42 43 XQueryPointer (dpy, win->scr->root, &root_win, &child_win, mouse_x, mouse_y, &root_x, &root_y, &mask); 44} 45 46void 47free_window (rp_window *w) 48{ 49 if (w == NULL) return; 50 51 free (w->user_name); 52 free (w->res_name); 53 free (w->res_class); 54 free (w->wm_name); 55 56 XFree (w->hints); 57 58 free (w); 59} 60 61void 62update_window_gravity (rp_window *win) 63{ 64/* if (win->hints->win_gravity == ForgetGravity) */ 65/* { */ 66 if (win->transient) 67 win->gravity = defaults.trans_gravity; 68 else if (win->hints->flags & PMaxSize || win->hints->flags & PAspect) 69 win->gravity = defaults.maxsize_gravity; 70 else 71 win->gravity = defaults.win_gravity; 72/* } */ 73/* else */ 74/* { */ 75/* win->gravity = win->hints->win_gravity; */ 76/* } */ 77} 78 79char * 80window_name (rp_window *win) 81{ 82 if (win == NULL) return NULL; 83 84 if (win->named) 85 return win->user_name; 86 87 switch (defaults.win_name) 88 { 89 case WIN_NAME_RES_NAME: 90 if (win->res_name) 91 return win->res_name; 92 else return win->user_name; 93 94 case WIN_NAME_RES_CLASS: 95 if (win->res_class) 96 return win->res_class; 97 else return win->user_name; 98 99 /* if we're not looking for the res name or res class, then 100 we're looking for the window title. */ 101 default: 102 if (win->wm_name) 103 return win->wm_name; 104 else return win->user_name; 105 } 106 107 return NULL; 108} 109 110/* FIXME: we need to verify that the window is running on the same 111 host as something. otherwise there could be overlapping PIDs. */ 112struct rp_child_info * 113get_child_info (Window w) 114{ 115 rp_child_info *cur; 116 int status; 117 int pid; 118 Atom type_ret; 119 int format_ret; 120 unsigned long nitems; 121 unsigned long bytes_after; 122 unsigned char *req; 123 124 status = XGetWindowProperty (dpy, w, _net_wm_pid, 125 0, 0, False, XA_CARDINAL, 126 &type_ret, &format_ret, &nitems, &bytes_after, &req); 127 128 if (status != Success || req == NULL) 129 { 130 PRINT_DEBUG (("Couldn't get _NET_WM_PID Property\n")); 131 return NULL; 132 } 133 134 /* XGetWindowProperty always allocates one extra byte even if 135 the property is zero length. */ 136 XFree (req); 137 138 status = XGetWindowProperty (dpy, w, _net_wm_pid, 139 0, (bytes_after / 4) + (bytes_after % 4 ? 1 : 0), 140 False, XA_CARDINAL, &type_ret, &format_ret, &nitems, 141 &bytes_after, &req); 142 143 if (status != Success || req == NULL) 144 { 145 PRINT_DEBUG (("Couldn't get _NET_WM_PID Property\n")); 146 return NULL; 147 } 148 149 pid = *((int *)req); 150 XFree(req); 151 152 PRINT_DEBUG(("pid: %d\n", pid)); 153 154 list_for_each_entry (cur, &rp_children, node) 155 if (pid == cur->pid) 156 return cur; 157 158 return NULL; 159} 160 161/* Allocate a new window and add it to the list of managed windows */ 162rp_window * 163add_to_window_list (rp_screen *s, Window w) 164{ 165 struct rp_child_info *child_info; 166 rp_window *new_window; 167 rp_group *group = NULL; 168 int frame_num = -1; 169 170 new_window = xmalloc (sizeof (rp_window)); 171 172 new_window->w = w; 173 new_window->scr = s; 174 new_window->last_access = 0; 175 new_window->state = WithdrawnState; 176 new_window->number = -1; 177 new_window->frame_number = EMPTY; 178 new_window->intended_frame_number = -1; 179 new_window->named = 0; 180 new_window->hints = XAllocSizeHints (); 181 new_window->colormap = DefaultColormap (dpy, s->screen_num); 182 new_window->transient = XGetTransientForHint (dpy, new_window->w, &new_window->transient_for); 183 new_window->sticky_frame = EMPTY; 184 PRINT_DEBUG (("transient %d\n", new_window->transient)); 185 186 update_window_gravity (new_window); 187 188 get_mouse_position (new_window, &new_window->mouse_x, &new_window->mouse_y); 189 190 XSelectInput (dpy, new_window->w, WIN_EVENTS); 191 192 new_window->user_name = xstrdup ("Unnamed"); 193 194 new_window->wm_name = NULL; 195 new_window->res_name = NULL; 196 new_window->res_class = NULL; 197 198 /* Add the window to the end of the unmapped list. */ 199 list_add_tail (&new_window->node, &rp_unmapped_window); 200 201 child_info = get_child_info (w); 202 203 if (child_info && !child_info->window_mapped) { 204 rp_frame *frame = screen_find_frame_by_frame (child_info->screen, child_info->frame); 205 206 PRINT_DEBUG(("frame=%p\n", frame)); 207 group = groups_find_group_by_group (child_info->group); 208 if (frame) 209 frame_num = frame->number; 210 /* Only map the first window in the launch frame. */ 211 child_info->window_mapped = 1; 212 } 213 214 /* Add the window to the group it's pid was launched in or the 215 current one. */ 216 if (group) 217 group_add_window (group, new_window); 218 else 219 group_add_window (rp_current_group, new_window); 220 221 PRINT_DEBUG(("frame_num: %d\n", frame_num)); 222 if (frame_num >= 0) 223 new_window->intended_frame_number = frame_num; 224 225 return new_window; 226} 227 228/* Check to see if the window is in the list of windows. */ 229rp_window * 230find_window_in_list (Window w, struct list_head *list) 231{ 232 rp_window *cur; 233 234 list_for_each_entry (cur, list, node) 235 { 236 if (cur->w == w) return cur; 237 } 238 239 return NULL; 240} 241 242/* Check to see if the window is in any of the lists of windows. */ 243rp_window * 244find_window (Window w) 245{ 246 rp_window *win = NULL; 247 248 249 win = find_window_in_list (w, &rp_mapped_window); 250 251 if (!win) 252 { 253 win = find_window_in_list (w, &rp_unmapped_window); 254 if (win) 255 PRINT_DEBUG (("Window found in unmapped window list\n")); 256 else 257 PRINT_DEBUG (("Window not found.\n")); 258 } 259 else 260 { 261 PRINT_DEBUG (("Window found in mapped window list.\n")); 262 } 263 264 return win; 265} 266 267rp_window * 268find_window_number (int n) 269{ 270 rp_window *cur; 271 272 list_for_each_entry (cur,&rp_mapped_window,node) 273 { 274/* if (cur->state == STATE_UNMAPPED) continue; */ 275 276 if (n == cur->number) return cur; 277 } 278 279 return NULL; 280} 281 282rp_window * 283find_window_name (char *name, int exact_match) 284{ 285 rp_window_elem *cur; 286 287 if (!exact_match) 288 { 289 list_for_each_entry (cur, &rp_current_group->mapped_windows, node) 290 { 291 if (str_comp (name, window_name (cur->win), strlen (name))) 292 return cur->win; 293 } 294 } 295 else 296 { 297 list_for_each_entry (cur, &rp_current_group->mapped_windows, node) 298 { 299 if (!strcmp (name, window_name (cur->win))) 300 return cur->win; 301 } 302 } 303 304 /* didn't find it */ 305 return NULL; 306} 307 308rp_window * 309find_window_other (rp_screen *screen) 310{ 311 return group_last_window (rp_current_group, screen); 312} 313 314/* Assumes the list is sorted by increasing number. Inserts win into 315 to Right place to keep the list sorted. */ 316void 317insert_into_list (rp_window *win, struct list_head *list) 318{ 319 rp_window *cur; 320 321 list_for_each_entry (cur, list, node) 322 { 323 if (cur->number > win->number) 324 { 325 list_add_tail (&win->node, &cur->node); 326 return; 327 } 328 } 329 330 list_add_tail(&win->node, list); 331} 332 333static void 334save_mouse_position (rp_window *win) 335{ 336 Window root_win, child_win; 337 int root_x, root_y; 338 unsigned int mask; 339 340 /* In the case the XQueryPointer raises a BadWindow error, the 341 window is not mapped or has been destroyed so it doesn't matter 342 what we store in mouse_x and mouse_y since they will never be 343 used again. */ 344 345 ignore_badwindow++; 346 347 XQueryPointer (dpy, win->w, &root_win, &child_win, 348 &root_x, &root_y, &win->mouse_x, &win->mouse_y, &mask); 349 350 ignore_badwindow--; 351} 352 353/* Takes focus away from last_win and gives focus to win */ 354void 355give_window_focus (rp_window *win, rp_window *last_win) 356{ 357 /* counter increments every time this function is called. This way 358 we can track which window was last accessed. */ 359 static int counter = 1; 360 361 /* Warp the cursor to the window's saved position if last_win and 362 win are different windows. */ 363 if (last_win != NULL && win != last_win) 364 { 365 save_mouse_position (last_win); 366 XSetWindowBorder (dpy, last_win->w, rp_glob_screen.bw_color); 367 } 368 369 if (win == NULL) return; 370 371 counter++; 372 win->last_access = counter; 373 unhide_window (win); 374 375 if (defaults.warp) 376 { 377 PRINT_DEBUG (("Warp pointer\n")); 378 XWarpPointer (dpy, None, win->w, 379 0, 0, 0, 0, win->mouse_x, win->mouse_y); 380 } 381 382 /* Swap colormaps */ 383 if (last_win != NULL) XUninstallColormap (dpy, last_win->colormap); 384 XInstallColormap (dpy, win->colormap); 385 386 XSetWindowBorder (dpy, win->w, rp_glob_screen.fw_color); 387 388 /* Finally, give the window focus */ 389 rp_current_screen = win->scr; 390 set_rp_window_focus (win); 391 392 XSync (dpy, False); 393} 394 395/* In the current frame, set the active window to win. win will have focus. */ 396void set_active_window (rp_window *win) 397{ 398 set_active_window_body(win, 0); 399} 400 401void set_active_window_force (rp_window *win) 402{ 403 set_active_window_body(win, 1); 404} 405 406static rp_frame * 407find_frame_non_dedicated(rp_screen *current_screen) 408{ 409 rp_frame *cur; 410 rp_screen *screen; 411 412 list_for_each_entry (screen, &rp_screens, node) 413 { 414 if (current_screen == screen) 415 continue; 416 417 list_for_each_entry (cur, &screen->frames, node) 418 { 419 if (!cur->dedicated) 420 return cur; 421 } 422 } 423 424 return NULL; 425} 426 427static void 428set_active_window_body (rp_window *win, int force) 429{ 430 rp_window *last_win; 431 rp_frame *frame = NULL, *last_frame = NULL; 432 433 if (win == NULL) 434 return; 435 436 PRINT_DEBUG (("intended_frame_number: %d\n", win->intended_frame_number)); 437 438 /* use the intended frame if we can. */ 439 if (win->intended_frame_number >= 0) 440 { 441 frame = screen_get_frame (rp_current_screen, win->intended_frame_number); 442 win->intended_frame_number = -1; 443 if (frame != current_frame ()) 444 last_frame = current_frame (); 445 } 446 447 if (frame == NULL) 448 frame = screen_get_frame (rp_current_screen, rp_current_screen->current_frame); 449 450 if (frame->dedicated && !force) 451 { 452 /* Try to find a non-dedicated frame. */ 453 rp_frame *non_dedicated; 454 455 non_dedicated = find_frame_non_dedicated (rp_current_screen); 456 if (non_dedicated != NULL) 457 { 458 last_frame = frame; 459 frame = non_dedicated; 460 set_active_frame (frame, 0); 461 } 462 } 463 464 last_win = set_frames_window (frame, win); 465 466 if (last_win != NULL) 467 PRINT_DEBUG (("last window: %s\n", window_name (last_win))); 468 PRINT_DEBUG (("new window: %s\n", window_name (win))); 469 470 /* Make sure the window comes up full screen */ 471 maximize (win); 472 473 /* Focus the window. */ 474 give_window_focus (win, last_win); 475 476 /* The other windows in the frame will be hidden if this window 477 doesn't qualify as a transient window (ie dialog box. */ 478 if (!window_is_transient (win)) 479 hide_others(win); 480 481 /* Make sure the program bar is always on the top */ 482 update_window_names (win->scr, defaults.window_fmt); 483 484 XSync (dpy, False); 485 486 /* If we switched frame, go back to the old one. */ 487 if (last_frame != NULL) 488 set_active_frame (last_frame, 0); 489 490 /* Call the switch window hook */ 491 hook_run (&rp_switch_win_hook); 492} 493 494/* Go to the window, switching frames if the window is already in a 495 frame. */ 496void 497goto_window (rp_window *win) 498{ 499 rp_frame *frame; 500 501 /* There is nothing to do if it is already the current window. */ 502 if (current_window() == win) 503 return; 504 505 frame = find_windows_frame (win); 506 if (frame) 507 { 508 set_active_frame (frame, 0); 509 } 510 else 511 { 512 set_active_window (win); 513 } 514} 515 516/* get the window list and store it in buffer delimiting each window 517 with delim. mark_start and mark_end will be filled with the text 518 positions for the start and end of the current window. */ 519void 520get_window_list (char *fmt, char *delim, struct sbuf *buffer, 521 int *mark_start, int *mark_end) 522{ 523 rp_window_elem *we; 524 525 if (buffer == NULL) return; 526 527 sbuf_clear (buffer); 528 find_window_other (rp_current_screen); 529 530 /* We only loop through the current group to look for windows. */ 531 list_for_each_entry (we,&rp_current_group->mapped_windows,node) 532 { 533 PRINT_DEBUG (("%d-%s\n", we->number, window_name (we->win))); 534 535 if (we->win != current_window()) 536 { 537 if (defaults.bar_sticky) 538 /* when sticky bar is on, we only want to show the current window */ 539 continue; 540 else 541 *mark_start = strlen (sbuf_get (buffer)); 542 } 543 544 /* A hack, pad the window with a space at the beginning and end 545 if there is no delimiter. */ 546 if (!delim && !defaults.bar_sticky) 547 sbuf_concat (buffer, " "); 548 549 format_string (fmt, we, buffer); 550 551 /* A hack, pad the window with a space at the beginning and end 552 if there is no delimiter. */ 553 if (!delim && !defaults.bar_sticky) 554 sbuf_concat (buffer, " "); 555 556 /* Only put the delimiter between the windows, and not after the the last 557 window. */ 558 if (delim && we->node.next != &rp_current_group->mapped_windows) 559 sbuf_concat (buffer, delim); 560 561 if (we->win == current_window()) 562 { 563 *mark_end = strlen (sbuf_get (buffer)); 564 } 565 } 566 567 if (!strcmp (sbuf_get (buffer), "")) 568 { 569 sbuf_copy (buffer, MESSAGE_NO_MANAGED_WINDOWS); 570 } 571} 572 573void 574init_window_stuff (void) 575{ 576 rp_window_numset = numset_new (); 577} 578 579void 580free_window_stuff (void) 581{ 582 rp_window *cur; 583 struct list_head *tmp, *iter; 584 585 list_for_each_safe_entry (cur, iter, tmp, &rp_unmapped_window, node) 586 { 587 list_del (&cur->node); 588 groups_del_window (cur); 589 free_window (cur); 590 } 591 592 list_for_each_safe_entry (cur, iter, tmp, &rp_mapped_window, node) 593 { 594 list_del (&cur->node); 595 groups_unmap_window (cur); 596 groups_del_window (cur); 597 free_window (cur); 598 } 599 600 numset_free (rp_window_numset); 601} 602 603rp_frame * 604win_get_frame (rp_window *win) 605{ 606 if (win->frame_number != EMPTY) 607 return screen_get_frame (win->scr, win->frame_number); 608 else 609 return NULL; 610} 611 612void 613change_windows_screen (rp_screen *old_screen, rp_screen *new_screen) 614{ 615 rp_window *win; 616 617 list_for_each_entry (win, &rp_mapped_window, node) 618 { 619 if (win->scr == old_screen) 620 win->scr = new_screen; 621 } 622}