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>
31 #include "statusbar.h"
33 #include "layouts/floating.h"
35 /** Get a Client by its window
36 * \param list Client list to look info
37 * \param w Client window to find
41 get_client_bywin(Client
*list
, Window w
)
45 for(c
= list
; c
&& c
->win
!= w
; c
= c
->next
);
49 /** Check if client supports protocol WM_DELETE_WINDOW
51 * \return True if client has WM_DELETE_WINDOW
54 isprotodel(Display
*disp
, Window win
)
60 if(XGetWMProtocols(disp
, win
, &protocols
, &n
))
62 for(i
= 0; !ret
&& i
< n
; i
++)
63 if(protocols
[i
] == XInternAtom(disp
, "WM_DELETE_WINDOW", False
))
70 /** Swap two client in the linked list clients
71 * \param c1 first client
72 * \param c2 second client
75 client_swap(Client
**head
, Client
*c1
, Client
*c2
)
81 c2
->next
= (tmp
== c2
? c1
: tmp
);
85 c1
->prev
= (tmp
== c1
? c2
: tmp
);
104 updatetitle(Client
*c
)
106 if(!xgettextprop(c
->display
, c
->win
, XInternAtom(c
->display
, "_NET_WM_NAME", False
), c
->name
, sizeof(c
->name
)))
107 xgettextprop(c
->display
, c
->win
, XInternAtom(c
->display
, "WM_NAME", False
), c
->name
, sizeof(c
->name
));
110 /** Ban client and unmapped it
111 * \param c the client
114 client_ban(Client
* c
)
116 XUnmapWindow(c
->display
, c
->win
);
117 window_setstate(c
->display
, c
->win
, IconicState
);
120 /** Attach client after another one
121 * \param client to attach to
122 * \param c the client
125 client_reattach_after(Client
*head
, Client
*c
)
131 head
->next
->prev
= c
;
134 c
->prev
->next
= c
->next
;
136 c
->next
= head
->next
;
141 /** Attach client to the beginning of the clients stack
142 * \param head client list
143 * \param c the client
146 client_attach(Client
**head
, Client
*c
)
154 /** Detach client from clients list
155 * \param head client list
156 * \param c client to detach
159 client_detach(Client
**head
, Client
*c
)
162 c
->prev
->next
= c
->next
;
164 c
->next
->prev
= c
->prev
;
167 c
->next
= c
->prev
= NULL
;
170 /** Give focus to client, or to first client if c is NULL
172 * \param selscreen True if current screen is selected
173 * \param awesomeconf awesome config
176 focus(Client
*c
, Bool selscreen
, awesome_config
*awesomeconf
)
179 Tag
*tag
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
);
181 /* if c is NULL or invisible, take next client in the stack */
182 if((!c
&& selscreen
) || (c
&& !isvisible(c
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
)))
183 for(c
= *awesomeconf
->clients
; c
&& !isvisible(c
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); c
= c
->next
);
185 /* XXX unfocus other tags clients, this is a bit too much */
186 for(i
= 0; i
< awesomeconf
->ntags
; i
++)
187 if(awesomeconf
->tags
[i
].client_sel
)
189 window_grabbuttons(awesomeconf
->tags
[i
].client_sel
->display
,
190 awesomeconf
->tags
[i
].client_sel
->phys_screen
,
191 awesomeconf
->tags
[i
].client_sel
->win
,
192 False
, True
, awesomeconf
->modkey
, awesomeconf
->numlockmask
);
193 XSetWindowBorder(awesomeconf
->tags
[i
].client_sel
->display
,
194 awesomeconf
->tags
[i
].client_sel
->win
,
195 awesomeconf
->colors_normal
[ColBorder
].pixel
);
196 window_settrans(awesomeconf
->tags
[i
].client_sel
->display
,
197 awesomeconf
->tags
[i
].client_sel
->win
, awesomeconf
->opacity_unfocused
);
201 XSetWindowBorder(awesomeconf
->display
, c
->win
, awesomeconf
->colors_selected
[ColBorder
].pixel
);
202 window_grabbuttons(c
->display
, c
->phys_screen
, c
->win
,
203 True
, True
, awesomeconf
->modkey
, awesomeconf
->numlockmask
);
208 drawstatusbar(awesomeconf
);
211 XSetInputFocus(tag
->client_sel
->display
, tag
->client_sel
->win
, RevertToPointerRoot
, CurrentTime
);
212 for(c
= *awesomeconf
->clients
; c
; c
= c
->next
)
213 if(c
!= tag
->client_sel
)
214 window_settrans(awesomeconf
->display
, tag
->client_sel
->win
, awesomeconf
->opacity_unfocused
);
215 window_settrans(awesomeconf
->display
, tag
->client_sel
->win
, -1);
218 XSetInputFocus(awesomeconf
->display
, RootWindow(awesomeconf
->display
, awesomeconf
->phys_screen
), RevertToPointerRoot
, CurrentTime
);
222 /** Load windows properties, restoring client's tag
223 * and floating state before awesome was restarted if any
224 * \todo this may bug if number of tags is != than before
225 * \param c Client ref
226 * \param ntags tags number
229 loadprops(Client
* c
, int ntags
)
235 prop
= p_new(char, ntags
+ 2);
237 if(xgettextprop(c
->display
, c
->win
, AWESOMEPROPS_ATOM(c
->display
), prop
, ntags
+ 2))
239 for(i
= 0; i
< ntags
&& prop
[i
]; i
++)
240 if((c
->tags
[i
] = prop
[i
] == '1'))
242 if(i
<= ntags
&& prop
[i
])
243 c
->isfloating
= prop
[i
] == '1';
251 /** Manage a new client
252 * \param w The window
253 * \param wa Window attributes
254 * \param awesomeconf awesome config
257 client_manage(Window w
, XWindowAttributes
*wa
, awesome_config
*awesomeconf
)
260 Client
*c
, *t
= NULL
;
264 ScreenInfo
*screen_info
= get_screen_info(awesomeconf
->display
, awesomeconf
->screen
, NULL
);
266 c
= p_new(Client
, 1);
269 c
->x
= c
->rx
= wa
->x
;
270 c
->y
= c
->ry
= wa
->y
;
271 c
->w
= c
->rw
= wa
->width
;
272 c
->h
= c
->rh
= wa
->height
;
273 c
->oldborder
= wa
->border_width
;
275 c
->display
= awesomeconf
->display
;
276 c
->phys_screen
= awesomeconf
->phys_screen
;
278 /* if window request fullscreen mode */
279 if(c
->w
== screen_info
[awesomeconf
->screen
].width
&& c
->h
== screen_info
[awesomeconf
->screen
].height
)
281 c
->x
= screen_info
[awesomeconf
->screen
].x_org
;
282 c
->y
= screen_info
[awesomeconf
->screen
].y_org
;
284 c
->border
= wa
->border_width
;
288 ScreenInfo
*display_info
= get_display_info(c
->display
, c
->phys_screen
, &awesomeconf
->statusbar
);
290 if(c
->x
+ c
->w
+ 2 * c
->border
> display_info
->x_org
+ display_info
->width
)
291 c
->x
= c
->rx
= display_info
->x_org
+ display_info
->width
- c
->w
- 2 * c
->border
;
292 if(c
->y
+ c
->h
+ 2 * c
->border
> display_info
->y_org
+ display_info
->height
)
293 c
->y
= c
->ry
= display_info
->y_org
+ display_info
->height
- c
->h
- 2 * c
->border
;
294 if(c
->x
< display_info
->x_org
)
295 c
->x
= c
->rx
= display_info
->x_org
;
296 if(c
->y
< display_info
->y_org
)
297 c
->y
= c
->ry
= display_info
->y_org
;
299 c
->border
= awesomeconf
->borderpx
;
301 p_delete(&display_info
);
303 p_delete(&screen_info
);
306 wc
.border_width
= c
->border
;
307 XConfigureWindow(c
->display
, w
, CWBorderWidth
, &wc
);
308 XSetWindowBorder(c
->display
, w
, awesomeconf
->colors_normal
[ColBorder
].pixel
);
310 /* propagates border_width, if size doesn't change */
311 window_configure(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
, c
->border
);
313 /* update sizehint */
316 XSelectInput(c
->display
, w
, StructureNotifyMask
| PropertyChangeMask
| EnterWindowMask
);
319 if(awesomeconf
->have_shape
)
321 XShapeSelectInput(c
->display
, w
, ShapeNotifyMask
);
322 window_setshape(c
->display
, c
->phys_screen
, c
->win
);
326 window_grabbuttons(c
->display
, c
->phys_screen
, c
->win
,
327 False
, True
, awesomeconf
->modkey
, awesomeconf
->numlockmask
);
329 /* update window title */
332 /* move client to screen: this will set screen and create tags array */
333 c
->screen
= get_screen_bycoord(c
->display
, c
->x
, c
->y
);
334 move_client_to_screen(c
, awesomeconf
, True
);
336 /* check for transient and set tags like its parent */
337 if((rettrans
= XGetTransientForHint(c
->display
, w
, &trans
) == Success
)
338 && (t
= get_client_bywin(*awesomeconf
->clients
, trans
)))
339 for(i
= 0; i
< awesomeconf
->ntags
; i
++)
340 c
->tags
[i
] = t
->tags
[i
];
342 /* loadprops or apply rules if no props */
343 if(!loadprops(c
, awesomeconf
->ntags
))
344 applyrules(c
, awesomeconf
);
346 /* should be floating if transsient or fixed) */
348 c
->isfloating
= (rettrans
== Success
) || c
->isfixed
;
351 saveprops(c
, awesomeconf
->ntags
);
353 /* attach to the stack */
354 client_attach(awesomeconf
->clients
, c
);
356 /* some windows require this */
357 XMoveResizeWindow(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
);
359 focus(c
, True
, awesomeconf
);
361 /* rearrange to display new window */
362 arrange(awesomeconf
);
366 client_resize(Client
*c
, int x
, int y
, int w
, int h
, awesome_config
*awesomeconf
,
367 Bool sizehints
, Bool volatile_coords
)
369 double dx
, dy
, max
, min
, ratio
;
375 if(c
->minay
> 0 && c
->maxay
> 0 && (h
- c
->baseh
) > 0 && (w
- c
->basew
) > 0)
377 dx
= (double) (w
- c
->basew
);
378 dy
= (double) (h
- c
->baseh
);
379 min
= (double) (c
->minax
) / (double) (c
->minay
);
380 max
= (double) (c
->maxax
) / (double) (c
->maxay
);
382 if(max
> 0 && min
> 0 && ratio
> 0)
386 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
388 w
= (int) dx
+ c
->basew
;
389 h
= (int) dy
+ c
->baseh
;
393 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
395 w
= (int) dx
+ c
->basew
;
396 h
= (int) dy
+ c
->baseh
;
400 if(c
->minw
&& w
< c
->minw
)
402 if(c
->minh
&& h
< c
->minh
)
404 if(c
->maxw
&& w
> c
->maxw
)
406 if(c
->maxh
&& h
> c
->maxh
)
409 w
-= (w
- c
->basew
) % c
->incw
;
411 h
-= (h
- c
->baseh
) % c
->inch
;
415 /* offscreen appearance fixes */
416 si
= get_display_info(c
->display
, c
->phys_screen
, NULL
);
418 x
= si
->width
- w
- 2 * c
->border
;
420 y
= si
->height
- h
- 2 * c
->border
;
422 if(x
+ w
+ 2 * c
->border
< 0)
424 if(y
+ h
+ 2 * c
->border
< 0)
426 if(c
->x
!= x
|| c
->y
!= y
|| c
->w
!= w
|| c
->h
!= h
)
431 c
->h
= wc
.height
= h
;
434 || get_current_layout(awesomeconf
->tags
, awesomeconf
->ntags
)->arrange
== layout_floating
))
441 wc
.border_width
= c
->border
;
442 XConfigureWindow(c
->display
, c
->win
, CWX
| CWY
| CWWidth
| CWHeight
| CWBorderWidth
, &wc
);
443 window_configure(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
, c
->border
);
444 XSync(c
->display
, False
);
445 if((c
->x
>= 0 || c
->y
>= 0) && XineramaIsActive(c
->display
))
447 int new_screen
= get_screen_bycoord(c
->display
, c
->x
, c
->y
);
448 if(c
->screen
!= new_screen
)
449 move_client_to_screen(c
, &awesomeconf
[new_screen
- awesomeconf
->screen
], False
);
455 saveprops(Client
* c
, int ntags
)
460 prop
= p_new(char, ntags
+ 2);
462 for(i
= 0; i
< ntags
; i
++)
463 prop
[i
] = c
->tags
[i
] ? '1' : '0';
466 prop
[i
] = c
->isfloating
? '1' : '0';
470 XChangeProperty(c
->display
, c
->win
, AWESOMEPROPS_ATOM(c
->display
), XA_STRING
, 8,
471 PropModeReplace
, (unsigned char *) prop
, i
);
477 client_unban(Client
*c
)
479 XMapWindow(c
->display
, c
->win
);
480 window_setstate(c
->display
, c
->win
, NormalState
);
484 client_unmanage(Client
*c
, long state
, awesome_config
*awesomeconf
)
489 wc
.border_width
= c
->oldborder
;
490 /* The server grab construct avoids race conditions. */
491 XGrabServer(c
->display
);
492 XConfigureWindow(c
->display
, c
->win
, CWBorderWidth
, &wc
); /* restore border */
493 client_detach(awesomeconf
->clients
, c
);
494 if(get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
== c
)
495 focus(NULL
, True
, awesomeconf
);
496 for(tag
= 0; tag
< awesomeconf
->ntags
; tag
++)
497 if(awesomeconf
->tags
[tag
].client_sel
== c
)
498 awesomeconf
->tags
[tag
].client_sel
= NULL
;
499 XUngrabButton(c
->display
, AnyButton
, AnyModifier
, c
->win
);
500 window_setstate(c
->display
, c
->win
, state
);
501 XSync(c
->display
, False
);
502 XSetErrorHandler(xerror
);
503 XUngrabServer(c
->display
);
504 if(state
!= NormalState
)
505 arrange(awesomeconf
);
511 updatesizehints(Client
*c
)
516 if(!XGetWMNormalHints(c
->display
, c
->win
, &size
, &msize
) || !size
.flags
)
518 c
->flags
= size
.flags
;
519 if(c
->flags
& PBaseSize
)
521 c
->basew
= size
.base_width
;
522 c
->baseh
= size
.base_height
;
524 else if(c
->flags
& PMinSize
)
526 c
->basew
= size
.min_width
;
527 c
->baseh
= size
.min_height
;
530 c
->basew
= c
->baseh
= 0;
531 if(c
->flags
& PResizeInc
)
533 c
->incw
= size
.width_inc
;
534 c
->inch
= size
.height_inc
;
537 c
->incw
= c
->inch
= 0;
539 if(c
->flags
& PMaxSize
)
541 c
->maxw
= size
.max_width
;
542 c
->maxh
= size
.max_height
;
545 c
->maxw
= c
->maxh
= 0;
547 if(c
->flags
& PMinSize
)
549 c
->minw
= size
.min_width
;
550 c
->minh
= size
.min_height
;
552 else if(c
->flags
& PBaseSize
)
554 c
->minw
= size
.base_width
;
555 c
->minh
= size
.base_height
;
558 c
->minw
= c
->minh
= 0;
560 if(c
->flags
& PAspect
)
562 c
->minax
= size
.min_aspect
.x
;
563 c
->maxax
= size
.max_aspect
.x
;
564 c
->minay
= size
.min_aspect
.y
;
565 c
->maxay
= size
.max_aspect
.y
;
568 c
->minax
= c
->maxax
= c
->minay
= c
->maxay
= 0;
570 c
->isfixed
= (c
->maxw
&& c
->minw
&& c
->maxh
&& c
->minh
571 && c
->maxw
== c
->minw
&& c
->maxh
== c
->minh
);
574 /** Set selected client transparency
575 * \param awesomeconf awesome config
576 * \param arg unused arg
577 * \ingroup ui_callback
580 uicb_settrans(awesome_config
*awesomeconf
,
583 double delta
= 100.0, current_opacity
= 100.0;
587 unsigned long n
, left
;
588 unsigned int current_opacity_raw
= 0;
590 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
595 XGetWindowProperty(awesomeconf
->display
, sel
->win
,
596 XInternAtom(sel
->display
, "_NET_WM_WINDOW_OPACITY", False
),
597 0L, 1L, False
, XA_CARDINAL
, &actual
, &format
, &n
, &left
,
598 (unsigned char **) &data
);
601 memcpy(¤t_opacity_raw
, data
, sizeof(unsigned int));
603 current_opacity
= (current_opacity_raw
* 100.0) / 0xffffffff;
608 delta
= compute_new_value_from_arg(arg
, current_opacity
);
612 else if(delta
> 100.0)
618 if(delta
== 100.0 && !set_prop
)
619 window_settrans(sel
->display
, sel
->win
, -1);
621 window_settrans(sel
->display
, sel
->win
, delta
);
626 * \param awesomeconf awesome config
627 * \param arg X, +X or -X
628 * \ingroup ui_callback
631 uicb_setborder(awesome_config
*awesomeconf
,
637 if((awesomeconf
->borderpx
= (int) compute_new_value_from_arg(arg
, (double) awesomeconf
->borderpx
)) < 0)
638 awesomeconf
->borderpx
= 0;
642 uicb_swapnext(awesome_config
*awesomeconf
,
643 const char *arg
__attribute__ ((unused
)))
645 Client
*next
, *sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
650 for(next
= sel
->next
; next
&& !isvisible(next
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); next
= next
->next
);
653 client_swap(awesomeconf
->clients
, sel
, next
);
654 arrange(awesomeconf
);
656 focus(sel
, True
, awesomeconf
);
661 uicb_swapprev(awesome_config
*awesomeconf
,
662 const char *arg
__attribute__ ((unused
)))
664 Client
*prev
, *sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
669 for(prev
= sel
->prev
; prev
&& !isvisible(prev
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); prev
= prev
->prev
);
672 client_swap(awesomeconf
->clients
, prev
, sel
);
673 arrange(awesomeconf
);
675 focus(sel
, True
, awesomeconf
);
680 uicb_moveresize(awesome_config
*awesomeconf
,
683 int nx
, ny
, nw
, nh
, ox
, oy
, ow
, oh
;
684 char x
[8], y
[8], w
[8], h
[8];
685 int mx
, my
, dx
, dy
, nmx
, nmy
;
688 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
690 if(get_current_layout(awesomeconf
->tags
, awesomeconf
->ntags
)->arrange
!= layout_floating
)
691 if(!sel
|| !sel
->isfloating
|| sel
->isfixed
|| !arg
)
693 if(sscanf(arg
, "%s %s %s %s", x
, y
, w
, h
) != 4)
695 nx
= (int) compute_new_value_from_arg(x
, sel
->x
);
696 ny
= (int) compute_new_value_from_arg(y
, sel
->y
);
697 nw
= (int) compute_new_value_from_arg(w
, sel
->w
);
698 nh
= (int) compute_new_value_from_arg(h
, sel
->h
);
705 Bool xqp
= XQueryPointer(awesomeconf
->display
, RootWindow(awesomeconf
->display
, awesomeconf
->phys_screen
), &dummy
, &dummy
, &mx
, &my
, &dx
, &dy
, &dui
);
706 client_resize(sel
, nx
, ny
, nw
, nh
, awesomeconf
, True
, False
);
707 if (xqp
&& ox
<= mx
&& (ox
+ ow
) >= mx
&& oy
<= my
&& (oy
+ oh
) >= my
)
709 nmx
= mx
- ox
+ sel
->w
- ow
- 1 < 0 ? 0 : mx
- ox
+ sel
->w
- ow
- 1;
710 nmy
= my
- oy
+ sel
->h
- oh
- 1 < 0 ? 0 : my
- oy
+ sel
->h
- oh
- 1;
711 XWarpPointer(awesomeconf
->display
, None
, sel
->win
, 0, 0, 0, 0, nmx
, nmy
);
715 /** Kill selected client
716 * \param awesomeconf awesome config
718 * \ingroup ui_callback
721 uicb_killclient(awesome_config
*awesomeconf
,
722 const char *arg
__attribute__ ((unused
)))
725 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
729 if(isprotodel(sel
->display
, sel
->win
))
731 ev
.type
= ClientMessage
;
732 ev
.xclient
.window
= sel
->win
;
733 ev
.xclient
.message_type
= XInternAtom(awesomeconf
->display
, "WM_PROTOCOLS", False
);
734 ev
.xclient
.format
= 32;
735 ev
.xclient
.data
.l
[0] = XInternAtom(awesomeconf
->display
, "WM_DELETE_WINDOW", False
);
736 ev
.xclient
.data
.l
[1] = CurrentTime
;
737 XSendEvent(awesomeconf
->display
, sel
->win
, False
, NoEventMask
, &ev
);
740 XKillClient(awesomeconf
->display
, sel
->win
);
742 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99