jcs ratpoison hax
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}