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
->modkey
, awesomeconf
->numlockmask
);
194 XSetWindowBorder(awesomeconf
->tags
[i
].client_sel
->display
,
195 awesomeconf
->tags
[i
].client_sel
->win
,
196 awesomeconf
->colors_normal
[ColBorder
].pixel
);
197 window_settrans(awesomeconf
->tags
[i
].client_sel
->display
,
198 awesomeconf
->tags
[i
].client_sel
->win
, awesomeconf
->opacity_unfocused
);
202 if(c
->tab
.next
|| c
->tab
.prev
)
203 XSetWindowBorder(awesomeconf
->display
, c
->win
, awesomeconf
->colors_tab
[ColBorder
].pixel
);
205 XSetWindowBorder(awesomeconf
->display
, c
->win
, awesomeconf
->colors_selected
[ColBorder
].pixel
);
208 window_grabbuttons(c
->display
, c
->phys_screen
, c
->win
,
209 True
, True
, awesomeconf
->modkey
, awesomeconf
->numlockmask
);
213 drawstatusbar(awesomeconf
);
216 XSetInputFocus(tag
->client_sel
->display
, tag
->client_sel
->win
, RevertToPointerRoot
, CurrentTime
);
217 for(c
= *awesomeconf
->clients
; c
; c
= c
->next
)
218 if(c
!= tag
->client_sel
)
219 window_settrans(awesomeconf
->display
, tag
->client_sel
->win
, awesomeconf
->opacity_unfocused
);
220 window_settrans(awesomeconf
->display
, tag
->client_sel
->win
, -1);
223 XSetInputFocus(awesomeconf
->display
, RootWindow(awesomeconf
->display
, awesomeconf
->phys_screen
), RevertToPointerRoot
, CurrentTime
);
227 /** Load windows properties, restoring client's tag
228 * and floating state before awesome was restarted if any
229 * \todo this may bug if number of tags is != than before
230 * \param c Client ref
231 * \param ntags tags number
234 loadprops(Client
* c
, int ntags
)
240 prop
= p_new(char, ntags
+ 2);
242 if(xgettextprop(c
->display
, c
->win
, AWESOMEPROPS_ATOM(c
->display
), prop
, ntags
+ 2))
244 for(i
= 0; i
< ntags
&& prop
[i
]; i
++)
245 if((c
->tags
[i
] = prop
[i
] == '1'))
247 if(i
<= ntags
&& prop
[i
])
248 c
->isfloating
= prop
[i
] == '1';
256 /** Manage a new client
257 * \param w The window
258 * \param wa Window attributes
259 * \param awesomeconf awesome config
262 client_manage(Window w
, XWindowAttributes
*wa
, awesome_config
*awesomeconf
)
265 Client
*c
, *t
= NULL
;
269 ScreenInfo
*screen_info
= get_screen_info(awesomeconf
->display
, awesomeconf
->screen
, NULL
);
271 c
= p_new(Client
, 1);
274 c
->x
= c
->rx
= wa
->x
;
275 c
->y
= c
->ry
= wa
->y
;
276 c
->w
= c
->rw
= wa
->width
;
277 c
->h
= c
->rh
= wa
->height
;
278 c
->oldborder
= wa
->border_width
;
280 c
->display
= awesomeconf
->display
;
281 c
->phys_screen
= awesomeconf
->phys_screen
;
283 c
->tab
.isvisible
= True
;
285 /* if window request fullscreen mode */
286 if(c
->w
== screen_info
[awesomeconf
->screen
].width
&& c
->h
== screen_info
[awesomeconf
->screen
].height
)
288 c
->x
= screen_info
[awesomeconf
->screen
].x_org
;
289 c
->y
= screen_info
[awesomeconf
->screen
].y_org
;
291 c
->border
= wa
->border_width
;
295 ScreenInfo
*display_info
= get_display_info(c
->display
, c
->phys_screen
, &awesomeconf
->statusbar
);
297 if(c
->x
+ c
->w
+ 2 * c
->border
> display_info
->x_org
+ display_info
->width
)
298 c
->x
= c
->rx
= display_info
->x_org
+ display_info
->width
- c
->w
- 2 * c
->border
;
299 if(c
->y
+ c
->h
+ 2 * c
->border
> display_info
->y_org
+ display_info
->height
)
300 c
->y
= c
->ry
= display_info
->y_org
+ display_info
->height
- c
->h
- 2 * c
->border
;
301 if(c
->x
< display_info
->x_org
)
302 c
->x
= c
->rx
= display_info
->x_org
;
303 if(c
->y
< display_info
->y_org
)
304 c
->y
= c
->ry
= display_info
->y_org
;
306 c
->border
= awesomeconf
->borderpx
;
308 p_delete(&display_info
);
310 p_delete(&screen_info
);
313 wc
.border_width
= c
->border
;
314 XConfigureWindow(c
->display
, w
, CWBorderWidth
, &wc
);
315 XSetWindowBorder(c
->display
, w
, awesomeconf
->colors_normal
[ColBorder
].pixel
);
317 /* propagates border_width, if size doesn't change */
318 window_configure(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
, c
->border
);
320 /* update sizehint */
323 XSelectInput(c
->display
, w
, StructureNotifyMask
| PropertyChangeMask
| EnterWindowMask
);
326 if(awesomeconf
->have_shape
)
328 XShapeSelectInput(c
->display
, w
, ShapeNotifyMask
);
329 window_setshape(c
->display
, c
->phys_screen
, c
->win
);
333 window_grabbuttons(c
->display
, c
->phys_screen
, c
->win
,
334 False
, True
, awesomeconf
->modkey
, awesomeconf
->numlockmask
);
336 /* update window title */
339 /* move client to screen: this will set screen and create tags array */
340 c
->screen
= get_screen_bycoord(c
->display
, c
->x
, c
->y
);
341 move_client_to_screen(c
, awesomeconf
, True
);
343 /* check for transient and set tags like its parent */
344 if((rettrans
= XGetTransientForHint(c
->display
, w
, &trans
) == Success
)
345 && (t
= get_client_bywin(*awesomeconf
->clients
, trans
)))
346 for(i
= 0; i
< awesomeconf
->ntags
; i
++)
347 c
->tags
[i
] = t
->tags
[i
];
349 /* loadprops or apply rules if no props */
350 if(!loadprops(c
, awesomeconf
->ntags
))
351 applyrules(c
, awesomeconf
);
353 /* should be floating if transsient or fixed) */
355 c
->isfloating
= (rettrans
== Success
) || c
->isfixed
;
358 saveprops(c
, awesomeconf
->ntags
);
360 /* attach to the stack */
361 client_attach(awesomeconf
->clients
, c
);
363 /* some windows require this */
364 XMoveResizeWindow(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
);
366 focus(c
, True
, awesomeconf
);
368 /* rearrange to display new window */
369 arrange(awesomeconf
);
373 client_resize(Client
*c
, int x
, int y
, int w
, int h
, awesome_config
*awesomeconf
,
374 Bool sizehints
, Bool volatile_coords
)
376 double dx
, dy
, max
, min
, ratio
;
382 if(c
->minay
> 0 && c
->maxay
> 0 && (h
- c
->baseh
) > 0 && (w
- c
->basew
) > 0)
384 dx
= (double) (w
- c
->basew
);
385 dy
= (double) (h
- c
->baseh
);
386 min
= (double) (c
->minax
) / (double) (c
->minay
);
387 max
= (double) (c
->maxax
) / (double) (c
->maxay
);
389 if(max
> 0 && min
> 0 && ratio
> 0)
393 dy
= (dx
* min
+ dy
) / (min
* min
+ 1);
395 w
= (int) dx
+ c
->basew
;
396 h
= (int) dy
+ c
->baseh
;
400 dy
= (dx
* min
+ dy
) / (max
* max
+ 1);
402 w
= (int) dx
+ c
->basew
;
403 h
= (int) dy
+ c
->baseh
;
407 if(c
->minw
&& w
< c
->minw
)
409 if(c
->minh
&& h
< c
->minh
)
411 if(c
->maxw
&& w
> c
->maxw
)
413 if(c
->maxh
&& h
> c
->maxh
)
416 w
-= (w
- c
->basew
) % c
->incw
;
418 h
-= (h
- c
->baseh
) % c
->inch
;
422 /* offscreen appearance fixes */
423 si
= get_display_info(c
->display
, c
->phys_screen
, NULL
);
425 x
= si
->width
- w
- 2 * c
->border
;
427 y
= si
->height
- h
- 2 * c
->border
;
429 if(x
+ w
+ 2 * c
->border
< 0)
431 if(y
+ h
+ 2 * c
->border
< 0)
433 if(c
->x
!= x
|| c
->y
!= y
|| c
->w
!= w
|| c
->h
!= h
)
438 c
->h
= wc
.height
= h
;
441 || get_current_layout(awesomeconf
->tags
, awesomeconf
->ntags
)->arrange
== layout_floating
))
448 wc
.border_width
= c
->border
;
449 XConfigureWindow(c
->display
, c
->win
, CWX
| CWY
| CWWidth
| CWHeight
| CWBorderWidth
, &wc
);
450 window_configure(c
->display
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
, c
->border
);
451 XSync(c
->display
, False
);
452 if(c
->x
>=0 && c
->y
>= 0 && XineramaIsActive(c
->display
))
454 int new_screen
= get_screen_bycoord(c
->display
, c
->x
, c
->y
);
455 if(new_screen
>= 0 && c
->screen
!= new_screen
)
456 move_client_to_screen(c
, &awesomeconf
[new_screen
- awesomeconf
->screen
], False
);
462 saveprops(Client
* c
, int ntags
)
467 prop
= p_new(char, ntags
+ 2);
469 for(i
= 0; i
< ntags
; i
++)
470 prop
[i
] = c
->tags
[i
] ? '1' : '0';
473 prop
[i
] = c
->isfloating
? '1' : '0';
477 XChangeProperty(c
->display
, c
->win
, AWESOMEPROPS_ATOM(c
->display
), XA_STRING
, 8,
478 PropModeReplace
, (unsigned char *) prop
, i
);
484 client_unban(Client
*c
)
486 XMapWindow(c
->display
, c
->win
);
487 window_setstate(c
->display
, c
->win
, NormalState
);
491 client_unmanage(Client
*c
, long state
, awesome_config
*awesomeconf
)
497 wc
.border_width
= c
->oldborder
;
498 /* The server grab construct avoids race conditions. */
499 XGrabServer(c
->display
);
500 XConfigureWindow(c
->display
, c
->win
, CWBorderWidth
, &wc
); /* restore border */
501 client_detach(awesomeconf
->clients
, c
);
502 if(get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
== c
)
503 focus(NULL
, True
, awesomeconf
);
504 for(tag
= 0; tag
< awesomeconf
->ntags
; tag
++)
505 if(awesomeconf
->tags
[tag
].client_sel
== c
)
506 awesomeconf
->tags
[tag
].client_sel
= NULL
;
507 XUngrabButton(c
->display
, AnyButton
, AnyModifier
, c
->win
);
508 window_setstate(c
->display
, c
->win
, state
);
509 XSync(c
->display
, False
);
510 XSetErrorHandler(xerror
);
511 XUngrabServer(c
->display
);
512 if(state
!= NormalState
)
513 arrange(awesomeconf
);
519 updatesizehints(Client
*c
)
524 if(!XGetWMNormalHints(c
->display
, c
->win
, &size
, &msize
) || !size
.flags
)
526 c
->flags
= size
.flags
;
527 if(c
->flags
& PBaseSize
)
529 c
->basew
= size
.base_width
;
530 c
->baseh
= size
.base_height
;
532 else if(c
->flags
& PMinSize
)
534 c
->basew
= size
.min_width
;
535 c
->baseh
= size
.min_height
;
538 c
->basew
= c
->baseh
= 0;
539 if(c
->flags
& PResizeInc
)
541 c
->incw
= size
.width_inc
;
542 c
->inch
= size
.height_inc
;
545 c
->incw
= c
->inch
= 0;
547 if(c
->flags
& PMaxSize
)
549 c
->maxw
= size
.max_width
;
550 c
->maxh
= size
.max_height
;
553 c
->maxw
= c
->maxh
= 0;
555 if(c
->flags
& PMinSize
)
557 c
->minw
= size
.min_width
;
558 c
->minh
= size
.min_height
;
560 else if(c
->flags
& PBaseSize
)
562 c
->minw
= size
.base_width
;
563 c
->minh
= size
.base_height
;
566 c
->minw
= c
->minh
= 0;
568 if(c
->flags
& PAspect
)
570 c
->minax
= size
.min_aspect
.x
;
571 c
->maxax
= size
.max_aspect
.x
;
572 c
->minay
= size
.min_aspect
.y
;
573 c
->maxay
= size
.max_aspect
.y
;
576 c
->minax
= c
->maxax
= c
->minay
= c
->maxay
= 0;
578 c
->isfixed
= (c
->maxw
&& c
->minw
&& c
->maxh
&& c
->minh
579 && c
->maxw
== c
->minw
&& c
->maxh
== c
->minh
);
582 /** Set selected client transparency
583 * \param awesomeconf awesome config
584 * \param arg unused arg
585 * \ingroup ui_callback
588 uicb_settrans(awesome_config
*awesomeconf
,
591 double delta
= 100.0, current_opacity
= 100.0;
595 unsigned long n
, left
;
596 unsigned int current_opacity_raw
= 0;
598 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
603 XGetWindowProperty(awesomeconf
->display
, sel
->win
,
604 XInternAtom(sel
->display
, "_NET_WM_WINDOW_OPACITY", False
),
605 0L, 1L, False
, XA_CARDINAL
, &actual
, &format
, &n
, &left
,
606 (unsigned char **) &data
);
609 memcpy(¤t_opacity_raw
, data
, sizeof(unsigned int));
611 current_opacity
= (current_opacity_raw
* 100.0) / 0xffffffff;
616 delta
= compute_new_value_from_arg(arg
, current_opacity
);
620 else if(delta
> 100.0)
626 if(delta
== 100.0 && !set_prop
)
627 window_settrans(sel
->display
, sel
->win
, -1);
629 window_settrans(sel
->display
, sel
->win
, delta
);
634 * \param awesomeconf awesome config
635 * \param arg X, +X or -X
636 * \ingroup ui_callback
639 uicb_setborder(awesome_config
*awesomeconf
,
645 if((awesomeconf
->borderpx
= (int) compute_new_value_from_arg(arg
, (double) awesomeconf
->borderpx
)) < 0)
646 awesomeconf
->borderpx
= 0;
650 uicb_swapnext(awesome_config
*awesomeconf
,
651 const char *arg
__attribute__ ((unused
)))
653 Client
*next
, *sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
658 for(next
= sel
->next
; next
&& !isvisible(next
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); next
= next
->next
);
661 client_swap(awesomeconf
->clients
, sel
, next
);
662 arrange(awesomeconf
);
664 focus(sel
, True
, awesomeconf
);
669 uicb_swapprev(awesome_config
*awesomeconf
,
670 const char *arg
__attribute__ ((unused
)))
672 Client
*prev
, *sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
677 for(prev
= sel
->prev
; prev
&& !isvisible(prev
, awesomeconf
->screen
, awesomeconf
->tags
, awesomeconf
->ntags
); prev
= prev
->prev
);
680 client_swap(awesomeconf
->clients
, prev
, sel
);
681 arrange(awesomeconf
);
683 focus(sel
, True
, awesomeconf
);
688 uicb_moveresize(awesome_config
*awesomeconf
,
691 int nx
, ny
, nw
, nh
, ox
, oy
, ow
, oh
;
692 char x
[8], y
[8], w
[8], h
[8];
693 int mx
, my
, dx
, dy
, nmx
, nmy
;
696 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
698 if(get_current_layout(awesomeconf
->tags
, awesomeconf
->ntags
)->arrange
!= layout_floating
)
699 if(!sel
|| !sel
->isfloating
|| sel
->isfixed
|| !arg
)
701 if(sscanf(arg
, "%s %s %s %s", x
, y
, w
, h
) != 4)
703 nx
= (int) compute_new_value_from_arg(x
, sel
->x
);
704 ny
= (int) compute_new_value_from_arg(y
, sel
->y
);
705 nw
= (int) compute_new_value_from_arg(w
, sel
->w
);
706 nh
= (int) compute_new_value_from_arg(h
, sel
->h
);
713 Bool xqp
= XQueryPointer(awesomeconf
->display
, RootWindow(awesomeconf
->display
, awesomeconf
->phys_screen
), &dummy
, &dummy
, &mx
, &my
, &dx
, &dy
, &dui
);
714 client_resize(sel
, nx
, ny
, nw
, nh
, awesomeconf
, True
, False
);
715 if (xqp
&& ox
<= mx
&& (ox
+ ow
) >= mx
&& oy
<= my
&& (oy
+ oh
) >= my
)
717 nmx
= mx
- ox
+ sel
->w
- ow
- 1 < 0 ? 0 : mx
- ox
+ sel
->w
- ow
- 1;
718 nmy
= my
- oy
+ sel
->h
- oh
- 1 < 0 ? 0 : my
- oy
+ sel
->h
- oh
- 1;
719 XWarpPointer(awesomeconf
->display
, None
, sel
->win
, 0, 0, 0, 0, nmx
, nmy
);
723 /** Kill selected client
724 * \param awesomeconf awesome config
726 * \ingroup ui_callback
729 uicb_killclient(awesome_config
*awesomeconf
,
730 const char *arg
__attribute__ ((unused
)))
733 Client
*sel
= get_current_tag(awesomeconf
->tags
, awesomeconf
->ntags
)->client_sel
;
737 if(isprotodel(sel
->display
, sel
->win
))
739 ev
.type
= ClientMessage
;
740 ev
.xclient
.window
= sel
->win
;
741 ev
.xclient
.message_type
= XInternAtom(awesomeconf
->display
, "WM_PROTOCOLS", False
);
742 ev
.xclient
.format
= 32;
743 ev
.xclient
.data
.l
[0] = XInternAtom(awesomeconf
->display
, "WM_DELETE_WINDOW", False
);
744 ev
.xclient
.data
.l
[1] = CurrentTime
;
745 XSendEvent(awesomeconf
->display
, sel
->win
, False
, NoEventMask
, &ev
);
748 XKillClient(awesomeconf
->display
, sel
->win
);
750 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99