jcs ratpoison hax
at master 615 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 PRINT_DEBUG (("transient %d\n", new_window->transient)); 184 185 update_window_gravity (new_window); 186 187 get_mouse_position (new_window, &new_window->mouse_x, &new_window->mouse_y); 188 189 XSelectInput (dpy, new_window->w, WIN_EVENTS); 190 191 new_window->user_name = xstrdup ("Unnamed"); 192 193 new_window->wm_name = NULL; 194 new_window->res_name = NULL; 195 new_window->res_class = NULL; 196 197 /* Add the window to the end of the unmapped list. */ 198 list_add_tail (&new_window->node, &rp_unmapped_window); 199 200 child_info = get_child_info (w); 201 202 if (child_info && !child_info->window_mapped) { 203 rp_frame *frame = screen_find_frame_by_frame (child_info->screen, child_info->frame); 204 205 PRINT_DEBUG(("frame=%p\n", frame)); 206 group = groups_find_group_by_group (child_info->group); 207 if (frame) 208 frame_num = frame->number; 209 /* Only map the first window in the launch frame. */ 210 child_info->window_mapped = 1; 211 } 212 213 /* Add the window to the group it's pid was launched in or the 214 current one. */ 215 if (group) 216 group_add_window (group, new_window); 217 else 218 group_add_window (rp_current_group, new_window); 219 220 PRINT_DEBUG(("frame_num: %d\n", frame_num)); 221 if (frame_num >= 0) 222 new_window->intended_frame_number = frame_num; 223 224 return new_window; 225} 226 227/* Check to see if the window is in the list of windows. */ 228rp_window * 229find_window_in_list (Window w, struct list_head *list) 230{ 231 rp_window *cur; 232 233 list_for_each_entry (cur, list, node) 234 { 235 if (cur->w == w) return cur; 236 } 237 238 return NULL; 239} 240 241/* Check to see if the window is in any of the lists of windows. */ 242rp_window * 243find_window (Window w) 244{ 245 rp_window *win = NULL; 246 247 248 win = find_window_in_list (w, &rp_mapped_window); 249 250 if (!win) 251 { 252 win = find_window_in_list (w, &rp_unmapped_window); 253 if (win) 254 PRINT_DEBUG (("Window found in unmapped window list\n")); 255 else 256 PRINT_DEBUG (("Window not found.\n")); 257 } 258 else 259 { 260 PRINT_DEBUG (("Window found in mapped window list.\n")); 261 } 262 263 return win; 264} 265 266rp_window * 267find_window_number (int n) 268{ 269 rp_window *cur; 270 271 list_for_each_entry (cur,&rp_mapped_window,node) 272 { 273/* if (cur->state == STATE_UNMAPPED) continue; */ 274 275 if (n == cur->number) return cur; 276 } 277 278 return NULL; 279} 280 281rp_window * 282find_window_name (char *name, int exact_match) 283{ 284 rp_window_elem *cur; 285 286 if (!exact_match) 287 { 288 list_for_each_entry (cur, &rp_current_group->mapped_windows, node) 289 { 290 if (str_comp (name, window_name (cur->win), strlen (name))) 291 return cur->win; 292 } 293 } 294 else 295 { 296 list_for_each_entry (cur, &rp_current_group->mapped_windows, node) 297 { 298 if (!strcmp (name, window_name (cur->win))) 299 return cur->win; 300 } 301 } 302 303 /* didn't find it */ 304 return NULL; 305} 306 307rp_window * 308find_window_other (rp_screen *screen) 309{ 310 return group_last_window (rp_current_group, screen); 311} 312 313/* Assumes the list is sorted by increasing number. Inserts win into 314 to Right place to keep the list sorted. */ 315void 316insert_into_list (rp_window *win, struct list_head *list) 317{ 318 rp_window *cur; 319 320 list_for_each_entry (cur, list, node) 321 { 322 if (cur->number > win->number) 323 { 324 list_add_tail (&win->node, &cur->node); 325 return; 326 } 327 } 328 329 list_add_tail(&win->node, list); 330} 331 332static void 333save_mouse_position (rp_window *win) 334{ 335 Window root_win, child_win; 336 int root_x, root_y; 337 unsigned int mask; 338 339 /* In the case the XQueryPointer raises a BadWindow error, the 340 window is not mapped or has been destroyed so it doesn't matter 341 what we store in mouse_x and mouse_y since they will never be 342 used again. */ 343 344 ignore_badwindow++; 345 346 XQueryPointer (dpy, win->w, &root_win, &child_win, 347 &root_x, &root_y, &win->mouse_x, &win->mouse_y, &mask); 348 349 ignore_badwindow--; 350} 351 352/* Takes focus away from last_win and gives focus to win */ 353void 354give_window_focus (rp_window *win, rp_window *last_win) 355{ 356 /* counter increments every time this function is called. This way 357 we can track which window was last accessed. */ 358 static int counter = 1; 359 360 /* Warp the cursor to the window's saved position if last_win and 361 win are different windows. */ 362 if (last_win != NULL && win != last_win) 363 { 364 save_mouse_position (last_win); 365 XSetWindowBorder (dpy, last_win->w, rp_glob_screen.bw_color); 366 } 367 368 if (win == NULL) return; 369 370 counter++; 371 win->last_access = counter; 372 unhide_window (win); 373 374 if (defaults.warp) 375 { 376 PRINT_DEBUG (("Warp pointer\n")); 377 XWarpPointer (dpy, None, win->w, 378 0, 0, 0, 0, win->mouse_x, win->mouse_y); 379 } 380 381 /* Swap colormaps */ 382 if (last_win != NULL) XUninstallColormap (dpy, last_win->colormap); 383 XInstallColormap (dpy, win->colormap); 384 385 XSetWindowBorder (dpy, win->w, rp_glob_screen.fw_color); 386 387 /* Finally, give the window focus */ 388 rp_current_screen = win->scr; 389 set_rp_window_focus (win); 390 391 XSync (dpy, False); 392} 393 394/* In the current frame, set the active window to win. win will have focus. */ 395void set_active_window (rp_window *win) 396{ 397 set_active_window_body(win, 0); 398} 399 400void set_active_window_force (rp_window *win) 401{ 402 set_active_window_body(win, 1); 403} 404 405static rp_frame * 406find_frame_non_dedicated(rp_screen *current_screen) 407{ 408 rp_frame *cur; 409 rp_screen *screen; 410 411 list_for_each_entry (screen, &rp_screens, node) 412 { 413 if (current_screen == screen) 414 continue; 415 416 list_for_each_entry (cur, &screen->frames, node) 417 { 418 if (!cur->dedicated) 419 return cur; 420 } 421 } 422 423 return NULL; 424} 425 426static void 427set_active_window_body (rp_window *win, int force) 428{ 429 rp_window *last_win; 430 rp_frame *frame = NULL, *last_frame = NULL; 431 432 if (win == NULL) 433 return; 434 435 PRINT_DEBUG (("intended_frame_number: %d\n", win->intended_frame_number)); 436 437 /* use the intended frame if we can. */ 438 if (win->intended_frame_number >= 0) 439 { 440 frame = screen_get_frame (rp_current_screen, win->intended_frame_number); 441 win->intended_frame_number = -1; 442 if (frame != current_frame ()) 443 last_frame = current_frame (); 444 } 445 446 if (frame == NULL) 447 frame = screen_get_frame (rp_current_screen, rp_current_screen->current_frame); 448 449 if (frame->dedicated && !force) 450 { 451 /* Try to find a non-dedicated frame. */ 452 rp_frame *non_dedicated; 453 454 non_dedicated = find_frame_non_dedicated (rp_current_screen); 455 if (non_dedicated != NULL) 456 { 457 last_frame = frame; 458 frame = non_dedicated; 459 set_active_frame (frame, 0); 460 } 461 } 462 463 last_win = set_frames_window (frame, win); 464 465 if (last_win != NULL) 466 PRINT_DEBUG (("last window: %s\n", window_name (last_win))); 467 PRINT_DEBUG (("new window: %s\n", window_name (win))); 468 469 /* Make sure the window comes up full screen */ 470 maximize (win); 471 472 /* Focus the window. */ 473 give_window_focus (win, last_win); 474 475 /* The other windows in the frame will be hidden if this window 476 doesn't qualify as a transient window (ie dialog box. */ 477 if (!window_is_transient (win)) 478 hide_others(win); 479 480 /* Make sure the program bar is always on the top */ 481 update_window_names (win->scr, defaults.window_fmt); 482 483 XSync (dpy, False); 484 485 /* If we switched frame, go back to the old one. */ 486 if (last_frame != NULL) 487 set_active_frame (last_frame, 0); 488 489 /* Call the switch window hook */ 490 hook_run (&rp_switch_win_hook); 491} 492 493/* Go to the window, switching frames if the window is already in a 494 frame. */ 495void 496goto_window (rp_window *win) 497{ 498 rp_frame *frame; 499 500 /* There is nothing to do if it is already the current window. */ 501 if (current_window() == win) 502 return; 503 504 frame = find_windows_frame (win); 505 if (frame) 506 { 507 set_active_frame (frame, 0); 508 } 509 else 510 { 511 set_active_window (win); 512 } 513} 514 515/* get the window list and store it in buffer delimiting each window 516 with delim. mark_start and mark_end will be filled with the text 517 positions for the start and end of the current window. */ 518void 519get_window_list (char *fmt, char *delim, struct sbuf *buffer, 520 int *mark_start, int *mark_end) 521{ 522 rp_window_elem *we; 523 524 if (buffer == NULL) return; 525 526 sbuf_clear (buffer); 527 find_window_other (rp_current_screen); 528 529 /* We only loop through the current group to look for windows. */ 530 list_for_each_entry (we,&rp_current_group->mapped_windows,node) 531 { 532 PRINT_DEBUG (("%d-%s\n", we->number, window_name (we->win))); 533 534 if (we->win == current_window()) 535 *mark_start = strlen (sbuf_get (buffer)); 536 537 /* A hack, pad the window with a space at the beginning and end 538 if there is no delimiter. */ 539 if (!delim) 540 sbuf_concat (buffer, " "); 541 542 format_string (fmt, we, buffer); 543 544 /* A hack, pad the window with a space at the beginning and end 545 if there is no delimiter. */ 546 if (!delim) 547 sbuf_concat (buffer, " "); 548 549 /* Only put the delimiter between the windows, and not after the the last 550 window. */ 551 if (delim && we->node.next != &rp_current_group->mapped_windows) 552 sbuf_concat (buffer, delim); 553 554 if (we->win == current_window()) 555 { 556 *mark_end = strlen (sbuf_get (buffer)); 557 } 558 } 559 560 if (!strcmp (sbuf_get (buffer), "")) 561 { 562 sbuf_copy (buffer, MESSAGE_NO_MANAGED_WINDOWS); 563 } 564} 565 566void 567init_window_stuff (void) 568{ 569 rp_window_numset = numset_new (); 570} 571 572void 573free_window_stuff (void) 574{ 575 rp_window *cur; 576 struct list_head *tmp, *iter; 577 578 list_for_each_safe_entry (cur, iter, tmp, &rp_unmapped_window, node) 579 { 580 list_del (&cur->node); 581 groups_del_window (cur); 582 free_window (cur); 583 } 584 585 list_for_each_safe_entry (cur, iter, tmp, &rp_mapped_window, node) 586 { 587 list_del (&cur->node); 588 groups_unmap_window (cur); 589 groups_del_window (cur); 590 free_window (cur); 591 } 592 593 numset_free (rp_window_numset); 594} 595 596rp_frame * 597win_get_frame (rp_window *win) 598{ 599 if (win->frame_number != EMPTY) 600 return screen_get_frame (win->scr, win->frame_number); 601 else 602 return NULL; 603} 604 605void 606change_windows_screen (rp_screen *old_screen, rp_screen *new_screen) 607{ 608 rp_window *win; 609 610 list_for_each_entry (win, &rp_mapped_window, node) 611 { 612 if (win->scr == old_screen) 613 win->scr = new_screen; 614 } 615}