Enable multiple keys bindings to be executed with the same key press event
[awesome.git] / client.c
blobd3255c8a64dea816b3eeec8b0e31bf0d8a2e4caa
1 /*
2 * client.c - client management
4 * Copyright © 2007-2008 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.
22 #include <stdio.h>
23 #include <X11/Xatom.h>
24 #include <X11/extensions/shape.h>
26 #include "client.h"
27 #include "tag.h"
28 #include "rules.h"
29 #include "window.h"
30 #include "focus.h"
31 #include "ewmh.h"
32 #include "widget.h"
33 #include "screen.h"
34 #include "layouts/floating.h"
35 #include "common/xutil.h"
36 #include "common/xscreen.h"
38 extern AwesomeConf globalconf;
40 /** Load windows properties, restoring client's tag
41 * and floating state before awesome was restarted if any
42 * \todo this may bug if number of tags is != than before
43 * \param c Client ref
44 * \param screen Screen ID
45 * \return true if client had property
47 static Bool
48 client_loadprops(Client * c, int screen)
50 int i, ntags = 0;
51 Tag *tag;
52 char *prop;
53 Bool result = False;
55 for(tag = globalconf.screens[screen].tags; tag; tag = tag->next)
56 ntags++;
58 prop = p_new(char, ntags + 2);
60 if(xgettextprop(globalconf.display, c->win,
61 XInternAtom(globalconf.display, "_AWESOME_PROPERTIES", False),
62 prop, ntags + 2))
64 for(i = 0, tag = globalconf.screens[screen].tags; tag && i < ntags && prop[i]; i++, tag = tag->next)
65 if(prop[i] == '1')
67 tag_client(c, tag);
68 result = True;
70 else
71 untag_client(c, tag);
73 if(i <= ntags && prop[i])
74 client_setfloating(c, prop[i] == '1');
77 p_delete(&prop);
79 return result;
82 /** Check if client supports protocol WM_DELETE_WINDOW
83 * \param disp the display
84 * \param win the Window
85 * \return True if client has WM_DELETE_WINDOW
87 static Bool
88 client_isprotodel(Display *disp, Window win)
90 int i, n;
91 Atom *protocols;
92 Bool ret = False;
94 if(XGetWMProtocols(disp, win, &protocols, &n))
96 for(i = 0; !ret && i < n; i++)
97 if(protocols[i] == XInternAtom(disp, "WM_DELETE_WINDOW", False))
98 ret = True;
99 XFree(protocols);
101 return ret;
104 /** Get a Client by its window
105 * \param list Client list to look info
106 * \param w Client window to find
107 * \return client
109 Client *
110 client_get_bywin(Client *list, Window w)
112 Client *c;
114 for(c = list; c && c->win != w; c = c->next);
115 return c;
118 /** Get a client by its name
119 * \param list Client list
120 * \param name name to search
121 * \return first matching client
123 Client *
124 client_get_byname(Client *list, char *name)
126 Client *c;
128 for(c = list; c; c = c->next)
129 if(strstr(c->name, name))
130 return c;
132 return NULL;
135 void
136 client_updatetitlebar(Client *c)
138 DrawCtx *ctx;
139 int phys_screen;
140 style_t style;
141 area_t geometry;
143 if(!c->titlebar.position)
144 return;
146 phys_screen = get_phys_screen(c->screen);
148 ctx = draw_context_new(globalconf.display, phys_screen,
149 c->titlebar.sw->geometry.width,
150 c->titlebar.sw->geometry.height,
151 c->titlebar.sw->drawable);
153 style = globalconf.focus->client == c ?
154 globalconf.screens[c->screen].styles.focus :
155 globalconf.screens[c->screen].styles.normal;
157 geometry = c->titlebar.sw->geometry;
158 geometry.x = geometry.y = 0;
160 draw_text(ctx, geometry, AlignCenter, 0,
161 c->name, style);
163 simplewindow_refresh_drawable(c->titlebar.sw, phys_screen);
165 draw_context_delete(ctx);
168 /** Update client name attribute with its title
169 * \param c the client
171 void
172 client_updatetitle(Client *c)
174 if(!xgettextprop(globalconf.display, c->win,
175 XInternAtom(globalconf.display, "_NET_WM_NAME", False), c->name, sizeof(c->name)))
176 xgettextprop(globalconf.display, c->win,
177 XInternAtom(globalconf.display, "WM_NAME", False), c->name, sizeof(c->name));
179 client_updatetitlebar(c);
181 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
184 static void
185 client_unfocus(Client *c)
187 if(globalconf.screens[c->screen].opacity_unfocused != -1)
188 window_settrans(c->win, globalconf.screens[c->screen].opacity_unfocused);
189 focus_add_client(NULL);
190 XSetWindowBorder(globalconf.display, c->win,
191 globalconf.screens[c->screen].styles.normal.border.pixel);
192 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
193 client_updatetitlebar(c);
196 /** Ban client and unmap it
197 * \param c the client
199 void
200 client_ban(Client *c)
202 if(globalconf.focus->client == c)
203 client_unfocus(c);
204 XUnmapWindow(globalconf.display, c->win);
205 window_setstate(c->win, IconicState);
206 if(c->titlebar.sw)
207 XUnmapWindow(globalconf.display, c->titlebar.sw->window);
210 /** Give focus to client, or to first client if c is NULL
211 * \param c client
212 * \param screen Screen ID
214 void
215 client_focus(Client *c, int screen, Bool raise)
217 int phys_screen = get_phys_screen(screen);
219 /* if c is NULL or invisible, take next client in the focus history */
220 if(!c || (c && !client_isvisible(c, screen)))
222 c = focus_get_current_client(screen);
223 /* if c is still NULL take next client in the stack */
224 if(!c)
225 for(c = globalconf.clients; c && (c->skip || !client_isvisible(c, screen)); c = c->next);
228 /* unfocus current selected client */
229 if(globalconf.focus->client)
230 client_unfocus(globalconf.focus->client);
232 if(c)
234 /* unban the client before focusing or it will fail */
235 client_unban(c);
236 /* save sel in focus history */
237 focus_add_client(c);
238 if(globalconf.screens[c->screen].opacity_unfocused != -1)
239 window_settrans(c->win, -1);
240 XSetWindowBorder(globalconf.display, c->win,
241 globalconf.screens[screen].styles.focus.border.pixel);
242 client_updatetitlebar(c);
243 XSetInputFocus(globalconf.display, c->win, RevertToPointerRoot, CurrentTime);
244 if(raise)
246 XWindowChanges wc;
247 Layout *curlay = layout_get_current(screen);
248 if(c->isfloating || curlay->arrange == layout_floating)
250 XRaiseWindow(globalconf.display, c->win);
251 if(c->titlebar.position)
252 XRaiseWindow(globalconf.display, c->titlebar.sw->window);
254 else
256 Client *client;
257 wc.stack_mode = Below;
258 wc.sibling = None;
259 for(client = globalconf.clients; client; client = client->next)
260 if(client != c && client_isvisible(client, c->screen) && client->isfloating)
262 if(client->titlebar.position)
264 XConfigureWindow(globalconf.display, client->titlebar.sw->window,
265 CWSibling | CWStackMode, &wc);
266 wc.sibling = client->titlebar.sw->window;
268 XConfigureWindow(globalconf.display, client->win, CWSibling | CWStackMode, &wc);
269 wc.sibling = client->win;
271 if(c->titlebar.position)
273 XConfigureWindow(globalconf.display, c->titlebar.sw->window,
274 CWSibling | CWStackMode, &wc);
275 wc.sibling = c->titlebar.sw->window;
277 XConfigureWindow(globalconf.display, c->win, CWSibling | CWStackMode, &wc);
278 wc.sibling = c->win;
279 for(client = globalconf.clients; client; client = client->next)
280 if(client != c && IS_TILED(client, c->screen))
282 if(client->titlebar.position)
284 XConfigureWindow(globalconf.display, client->titlebar.sw->window,
285 CWSibling | CWStackMode, &wc);
286 wc.sibling = client->titlebar.sw->window;
288 XConfigureWindow(globalconf.display, client->win, CWSibling | CWStackMode, &wc);
289 wc.sibling = client->win;
293 /* since we're dropping EnterWindow events and sometimes the window
294 * will appear under the mouse, grabbuttons */
295 window_grabbuttons(phys_screen, c->win);
297 else
298 XSetInputFocus(globalconf.display,
299 RootWindow(globalconf.display, phys_screen),
300 RevertToPointerRoot, CurrentTime);
302 widget_invalidate_cache(screen, WIDGET_CACHE_CLIENTS);
303 ewmh_update_net_active_window(phys_screen);
306 /** Manage a new client
307 * \param w The window
308 * \param wa Window attributes
309 * \param screen Screen ID
311 void
312 client_manage(Window w, XWindowAttributes *wa, int screen)
314 Client *c, *t = NULL;
315 Window trans;
316 Bool rettrans, retloadprops;
317 XWindowChanges wc;
318 Tag *tag;
319 Rule *rule;
320 area_t screen_geom;
321 int phys_screen = get_phys_screen(screen), titlebar_height;
322 long flags;
324 c = p_new(Client, 1);
326 c->screen = screen_get_bycoord(globalconf.screens_info, screen, wa->x, wa->y);
328 screen_geom = screen_get_area(c->screen,
329 globalconf.screens[screen].statusbar,
330 &globalconf.screens[screen].padding);
331 /* Initial values */
332 c->win = w;
333 c->geometry.x = c->f_geometry.x = c->m_geometry.x = MAX(wa->x, screen_geom.x);
334 c->geometry.y = c->f_geometry.y = c->m_geometry.y = MAX(wa->y, screen_geom.y);
335 c->geometry.width = c->f_geometry.width = c->m_geometry.width = wa->width;
336 c->geometry.height = c->f_geometry.height = c->m_geometry.height = wa->height;
337 c->oldborder = wa->border_width;
338 c->newcomer = True;
340 /* Set windows borders */
341 wc.border_width = c->border = globalconf.screens[screen].borderpx;
342 XConfigureWindow(globalconf.display, w, CWBorderWidth, &wc);
343 XSetWindowBorder(globalconf.display, w, c->border);
344 /* propagates border_width, if size doesn't change */
345 window_configure(c->win, c->geometry, c->border);
347 /* update window title */
348 client_updatetitle(c);
350 /* update hints */
351 flags = client_updatesizehints(c);
352 client_updatewmhints(c);
354 /* Try to load props if any */
355 retloadprops = client_loadprops(c, screen);
357 /* Then check clients hints */
358 ewmh_check_client_hints(c);
360 /* default titlebar position */
361 c->titlebar.position = globalconf.screens[screen].titlebar_default.position;
363 /* First check clients hints */
364 ewmh_check_client_hints(c);
366 /* get the matching rule if any */
367 rule = rule_matching_client(c);
369 /* Then apply rules if no props */
370 if(!retloadprops)
372 /* Get the client's rule */
373 if(rule)
375 if(rule->screen != RULE_NOSCREEN)
376 move_client_to_screen(c, rule->screen, True);
377 else
378 move_client_to_screen(c, screen, True);
379 tag_client_with_rule(c, rule);
381 switch(rule->isfloating)
383 case Maybe:
384 break;
385 case Yes:
386 client_setfloating(c, True);
387 break;
388 case No:
389 client_setfloating(c, False);
390 break;
393 if(rule->opacity >= 0.0f)
394 window_settrans(c->win, rule->opacity);
396 if(rule->titlebar.position != Auto)
397 c->titlebar.position = rule->titlebar.position;
399 else
400 move_client_to_screen(c, screen, True);
404 switch(c->titlebar.position)
406 case Top:
407 titlebar_height = 1.5 * MAX(globalconf.screens[c->screen].styles.normal.font->height,
408 MAX(globalconf.screens[c->screen].styles.focus.font->height,
409 globalconf.screens[c->screen].styles.urgent.font->height)),
410 c->titlebar.sw = simplewindow_new(globalconf.display,
411 phys_screen,
412 c->geometry.x,
413 c->geometry.y - titlebar_height,
414 c->geometry.width + 2 * c->border,
415 titlebar_height,
417 break;
418 default:
419 break;
422 /* check for transient and set tags like its parent,
423 * XGetTransientForHint returns 1 on success
425 if((rettrans = XGetTransientForHint(globalconf.display, w, &trans))
426 && (t = client_get_bywin(globalconf.clients, trans)))
427 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
428 if(is_client_tagged(t, tag))
429 tag_client(c, tag);
431 /* should be floating if transsient or fixed */
432 if(!c->isfloating)
433 client_setfloating(c, rettrans || c->isfixed);
435 if(!(flags & (USPosition | PPosition)))
437 c->f_geometry =
438 globalconf.screens[c->screen].floating_placement(c->f_geometry, c->border, c->screen);
439 switch(c->titlebar.position)
441 case Top:
442 c->f_geometry.y += c->titlebar.sw->geometry.height;
443 break;
444 default:
445 break;
449 XSelectInput(globalconf.display, w, StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
451 /* handle xshape */
452 if(globalconf.have_shape)
454 XShapeSelectInput(globalconf.display, w, ShapeNotifyMask);
455 window_setshape(phys_screen, c->win);
458 /* attach to the stack */
459 if(rule)
460 switch(rule->ismaster)
462 case Yes:
463 client_list_push(&globalconf.clients, c);
464 break;
465 case No:
466 client_list_append(&globalconf.clients, c);
467 break;
468 case Maybe:
469 rule = NULL;
470 break;
473 if(!rule)
475 if(globalconf.screens[c->screen].new_become_master)
476 client_list_push(&globalconf.clients, c);
477 else
478 client_list_append(&globalconf.clients, c);
481 /* some windows require this */
482 XMoveResizeWindow(globalconf.display, c->win, c->geometry.x, c->geometry.y,
483 c->geometry.width, c->geometry.height);
485 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
486 ewmh_update_net_client_list(phys_screen);
489 area_t
490 client_titlebar_update_geometry(Client *c, area_t geometry)
492 if(!c->titlebar.position)
493 return geometry;;
495 simplewindow_move_resize(c->titlebar.sw,
496 geometry.x,
497 geometry.y,
498 geometry.width,
499 c->titlebar.sw->geometry.height);
501 client_updatetitlebar(c);
502 geometry.y += c->titlebar.sw->geometry.height;
503 geometry.height -= c->titlebar.sw->geometry.height;
505 return geometry;
508 area_t
509 client_geometry_hints(Client *c, area_t geometry)
511 double dx, dy, max, min, ratio;
513 if(c->minay > 0 && c->maxay > 0 && (geometry.height - c->baseh) > 0
514 && (geometry.width - c->basew) > 0)
516 dx = (double) (geometry.width - c->basew);
517 dy = (double) (geometry.height - c->baseh);
518 min = (double) (c->minax) / (double) (c->minay);
519 max = (double) (c->maxax) / (double) (c->maxay);
520 ratio = dx / dy;
521 if(max > 0 && min > 0 && ratio > 0)
523 if(ratio < min)
525 dy = (dx * min + dy) / (min * min + 1);
526 dx = dy * min;
527 geometry.width = (int) dx + c->basew;
528 geometry.height = (int) dy + c->baseh;
530 else if(ratio > max)
532 dy = (dx * min + dy) / (max * max + 1);
533 dx = dy * min;
534 geometry.width = (int) dx + c->basew;
535 geometry.height = (int) dy + c->baseh;
539 if(c->minw && geometry.width < c->minw)
540 geometry.width = c->minw;
541 if(c->minh && geometry.height < c->minh)
542 geometry.height = c->minh;
543 if(c->maxw && geometry.width > c->maxw)
544 geometry.width = c->maxw;
545 if(c->maxh && geometry.height > c->maxh)
546 geometry.height = c->maxh;
547 if(c->incw)
548 geometry.width -= (geometry.width - c->basew) % c->incw;
549 if(c->inch)
550 geometry.height -= (geometry.height - c->baseh) % c->inch;
552 return geometry;
555 /** Resize client window
556 * \param c client to resize
557 * \param geometry new window geometry
558 * \param return True if resize has been done
560 Bool
561 client_resize(Client *c, area_t geometry)
563 int new_screen;
564 area_t area;
565 XWindowChanges wc;
567 if(geometry.width <= 0 || geometry.height <= 0)
568 return False;
570 /* offscreen appearance fixes */
571 area = get_display_area(get_phys_screen(c->screen),
572 NULL,
573 &globalconf.screens[c->screen].padding);
574 if(geometry.x > area.width)
575 geometry.x = area.width - geometry.width - 2 * c->border;
576 if(geometry.y > area.height)
577 geometry.y = area.height - geometry.height - 2 * c->border;
578 if(geometry.x + geometry.width + 2 * c->border < 0)
579 geometry.x = 0;
580 if(geometry.y + geometry.height + 2 * c->border < 0)
581 geometry.y = 0;
583 if(c->geometry.x != geometry.x || c->geometry.y != geometry.y
584 || c->geometry.width != geometry.width || c->geometry.height != geometry.height)
586 new_screen = screen_get_bycoord(globalconf.screens_info, c->screen, geometry.x, geometry.y);
588 c->geometry.x = wc.x = geometry.x;
589 c->geometry.width = wc.width = geometry.width;
590 c->geometry.y = wc.y = geometry.y;
591 c->geometry.height = wc.height = geometry.height;
593 /* save the floating geometry if the window is floating but not
594 * maximized */
595 if(c->isfloating
596 || layout_get_current(new_screen)->arrange == layout_floating)
598 if(!c->ismax)
599 c->f_geometry = geometry;
601 if(c->titlebar.sw)
603 switch(c->titlebar.position)
605 case Top:
606 simplewindow_move_resize(c->titlebar.sw,
607 geometry.x,
608 geometry.y - c->titlebar.sw->geometry.height,
609 geometry.width,
610 c->titlebar.sw->geometry.height);
611 break;
612 default:
613 break;
615 client_updatetitlebar(c);
619 XConfigureWindow(globalconf.display, c->win,
620 CWX | CWY | CWWidth | CWHeight, &wc);
621 window_configure(c->win, geometry, c->border);
623 if(c->screen != new_screen)
624 move_client_to_screen(c, new_screen, False);
626 return True;
628 return False;
631 void
632 client_setfloating(Client *c, Bool floating)
634 if(c->isfloating != floating)
636 if((c->isfloating = floating))
638 client_resize(c, c->f_geometry);
639 XRaiseWindow(globalconf.display, c->win);
641 else
643 XLowerWindow(globalconf.display, c->win);
644 if(c->ismax)
646 c->ismax = False;
647 client_resize(c, c->m_geometry);
650 if(client_isvisible(c, c->screen))
651 globalconf.screens[c->screen].need_arrange = True;
652 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
653 client_saveprops(c);
657 /** Save client properties as an X property
658 * \param c client
660 void
661 client_saveprops(Client *c)
663 int i = 0, ntags = 0;
664 char *prop;
665 Tag *tag;
667 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
668 ntags++;
670 prop = p_new(char, ntags + 2);
672 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next, i++)
673 prop[i] = is_client_tagged(c, tag) ? '1' : '0';
675 if(i <= ntags)
676 prop[i] = c->isfloating ? '1' : '0';
678 prop[++i] = '\0';
680 XChangeProperty(globalconf.display, c->win,
681 XInternAtom(globalconf.display, "_AWESOME_PROPERTIES", False),
682 XA_STRING, 8, PropModeReplace, (unsigned char *) prop, i);
684 p_delete(&prop);
687 void
688 client_unban(Client *c)
690 XMapWindow(globalconf.display, c->win);
691 window_setstate(c->win, NormalState);
692 if(c->titlebar.sw && c->titlebar.position != Off)
693 XMapWindow(globalconf.display, c->titlebar.sw->window);
696 void
697 client_unmanage(Client *c)
699 XWindowChanges wc;
700 Tag *tag;
702 wc.border_width = c->oldborder;
704 /* The server grab construct avoids race conditions. */
705 XGrabServer(globalconf.display);
707 XConfigureWindow(globalconf.display, c->win, CWBorderWidth, &wc); /* restore border */
709 /* remove client everywhere */
710 client_list_detach(&globalconf.clients, c);
711 focus_delete_client(c);
712 if(globalconf.scratch.client == c)
713 globalconf.scratch.client = NULL;
714 for(tag = globalconf.screens[c->screen].tags; tag; tag = tag->next)
715 untag_client(c, tag);
717 if(globalconf.focus->client == c)
718 client_focus(NULL, c->screen, True);
720 XUngrabButton(globalconf.display, AnyButton, AnyModifier, c->win);
721 window_setstate(c->win, WithdrawnState);
723 XSync(globalconf.display, False);
724 XUngrabServer(globalconf.display);
726 if(c->titlebar.sw)
727 simplewindow_delete(c->titlebar.sw);
729 p_delete(&c);
732 void
733 client_updatewmhints(Client *c)
735 XWMHints *wmh;
737 if((wmh = XGetWMHints(globalconf.display, c->win)))
739 if((c->isurgent = (wmh->flags & XUrgencyHint)))
740 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
741 if((wmh->flags & StateHint) && wmh->initial_state == WithdrawnState)
743 c->border = 0;
744 c->skip = True;
746 XFree(wmh);
750 long
751 client_updatesizehints(Client *c)
753 long msize;
754 XSizeHints size;
756 if(!XGetWMNormalHints(globalconf.display, c->win, &size, &msize))
757 return 0L;
759 if(size.flags & PBaseSize)
761 c->basew = size.base_width;
762 c->baseh = size.base_height;
764 else if(size.flags & PMinSize)
766 c->basew = size.min_width;
767 c->baseh = size.min_height;
769 else
770 c->basew = c->baseh = 0;
771 if(size.flags & PResizeInc)
773 c->incw = size.width_inc;
774 c->inch = size.height_inc;
776 else
777 c->incw = c->inch = 0;
779 if(size.flags & PMaxSize)
781 c->maxw = size.max_width;
782 c->maxh = size.max_height;
784 else
785 c->maxw = c->maxh = 0;
787 if(size.flags & PMinSize)
789 c->minw = size.min_width;
790 c->minh = size.min_height;
792 else if(size.flags & PBaseSize)
794 c->minw = size.base_width;
795 c->minh = size.base_height;
797 else
798 c->minw = c->minh = 0;
800 if(size.flags & PAspect)
802 c->minax = size.min_aspect.x;
803 c->maxax = size.max_aspect.x;
804 c->minay = size.min_aspect.y;
805 c->maxay = size.max_aspect.y;
807 else
808 c->minax = c->maxax = c->minay = c->maxay = 0;
810 if(c->maxw && c->minw && c->maxh && c->minh
811 && c->maxw == c->minw && c->maxh == c->minh)
812 c->isfixed = True;
814 return size.flags;
817 /** Returns True if a client is tagged
818 * with one of the tags
819 * \return True or False
821 Bool
822 client_isvisible(Client *c, int screen)
824 Tag *tag;
826 if(!c || c->screen != screen)
827 return False;
829 if(globalconf.scratch.client == c)
830 return globalconf.scratch.isvisible;
832 for(tag = globalconf.screens[screen].tags; tag; tag = tag->next)
833 if(tag->selected && is_client_tagged(c, tag))
834 return True;
835 return False;
838 /** Set selected client transparency
839 * \param screen Screen ID
840 * \param arg unused arg
841 * \ingroup ui_callback
843 void
844 uicb_client_settrans(int screen __attribute__ ((unused)), char *arg)
846 double delta = 100.0, current_opacity = 100.0;
847 unsigned char *data;
848 Atom actual;
849 int format;
850 unsigned long n, left;
851 unsigned int current_opacity_raw = 0;
852 int set_prop = 0;
853 Client *sel = globalconf.focus->client;
855 if(!sel)
856 return;
858 XGetWindowProperty(globalconf.display, sel->win,
859 XInternAtom(globalconf.display, "_NET_WM_WINDOW_OPACITY", False),
860 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left,
861 (unsigned char **) &data);
862 if(data)
864 memcpy(&current_opacity_raw, data, sizeof(unsigned int));
865 XFree(data);
866 current_opacity = (current_opacity_raw * 100.0) / 0xffffffff;
868 else
869 set_prop = 1;
871 delta = compute_new_value_from_arg(arg, current_opacity);
873 if(delta <= 0.0)
874 delta = 0.0;
875 else if(delta > 100.0)
877 delta = 100.0;
878 set_prop = 1;
881 if(delta == 100.0 && !set_prop)
882 window_settrans(sel->win, -1);
883 else
884 window_settrans(sel->win, delta);
887 static Client *
888 client_find_visible(Client *sel, Bool reverse)
890 Client *next;
891 Client *(*client_iter)(Client **, Client *) = client_list_next_cycle;
893 if(!sel) return NULL;
895 if(reverse)
896 client_iter = client_list_prev_cycle;
898 /* look for previous or next starting at sel */
900 for(next = client_iter(&globalconf.clients, sel);
901 next && (next->skip || !client_isvisible(next, sel->screen));
902 next = client_iter(&globalconf.clients, next));
904 return next;
907 /** Swap current with previous client
908 * \param screen Screen ID
909 * \param arg nothing
910 * \ingroup ui_callback
912 void
913 uicb_client_swapprev(int screen __attribute__ ((unused)),
914 char *arg __attribute__ ((unused)))
916 Client *prev;
918 if((prev = client_find_visible(globalconf.focus->client, True)))
920 client_list_swap(&globalconf.clients, prev, globalconf.focus->client);
921 globalconf.screens[prev->screen].need_arrange = True;
925 /** Swap current with next client
926 * \param screen Screen ID
927 * \param arg nothing
928 * \ingroup ui_callback
930 void
931 uicb_client_swapnext(int screen __attribute__ ((unused)),
932 char *arg __attribute__ ((unused)))
934 Client *next;
936 if((next = client_find_visible(globalconf.focus->client, False)))
938 client_list_swap(&globalconf.clients, globalconf.focus->client, next);
939 globalconf.screens[next->screen].need_arrange = True;
943 /** Move and resize client
944 * \param screen Screen ID
945 * \param arg x y w h
946 * \ingroup ui_callback
948 void
949 uicb_client_moveresize(int screen, char *arg)
951 int ox, oy, ow, oh; /* old geometry */
952 char x[8], y[8], w[8], h[8];
953 int mx, my, dx, dy, nmx, nmy;
954 unsigned int dui;
955 Window dummy;
956 area_t geometry;
957 Client *sel = globalconf.focus->client;
958 Layout *curlay = layout_get_current(screen);
960 if(!sel || sel->isfixed || !arg ||
961 (curlay->arrange != layout_floating && !sel->isfloating))
962 return;
964 if(sscanf(arg, "%s %s %s %s", x, y, w, h) != 4)
965 return;
967 geometry.x = (int) compute_new_value_from_arg(x, sel->geometry.x);
968 geometry.y = (int) compute_new_value_from_arg(y, sel->geometry.y);
969 geometry.width = (int) compute_new_value_from_arg(w, sel->geometry.width);
970 geometry.height = (int) compute_new_value_from_arg(h, sel->geometry.height);
972 ox = sel->geometry.x;
973 oy = sel->geometry.y;
974 ow = sel->geometry.width;
975 oh = sel->geometry.height;
977 Bool xqp = XQueryPointer(globalconf.display,
978 RootWindow(globalconf.display,
979 get_phys_screen(screen)),
980 &dummy, &dummy, &mx, &my, &dx, &dy, &dui);
981 if(globalconf.screens[sel->screen].resize_hints)
982 geometry = client_geometry_hints(sel, geometry);
983 client_resize(sel, geometry);
984 if (xqp && ox <= mx && (ox + 2 * sel->border + ow) >= mx &&
985 oy <= my && (oy + 2 * sel->border + oh) >= my)
987 nmx = mx - (ox + sel->border) + sel->geometry.width - ow;
988 nmy = my - (oy + sel->border) + sel->geometry.height - oh;
990 if(nmx < -sel->border) /* can happen on a resize */
991 nmx = -sel->border;
992 if(nmy < -sel->border)
993 nmy = -sel->border;
995 XWarpPointer(globalconf.display,
996 None, sel->win,
997 0, 0, 0, 0, nmx, nmy);
1001 void
1002 client_kill(Client *c)
1004 XEvent ev;
1006 if(client_isprotodel(globalconf.display, c->win))
1008 ev.type = ClientMessage;
1009 ev.xclient.window = c->win;
1010 ev.xclient.message_type = XInternAtom(globalconf.display, "WM_PROTOCOLS", False);
1011 ev.xclient.format = 32;
1012 ev.xclient.data.l[0] = XInternAtom(globalconf.display, "WM_DELETE_WINDOW", False);
1013 ev.xclient.data.l[1] = CurrentTime;
1014 XSendEvent(globalconf.display, c->win, False, NoEventMask, &ev);
1016 else
1017 XKillClient(globalconf.display, c->win);
1020 /** Kill selected client
1021 * \param screen Screen ID
1022 * \param arg unused
1023 * \ingroup ui_callback
1025 void
1026 uicb_client_kill(int screen __attribute__ ((unused)), char *arg __attribute__ ((unused)))
1028 Client *sel = globalconf.focus->client;
1030 if(sel)
1031 client_kill(sel);
1034 static void
1035 client_maximize(Client *c, area_t geometry)
1038 if((c->ismax = !c->ismax))
1040 c->wasfloating = c->isfloating;
1041 c->m_geometry = c->geometry;
1042 if(layout_get_current(c->screen)->arrange != layout_floating)
1043 client_setfloating(c, True);
1044 client_focus(c, c->screen, True);
1045 client_resize(c, geometry);
1046 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
1048 else if(c->wasfloating)
1050 client_setfloating(c, True);
1051 client_resize(c, c->m_geometry);
1052 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
1054 else if(layout_get_current(c->screen)->arrange == layout_floating)
1056 client_resize(c, c->m_geometry);
1057 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
1059 else
1061 client_setfloating(c, False);
1062 widget_invalidate_cache(c->screen, WIDGET_CACHE_CLIENTS);
1066 /** Toggle maximize for client
1067 * \param screen Screen ID
1068 * \param arg Unused
1069 * \ingroup ui_callback
1071 void
1072 uicb_client_togglemax(int screen, char *arg __attribute__ ((unused)))
1074 Client *sel = globalconf.focus->client;
1075 area_t area = screen_get_area(screen,
1076 globalconf.screens[screen].statusbar,
1077 &globalconf.screens[screen].padding);
1079 if(sel)
1081 area.width -= 2 * sel->border;
1082 area.height -= 2 * sel->border;
1083 client_maximize(sel, area);
1087 /** Toggle vertical maximize for client
1088 * \param screen Screen ID
1089 * \param arg Unused
1090 * \ingroup ui_callback
1092 void
1093 uicb_client_toggleverticalmax(int screen, char *arg __attribute__ ((unused)))
1095 Client *sel = globalconf.focus->client;
1096 area_t area = screen_get_area(screen,
1097 globalconf.screens[screen].statusbar,
1098 &globalconf.screens[screen].padding);
1100 if(sel)
1102 area.x = sel->geometry.x;
1103 area.width = sel->geometry.width;
1104 area.height -= 2 * sel->border;
1105 client_maximize(sel, area);
1110 /** Toggle horizontal maximize for client
1111 * \param screen Screen ID
1112 * \param arg Unused
1113 * \ingroup ui_callback
1115 void
1116 uicb_client_togglehorizontalmax(int screen, char *arg __attribute__ ((unused)))
1118 Client *sel = globalconf.focus->client;
1119 area_t area = screen_get_area(screen,
1120 globalconf.screens[screen].statusbar,
1121 &globalconf.screens[screen].padding);
1123 if(sel)
1125 area.y = sel->geometry.y;
1126 area.height = sel->geometry.height;
1127 area.width -= 2 * sel->border;
1128 client_maximize(sel, area);
1132 /** Zoom client
1133 * \param screen Screen ID
1134 * \param arg Unused
1135 * \ingroup ui_callback
1137 void
1138 uicb_client_zoom(int screen, char *arg __attribute__ ((unused)))
1140 Client *c, *sel = globalconf.focus->client;
1142 if(!sel)
1143 return;
1145 for(c = globalconf.clients; !client_isvisible(c, screen); c = c->next);
1146 if(c == sel)
1147 for(sel = sel->next; sel && !client_isvisible(sel, screen); sel = sel->next);
1149 if(sel)
1151 client_list_detach(&globalconf.clients, sel);
1152 client_list_push(&globalconf.clients, sel);
1153 globalconf.screens[screen].need_arrange = True;
1157 /** Send focus to next client in stack
1158 * \param screen Screen ID
1159 * \param arg Unused
1160 * \ingroup ui_callback
1162 void
1163 uicb_client_focusnext(int screen, char *arg __attribute__ ((unused)))
1165 Client *next;
1167 if((next = client_find_visible(globalconf.focus->client, False)))
1168 client_focus(next, screen, True);
1171 /** Send focus to previous client in stack
1172 * \param screen Screen ID
1173 * \param arg Unused
1174 * \ingroup ui_callback
1176 void
1177 uicb_client_focusprev(int screen, char *arg __attribute__ ((unused)))
1179 Client *prev;
1181 if((prev = client_find_visible(globalconf.focus->client, True)))
1182 client_focus(prev, screen, True);
1185 /** Toggle floating state of a client
1186 * \param screen Screen ID
1187 * \param arg unused
1188 * \ingroup ui_callback
1190 void
1191 uicb_client_togglefloating(int screen __attribute__ ((unused)),
1192 char *arg __attribute__ ((unused)))
1194 if(globalconf.focus->client)
1195 client_setfloating(globalconf.focus->client, !globalconf.focus->client->isfloating);
1198 /** Toggle scratch client attribute
1199 * \param screen screen number
1200 * \param arg unused argument
1201 * \ingroup ui_callback
1203 void
1204 uicb_client_setscratch(int screen,
1205 char *arg __attribute__ ((unused)))
1207 if(!globalconf.focus->client)
1208 return;
1210 if(globalconf.scratch.client == globalconf.focus->client)
1211 globalconf.scratch.client = NULL;
1212 else
1213 globalconf.scratch.client = globalconf.focus->client;
1215 widget_invalidate_cache(screen, WIDGET_CACHE_CLIENTS | WIDGET_CACHE_TAGS);
1216 globalconf.screens[screen].need_arrange = True;
1219 /** Toggle scratch client visibility
1220 * \param screen screen number
1221 * \param arg unused argument
1222 * \ingroup ui_callback
1224 void
1225 uicb_client_togglescratch(int screen,
1226 char *arg __attribute__ ((unused)))
1228 if(globalconf.scratch.client)
1230 globalconf.scratch.isvisible = !globalconf.scratch.isvisible;
1231 if(globalconf.scratch.isvisible)
1232 client_focus(globalconf.scratch.client, screen, True);
1233 globalconf.screens[globalconf.scratch.client->screen].need_arrange = True;
1234 widget_invalidate_cache(globalconf.scratch.client->screen, WIDGET_CACHE_CLIENTS);
1237 // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80