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 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}