2 * client.c - client management
4 * Copyright © 2007 Julien Danjou <julien@danjou.info>
6 * This program 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 of the License, or
9 * (at your option) any later version.
11 * This program 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.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <X11/Xatom.h>
24 #include <X11/extensions/shape.h>
32 #include "statusbar.h"
34 #include "layouts/floating.h"
36 /** Get a Client by its window
37 * \param list Client list to look info
38 * \param w Client window to find
42 get_client_bywin(Client
*list
, Window w
)
46 for(c
= list
; c
&& c
->win
!= w
; c
= c
->next
);
50 /** Check if client supports protocol WM_DELETE_WINDOW
52 * \return True if client has WM_DELETE_WINDOW
55 isprotodel(Display
*disp
, Window win
)
61 if(XGetWMProtocols(disp
, win
, &protocols
, &n
))
63 for(i
= 0; !ret
&& i
< n
; i
++)
64 if(protocols
[i
] == XInternAtom(disp
, "WM_DELETE_WINDOW", False
))
71 /** Swap two client in the linked list clients
72 * \param c1 first client
73 * \param c2 second client
76 client_swap(Client
**head
, Client
*c1
, Client
*c2
)
82 c2
->next
= (tmp
== c2
? c1
: tmp
);
86 c1
->prev
= (tmp
== c1
? c2
: tmp
);
105 updatetitle(Client
*c
)
107 if(!xgettextprop(c
->display
, c
->win
, XInternAtom(c
->display
, "_NET_WM_NAME", False
), c
->name
, sizeof(c
->name
)))
108 xgettextprop(c
->display
, c
->win
, XInternAtom(c
->display
, "WM_NAME", False
), c
->name
, sizeof(c
->name
));
111 /** Ban client and unmapped it
112 * \param c the client
115 client_ban(Client
* c
)
117 XUnmapWindow(c
->display
, c
->win
);
118 window_setstate(c
->display
, c
->win
, IconicState
);
121 /** Attach client after another one
122 * \param client to attach to
123 * \param c the client
126 client_reattach_after(Client
*head
, Client
*c
)
132 head
->next
->prev
= c
;
135 c
->prev
->next
= c
->next
;
137 c
->next
= head
->next
;
142 /** Attach client to the beginning of the clients stack
143 * \param head client list
144 * \param c the client
147 client_attach(Client
**head
, Client
*c
)
155 /** Detach client from clients list
156 * \param head client list
157 * \param c client to detach
160 client_detach(Client
**head
, Client
*c
)
163 c
->prev
->next
= c
->next
;
165 c
->next
->prev
= c
->prev
;
168 c
->next
= c
->prev
= NULL
;
171 /** Give focus to client, or to first client if c is NULL
173 * \param selscreen True if current screen is selected
174 * \param awesomeconf awesome config
177 focus(Client
*c
, Bool selscreen
, awesome_config
*awesomeconf
)
180 Tag
*tag
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
);
182 /* if c is NULL or invisible, take next client in the stack */
183 if((!c
&& selscreen
) || (c
&& !isvisible(c
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
)))
184 for(c
= *awesomeconf
->clients
; c
&& !isvisible(c
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); c
= c
->next
);
186 /* XXX unfocus other tags clients, this is a bit too much */
187 for(i
= 0; i
< awesomeconf
->ntags
; i
++)
188 if(awesomeconf
->tags
[i
].client_sel
)
190 window_grabbuttons(awesomeconf
->tags
[i
].client_sel
->display
,
191 awesomeconf
->tags
[i
].client_sel
->phys_screen
,
192 awesomeconf
->tags
[i
].client_sel
->win
,
193 False
, True
, awesomeconf
->buttons
.root
,
194 awesomeconf
->buttons
.client
, awesomeconf
->numlockmask
);
195 XSetWindowBorder(awesomeconf
->tags
[i
].client_sel
->display
,
196 awesomeconf
->tags
[i
].client_sel
->win
,
197 awesomeconf
->colors_normal
[ColBorder
].pixel
);
198 window_settrans(awesomeconf
->tags
[i
].client_sel
->display
,
199 awesomeconf
->tags
[i
].client_sel
->win
, awesomeconf
->opacity_unfocused
);
203 XSetWindowBorder(awesomeconf
->display
, c
->win
, awesomeconf
->colors_selected
[ColBorder
].pixel
);
204 window_grabbuttons(c
->display
, c
->phys_screen
, c
->win
,
205 True
, True
, awesomeconf
->buttons
.root
,
206 awesomeconf
->buttons
.client
, awesomeconf
->numlockmask
);
211 drawstatusbar(awesomeconf
);
214 XSetInputFocus(tag
->client_sel
->display
, tag
->client_sel
->win
, RevertToPointerRoot
, CurrentTime
);
215 for(c
= *awesomeconf
->clients
; c
; c
= c
->next
)
216 if(c
!= tag
->client_sel
)
217 window_settrans(awesomeconf
->display
, tag
->client_sel
->win
, awesomeconf
->opacity_unfocused
);
218 window_settrans(awesomeconf
->display
, tag
->client_sel
->win
, -1);
221 XSetInputFocus(awesomeconf
->display
, RootWindow(awesomeconf
->display
, awesomeconf
->phys_screen
), RevertToPointerRoot
, CurrentTime
);
225 /** Load windows properties, restoring client's tag
226 * and floating state before awesome was restarted if any
227 * \todo this may bug if number of tags is != than before
228 * \param c Client ref
229 * \param ntags tags number
232 loadprops(Client
* c
, int ntags
)
238 prop
= p_new(char, ntags
+ 2);
240 if(xgettextprop(c
->display
, c
->win
, AWESOMEPROPS_ATOM(c
->display
), prop
, ntags
+ 2))
242 for(i
= 0; i
< ntags
&& prop
[i
]; i
++)
243 if((c
->tags
[i
] = prop
[i
] == '1'))
245 if(i
<= ntags
&& prop
[i
])
246 c
->isfloating
= prop
[i
] == '1';
254 /** Manage a new client
255 * \param w The window
256 * \param wa Window attributes
257 * \param awesomeconf awesome config
260 client_manage(Window w
, XWindowAttributes
*wa
, awesome_config
*awesomeconf
)
263 Client
*c
, *t
= NULL
;
267 ScreenInfo
*screen_info
;
268 awesome_config
*current_acf
= awesomeconf
;
270 c
= p_new(Client
, 1);
273 c
->x
= c
->rx
= wa
->x
;
274 c
->y
= c
->ry
= wa
->y
;
275 c
->w
= c
->rw
= wa
->width
;
276 c
->h
= c
->rh
= wa
->height
;
277 c
->oldborder
= wa
->border_width
;
279 c
->display
= awesomeconf
->display
;
280 c
->phys_screen
= awesomeconf
->phys_screen
;
281 tag_client_with_current_selected(c
, awesomeconf
);
283 /* update window title */
286 c
->screen
= get_screen_bycoord(c
->display
, c
->x
, c
->y
);
287 /* loadprops or apply rules if no props */
288 if(!loadprops(c
, awesomeconf
->ntags
))
291 Bool matched
= False
, has_rule
= False
;
292 for(r
= current_acf
->rules
; r
; r
= r
->next
)
293 if(client_match_rule(c
, r
))
297 c
->isfloating
= r
->isfloating
;
299 if(r
->screen
!= RULE_NOSCREEN
&& r
->screen
!= c
->screen
)
301 current_acf
= &awesomeconf
[r
->screen
- awesomeconf
->screen
];
302 tag_client_with_current_selected(c
, current_acf
);
303 move_client_to_screen(c
, current_acf
, True
);
306 for(i
= 0; i
< current_acf
->ntags
; i
++)
307 if(is_tag_match_rules(¤t_acf
->tags
[i
], r
))
316 tag_client_with_current_selected(c
, current_acf
);
321 tag_client_with_current_selected(c
, current_acf
);
322 move_client_to_screen(c
, current_acf
, True
);
326 screen_info
= get_screen_info(current_acf
->display
, current_acf
->screen
, NULL
);
327 /* if window request fullscreen mode */
328 if(c
->w
== screen_info
[current_acf
->screen
].width
&& c
->h
== screen_info
[current_acf
->screen
].height
)
330 c
->x
= screen_info
[current_acf
->screen
].x_org
;
331 c
->y
= screen_info
[current_acf
->screen
].y_org
;
333 c
->border
= wa
->border_width
;
337 ScreenInfo
*display_info
= get_display_info(c
->display
, c
->phys_screen
, ¤t_acf
->statusbar
);
339 if(c
->x
+ c
->w
+ 2 * c
->border
> display_info
->x_org
+ display_info
->width
)
340 c
->x
= c
->rx
= display_info
->x_org
+ display_info
->width
- c
->w
- 2 * c
->border
;
341 if(c
->y
+ c
->h
+ 2 * c
->border
> display_info
->y_org
+ display_info
->height
)
342 c
->y
= c
->ry
= display_info
->y_org
+ display_info
->height
- c
->h
- 2 * c
->border
;
343 if(c
->x
< display_info
->x_org
)
344 c
->x
= c
->rx
= display_info
->x_org
;
345 if(c
->y
< display_info
->y_org
)
346 c
->y
= c
->ry
= display_info
->y_org
;
348 c
->border
= current_acf
->borderpx
;
350 p_delete(&display_info
);
352 p_delete(&screen_info
);
355 wc
.border_width
= c
->border
;
356 XConfigureWindow(c
->display
, w
, CWBorderWidth
, &wc
);
357 XSetWindowBorder(c
->display
, w
, current_acf
->colors_normal
[ColBorder
].pixel
);
359 /* propagates border_width, if size doesn't change */
360 window_configure(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
, c
->border
);
362 /* update sizehint */
365 XSelectInput(c
->display
, w
, StructureNotifyMask
| PropertyChangeMask
| EnterWindowMask
);
368 if(current_acf
->have_shape
)
370 XShapeSelectInput(c
->display
, w
, ShapeNotifyMask
);
371 window_setshape(c
->display
, c
->phys_screen
, c
->win
);
375 window_grabbuttons(c
->display
, c
->phys_screen
, c
->win
,
376 False
, True
, current_acf
->buttons
.root
,
377 current_acf
->buttons
.client
, current_acf
->numlockmask
);
379 move_client_to_screen(c
, current_acf
, True
);
381 /* check for transient and set tags like its parent */
382 if((rettrans
= XGetTransientForHint(c
->display
, w
, &trans
) == Success
)
383 && (t
= get_client_bywin(*current_acf
->clients
, trans
)))
384 for(i
= 0; i
< current_acf
->ntags
; i
++)
385 c
->tags
[i
] = t
->tags
[i
];
387 /* should be floating if transsient or fixed) */
389 c
->isfloating
= (rettrans
== Success
) || c
->isfixed
;
392 saveprops(c
, current_acf
->ntags
);
394 /* attach to the stack */
395 client_attach(current_acf
->clients
, c
);
397 /* some windows require this */
398 XMoveResizeWindow(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
);
400 focus(c
, True
, current_acf
);
402 /* rearrange to display new window */
403 arrange(current_acf
);
407 client_resize(Client
*c
, int x
, int y
, int w
, int h
, awesome_config
*awesomeconf
,
408 Bool sizehints
, Bool volatile_coords
)
410 double dx
, dy
, max
, min
, ratio
;
416 if(c
->minay
> 0 && c
->maxay
> 0 && (h
- c
->baseh
) > 0 && (w
- c
->basew
) > 0)
418 dx
= (double) (w
- c
->basew
);
419 dy
= (double) (h
- c
->baseh
);
420 min
= (double) (c
->minax
) / (double) (c
->minay
);
421 max
= (double) (c
->maxax
) / (double) (c
->maxay
);
423 if(max
> 0 && min
> 0 && ratio
> 0)
427 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
429 w
= (int) dx
+ c
->basew
;
430 h
= (int) dy
+ c
->baseh
;
434 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
436 w
= (int) dx
+ c
->basew
;
437 h
= (int) dy
+ c
->baseh
;
441 if(c
->minw
&& w
< c
->minw
)
443 if(c
->minh
&& h
< c
->minh
)
445 if(c
->maxw
&& w
> c
->maxw
)
447 if(c
->maxh
&& h
> c
->maxh
)
450 w
-= (w
- c
->basew
) % c
->incw
;
452 h
-= (h
- c
->baseh
) % c
->inch
;
456 /* offscreen appearance fixes */
457 si
= get_display_info(c
->display
, c
->phys_screen
, NULL
);
459 x
= si
->width
- w
- 2 * c
->border
;
461 y
= si
->height
- h
- 2 * c
->border
;
463 if(x
+ w
+ 2 * c
->border
< 0)
465 if(y
+ h
+ 2 * c
->border
< 0)
467 if(c
->x
!= x
|| c
->y
!= y
|| c
->w
!= w
|| c
->h
!= h
)
472 c
->h
= wc
.height
= h
;
475 || get_current_layout(awesomeconf
->tags
, awesomeconf
->ntags
)->arrange
== layout_floating
))
482 wc
.border_width
= c
->border
;
483 XConfigureWindow(c
->display
, c
->win
, CWX
| CWY
| CWWidth
| CWHeight
| CWBorderWidth
, &wc
);
484 window_configure(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
, c
->border
);
485 XSync(c
->display
, False
);
486 if((c
->x
>= 0 || c
->y
>= 0) && XineramaIsActive(c
->display
))
488 int new_screen
= get_screen_bycoord(c
->display
, c
->x
, c
->y
);
489 if(c
->screen
!= new_screen
)
491 tag_client_with_current_selected(c
, &awesomeconf
[new_screen
- awesomeconf
->screen
]);
492 move_client_to_screen(c
, &awesomeconf
[new_screen
- awesomeconf
->screen
], False
);
499 saveprops(Client
* c
, int ntags
)
504 prop
= p_new(char, ntags
+ 2);
506 for(i
= 0; i
< ntags
; i
++)
507 prop
[i
] = c
->tags
[i
] ? '1' : '0';
510 prop
[i
] = c
->isfloating
? '1' : '0';
514 XChangeProperty(c
->display
, c
->win
, AWESOMEPROPS_ATOM(c
->display
), XA_STRING
, 8,
515 PropModeReplace
, (unsigned char *) prop
, i
);
521 client_unban(Client
*c
)
523 XMapWindow(c
->display
, c
->win
);
524 window_setstate(c
->display
, c
->win
, NormalState
);
528 client_unmanage(Client
*c
, long state
, awesome_config
*awesomeconf
)
533 wc
.border_width
= c
->oldborder
;
534 /* The server grab construct avoids race conditions. */
535 XGrabServer(c
->display
);
536 XConfigureWindow(c
->display
, c
->win
, CWBorderWidth
, &wc
); /* restore border */
537 client_detach(awesomeconf
->clients
, c
);
538 if(get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
== c
)
539 focus(NULL
, True
, awesomeconf
);
540 for(tag
= 0; tag
< awesomeconf
->ntags
; tag
++)
541 if(awesomeconf
->tags
[tag
].client_sel
== c
)
542 awesomeconf
->tags
[tag
].client_sel
= NULL
;
543 XUngrabButton(c
->display
, AnyButton
, AnyModifier
, c
->win
);
544 window_setstate(c
->display
, c
->win
, state
);
545 XSync(c
->display
, False
);
546 XSetErrorHandler(xerror
);
547 XUngrabServer(c
->display
);
548 if(state
!= NormalState
)
549 arrange(awesomeconf
);
555 updatesizehints(Client
*c
)
560 if(!XGetWMNormalHints(c
->display
, c
->win
, &size
, &msize
) || !size
.flags
)
562 c
->flags
= size
.flags
;
563 if(c
->flags
& PBaseSize
)
565 c
->basew
= size
.base_width
;
566 c
->baseh
= size
.base_height
;
568 else if(c
->flags
& PMinSize
)
570 c
->basew
= size
.min_width
;
571 c
->baseh
= size
.min_height
;
574 c
->basew
= c
->baseh
= 0;
575 if(c
->flags
& PResizeInc
)
577 c
->incw
= size
.width_inc
;
578 c
->inch
= size
.height_inc
;
581 c
->incw
= c
->inch
= 0;
583 if(c
->flags
& PMaxSize
)
585 c
->maxw
= size
.max_width
;
586 c
->maxh
= size
.max_height
;
589 c
->maxw
= c
->maxh
= 0;
591 if(c
->flags
& PMinSize
)
593 c
->minw
= size
.min_width
;
594 c
->minh
= size
.min_height
;
596 else if(c
->flags
& PBaseSize
)
598 c
->minw
= size
.base_width
;
599 c
->minh
= size
.base_height
;
602 c
->minw
= c
->minh
= 0;
604 if(c
->flags
& PAspect
)
606 c
->minax
= size
.min_aspect
.x
;
607 c
->maxax
= size
.max_aspect
.x
;
608 c
->minay
= size
.min_aspect
.y
;
609 c
->maxay
= size
.max_aspect
.y
;
612 c
->minax
= c
->maxax
= c
->minay
= c
->maxay
= 0;
614 c
->isfixed
= (c
->maxw
&& c
->minw
&& c
->maxh
&& c
->minh
615 && c
->maxw
== c
->minw
&& c
->maxh
== c
->minh
);
618 /** Set selected client transparency
619 * \param awesomeconf awesome config
620 * \param arg unused arg
621 * \ingroup ui_callback
624 uicb_client_settrans(awesome_config
*awesomeconf
,
627 double delta
= 100.0, current_opacity
= 100.0;
631 unsigned long n
, left
;
632 unsigned int current_opacity_raw
= 0;
634 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
639 XGetWindowProperty(awesomeconf
->display
, sel
->win
,
640 XInternAtom(sel
->display
, "_NET_WM_WINDOW_OPACITY", False
),
641 0L, 1L, False
, XA_CARDINAL
, &actual
, &format
, &n
, &left
,
642 (unsigned char **) &data
);
645 memcpy(¤t_opacity_raw
, data
, sizeof(unsigned int));
647 current_opacity
= (current_opacity_raw
* 100.0) / 0xffffffff;
652 delta
= compute_new_value_from_arg(arg
, current_opacity
);
656 else if(delta
> 100.0)
662 if(delta
== 100.0 && !set_prop
)
663 window_settrans(sel
->display
, sel
->win
, -1);
665 window_settrans(sel
->display
, sel
->win
, delta
);
670 * \param awesomeconf awesome config
671 * \param arg X, +X or -X
672 * \ingroup ui_callback
675 uicb_setborder(awesome_config
*awesomeconf
,
681 if((awesomeconf
->borderpx
= (int) compute_new_value_from_arg(arg
, (double) awesomeconf
->borderpx
)) < 0)
682 awesomeconf
->borderpx
= 0;
686 uicb_client_swapnext(awesome_config
*awesomeconf
,
687 const char *arg
__attribute__ ((unused
)))
689 Client
*next
, *sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
694 for(next
= sel
->next
; next
&& !isvisible(next
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); next
= next
->next
);
697 client_swap(awesomeconf
->clients
, sel
, next
);
698 arrange(awesomeconf
);
700 focus(sel
, True
, awesomeconf
);
705 uicb_client_swapprev(awesome_config
*awesomeconf
,
706 const char *arg
__attribute__ ((unused
)))
708 Client
*prev
, *sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
713 for(prev
= sel
->prev
; prev
&& !isvisible(prev
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); prev
= prev
->prev
);
716 client_swap(awesomeconf
->clients
, prev
, sel
);
717 arrange(awesomeconf
);
719 focus(sel
, True
, awesomeconf
);
724 uicb_client_moveresize(awesome_config
*awesomeconf
,
727 int nx
, ny
, nw
, nh
, ox
, oy
, ow
, oh
;
728 char x
[8], y
[8], w
[8], h
[8];
729 int mx
, my
, dx
, dy
, nmx
, nmy
;
732 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
734 if(get_current_layout(awesomeconf
->tags
, awesomeconf
->ntags
)->arrange
!= layout_floating
)
735 if(!sel
|| !sel
->isfloating
|| sel
->isfixed
|| !arg
)
737 if(sscanf(arg
, "%s %s %s %s", x
, y
, w
, h
) != 4)
739 nx
= (int) compute_new_value_from_arg(x
, sel
->x
);
740 ny
= (int) compute_new_value_from_arg(y
, sel
->y
);
741 nw
= (int) compute_new_value_from_arg(w
, sel
->w
);
742 nh
= (int) compute_new_value_from_arg(h
, sel
->h
);
749 Bool xqp
= XQueryPointer(awesomeconf
->display
, RootWindow(awesomeconf
->display
, awesomeconf
->phys_screen
), &dummy
, &dummy
, &mx
, &my
, &dx
, &dy
, &dui
);
750 client_resize(sel
, nx
, ny
, nw
, nh
, awesomeconf
, True
, False
);
751 if (xqp
&& ox
<= mx
&& (ox
+ ow
) >= mx
&& oy
<= my
&& (oy
+ oh
) >= my
)
753 nmx
= mx
- ox
+ sel
->w
- ow
- 1 < 0 ? 0 : mx
- ox
+ sel
->w
- ow
- 1;
754 nmy
= my
- oy
+ sel
->h
- oh
- 1 < 0 ? 0 : my
- oy
+ sel
->h
- oh
- 1;
755 XWarpPointer(awesomeconf
->display
, None
, sel
->win
, 0, 0, 0, 0, nmx
, nmy
);
759 /** Kill selected client
760 * \param awesomeconf awesome config
762 * \ingroup ui_callback
765 uicb_client_kill(awesome_config
*awesomeconf
,
766 const char *arg
__attribute__ ((unused
)))
769 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
773 if(isprotodel(sel
->display
, sel
->win
))
775 ev
.type
= ClientMessage
;
776 ev
.xclient
.window
= sel
->win
;
777 ev
.xclient
.message_type
= XInternAtom(awesomeconf
->display
, "WM_PROTOCOLS", False
);
778 ev
.xclient
.format
= 32;
779 ev
.xclient
.data
.l
[0] = XInternAtom(awesomeconf
->display
, "WM_DELETE_WINDOW", False
);
780 ev
.xclient
.data
.l
[1] = CurrentTime
;
781 XSendEvent(awesomeconf
->display
, sel
->win
, False
, NoEventMask
, &ev
);
784 XKillClient(awesomeconf
->display
, sel
->win
);
786 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99