810 lines
18 KiB
C
810 lines
18 KiB
C
/*
|
|
* client.c
|
|
* Copyright © 2008 Martin Duquesnoy <xorg62@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of the nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "wmfs.h"
|
|
|
|
/** Attach client in the client chain
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_attach(Client *c)
|
|
{
|
|
if(clients)
|
|
clients->prev = c;
|
|
c->next = clients;
|
|
clients = c;
|
|
|
|
return;
|
|
}
|
|
|
|
/** Send a ConfigureRequest event to the Client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_configure(Client *c)
|
|
{
|
|
XConfigureEvent ev;
|
|
|
|
ev.type = ConfigureNotify;
|
|
ev.event = c->win;
|
|
ev.window = c->win;
|
|
ev.x = c->geo.x;
|
|
ev.y = c->geo.y;
|
|
ev.width = c->geo.width;
|
|
ev.height = c->geo.height;
|
|
ev.above = None;
|
|
ev.border_width = 0;
|
|
ev.override_redirect = 0;
|
|
|
|
XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ev);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Detach a client to the client chain
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_detach(Client *c)
|
|
{
|
|
Client **cc;
|
|
|
|
for(cc = &clients; *cc && *cc != c; cc = &(*cc)->next);
|
|
*cc = c->next;
|
|
|
|
return;
|
|
}
|
|
|
|
/** Switch to the previous client
|
|
* \param cmd uicb_t type unused
|
|
*/
|
|
void
|
|
uicb_client_prev(uicb_t cmd)
|
|
{
|
|
Client *c = NULL, *d;
|
|
|
|
if(!sel || ishide(sel))
|
|
return;
|
|
|
|
for(d = clients; d != sel; d = d->next)
|
|
if(!ishide(d))
|
|
c = d;
|
|
if(!c)
|
|
for(; d; d = d->next)
|
|
if(!ishide(d))
|
|
c = d;
|
|
if(c)
|
|
{
|
|
client_focus(c);
|
|
client_raise(c);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/** Switch to the next client
|
|
* \param cmd uicb_t type unused
|
|
*/
|
|
void
|
|
uicb_client_next(uicb_t cmd)
|
|
{
|
|
Client *c = NULL;
|
|
|
|
if(!sel || ishide(sel))
|
|
return;
|
|
|
|
for(c = sel->next; c && ishide(c); c = c->next);
|
|
if(!c)
|
|
for(c = clients; c && ishide(c); c = c->next);
|
|
if(c)
|
|
{
|
|
client_focus(c);
|
|
client_raise(c);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/** Set the focus to a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_focus(Client *c)
|
|
{
|
|
if(sel && sel != c)
|
|
{
|
|
sel->colors.frame = conf.client.bordernormal;
|
|
sel->colors.fg = conf.titlebar.fg_normal;
|
|
sel->colors.resizecorner = conf.client.resizecorner_normal;
|
|
frame_update(sel);
|
|
mouse_grabbuttons(sel, False);
|
|
}
|
|
|
|
sel = c;
|
|
|
|
if(c)
|
|
{
|
|
c->colors.frame = conf.client.borderfocus;
|
|
c->colors.fg = conf.titlebar.fg_focus;
|
|
c->colors.resizecorner = conf.client.resizecorner_focus;
|
|
frame_update(c);
|
|
mouse_grabbuttons(c, True);
|
|
if(conf.raisefocus)
|
|
client_raise(c);
|
|
XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
|
|
}
|
|
else
|
|
XSetInputFocus(dpy, ROOT, RevertToPointerRoot, CurrentTime);
|
|
|
|
return;
|
|
}
|
|
|
|
/* The following function have the same point :
|
|
* find a client member with a Window {{{
|
|
*/
|
|
|
|
/* Get Client with a window */
|
|
/** Get a client->win with a window
|
|
* \param w Window
|
|
* \return The client
|
|
*/
|
|
Client* client_gb_win(Window w)
|
|
{
|
|
Client *c;
|
|
|
|
for(c = clients; c && c->win != w; c = c->next);
|
|
|
|
return c;
|
|
}
|
|
|
|
/** Get a client->frame with a window
|
|
* \param w Window
|
|
* \return The client
|
|
*/
|
|
Client* client_gb_frame(Window w)
|
|
{
|
|
Client *c;
|
|
|
|
for(c = clients; c && c->frame != w; c = c->next);
|
|
|
|
return c;
|
|
}
|
|
|
|
/** Get a client->titlebar with a window
|
|
* \param w Window
|
|
* \return The client
|
|
*/
|
|
Client* client_gb_titlebar(Window w)
|
|
{
|
|
Client *c;
|
|
|
|
if(!TBARH)
|
|
return NULL;
|
|
|
|
for(c = clients; c && c->titlebar->win != w; c = c->next);
|
|
|
|
return c;
|
|
}
|
|
|
|
/** Get a client->resize with a window
|
|
* \param w Window
|
|
* \return The client
|
|
*/
|
|
Client* client_gb_resize(Window w)
|
|
{
|
|
Client *c;
|
|
|
|
for(c = clients; c && c->resize != w; c = c->next);
|
|
|
|
return c;
|
|
}
|
|
|
|
/** Get a client->button[button_num] with a window
|
|
* \param w Window
|
|
* \param n Pointer who return the button_num
|
|
* \return The client
|
|
*/
|
|
Client* client_gb_button(Window w, int *n)
|
|
{
|
|
Client *c;
|
|
int i;
|
|
|
|
for(c = clients; c; c = c->next)
|
|
for(i = 0; i < conf.titlebar.nbutton; ++i)
|
|
if(c->button[i] == w)
|
|
{
|
|
*n = i;
|
|
return c;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/** Get a client name
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_get_name(Client *c)
|
|
{
|
|
Atom rt;
|
|
int rf;
|
|
ulong ir, il;
|
|
|
|
/* This one instead XFetchName for utf8 name support */
|
|
if(XGetWindowProperty(dpy, c->win, net_atom[net_wm_name], 0, 4096,
|
|
False, net_atom[utf8_string], &rt, &rf, &ir, &il, (uchar**)&c->title) != Success)
|
|
XGetWindowProperty(dpy, c->win, ATOM("WM_NAME"), 0, 4096,
|
|
False, net_atom[utf8_string], &rt, &rf, &ir, &il, (uchar**)&c->title);
|
|
|
|
/* If there is no title... */
|
|
if(!c->title)
|
|
{
|
|
XFetchName(dpy, c->win, &(c->title));
|
|
if(!c->title)
|
|
c->title = _strdup("WMFS");
|
|
}
|
|
|
|
frame_update(c);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Hide a client (for tag switching)
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_hide(Client *c)
|
|
{
|
|
CHECK(!c->hide);
|
|
|
|
client_unmap(c);
|
|
c->hide = True;
|
|
setwinstate(c->win, IconicState);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Check if the client 'c' is hide
|
|
* \param c Client pointer
|
|
* \return True if the client is hide; False if not
|
|
*/
|
|
Bool
|
|
ishide(Client *c)
|
|
{
|
|
if(c->tag && c->tag == seltag[screen_get_sel()])
|
|
return False;
|
|
return True;
|
|
}
|
|
|
|
/** Kill a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_kill(Client *c)
|
|
{
|
|
XEvent ev;
|
|
Atom *atom = NULL;
|
|
int proto;
|
|
int canbedel = 0;
|
|
|
|
CHECK(c);
|
|
|
|
if(XGetWMProtocols(dpy, c->win, &atom, &proto) && atom)
|
|
{
|
|
while(proto--)
|
|
if(atom[proto] == ATOM("WM_DELETE_WINDOW"))
|
|
++canbedel;
|
|
XFree(atom);
|
|
|
|
if(canbedel)
|
|
{
|
|
ev.type = ClientMessage;
|
|
ev.xclient.window = c->win;
|
|
ev.xclient.message_type = ATOM("WM_PROTOCOLS");
|
|
ev.xclient.format = 32;
|
|
ev.xclient.data.l[0] = ATOM("WM_DELETE_WINDOW");
|
|
ev.xclient.data.l[1] = CurrentTime;
|
|
ev.xclient.data.l[2] = 0;
|
|
ev.xclient.data.l[3] = 0;
|
|
ev.xclient.data.l[4] = 0;
|
|
XSendEvent(dpy, c->win, False, NoEventMask, &ev);
|
|
}
|
|
else
|
|
XKillClient(dpy, c->win);
|
|
}
|
|
else
|
|
XKillClient(dpy, c->win);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Kill the selected client
|
|
* \param cmd uicb_t type unused
|
|
*/
|
|
void
|
|
uicb_client_kill(uicb_t cmd)
|
|
{
|
|
CHECK(sel);
|
|
|
|
client_kill(sel);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Map a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_map(Client *c)
|
|
{
|
|
CHECK(c);
|
|
|
|
XMapWindow(dpy, c->frame);
|
|
XMapSubwindows(dpy, c->frame);
|
|
if(TBARH)
|
|
{
|
|
barwin_map(c->titlebar);
|
|
barwin_map_subwin(c->titlebar);
|
|
}
|
|
XMapSubwindows(dpy, c->frame);
|
|
c->unmapped = False;
|
|
|
|
return;
|
|
}
|
|
|
|
/** Manage a client with a window and his attributes
|
|
* \param w Cient's futur Window
|
|
* \param wa XWindowAttributes pointer, Window w attributes
|
|
*/
|
|
void
|
|
client_manage(Window w, XWindowAttributes *wa)
|
|
{
|
|
Client *c, *t = NULL;
|
|
Window trans;
|
|
Status rettrans;
|
|
XSetWindowAttributes at;
|
|
int mx, my;
|
|
|
|
screen_get_sel();
|
|
|
|
c = emalloc(1, sizeof(Client));
|
|
c->win = w;
|
|
c->screen = selscreen;
|
|
|
|
if(conf.client.place_at_mouse)
|
|
{
|
|
int dint;
|
|
uint duint;
|
|
Window dw;
|
|
|
|
XQueryPointer(dpy, ROOT, &dw, &dw, &mx, &my, &dint, &dint, &duint);
|
|
|
|
mx += BORDH;
|
|
my += TBARH;
|
|
|
|
if((MAXW - mx) < wa->width)
|
|
mx -= wa->width + BORDH;
|
|
|
|
if((MAXH - my) < wa->height)
|
|
my -= wa->height + TBARH;
|
|
}
|
|
else
|
|
{
|
|
mx = wa->x + BORDH;
|
|
my = wa->y + TBARH + INFOBARH;
|
|
|
|
/* Check if the client is already in the selected
|
|
* screen, else place the client in it */
|
|
if(screen_get_with_geo(mx, my) != selscreen)
|
|
{
|
|
mx += sgeo[selscreen].x - BORDH;
|
|
my += sgeo[selscreen].y - TBARH - INFOBARH;
|
|
}
|
|
}
|
|
c->ogeo.x = c->geo.x = mx;
|
|
c->ogeo.y = c->geo.y = my;
|
|
c->ogeo.width = c->geo.width = wa->width;
|
|
c->ogeo.height = c->geo.height = wa->height;
|
|
|
|
c->tag = seltag[c->screen];
|
|
at.event_mask = PropertyChangeMask;
|
|
|
|
frame_create(c);
|
|
client_size_hints(c);
|
|
XChangeWindowAttributes(dpy, c->win, CWEventMask, &at);
|
|
XSetWindowBorderWidth(dpy, c->win, 0); /* client win sould _not_ have borders */
|
|
mouse_grabbuttons(c, False);
|
|
if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success))
|
|
for(t = clients; t && t->win != trans; t = t->next);
|
|
if(t) c->tag = t->tag;
|
|
if(!c->free) c->free = (rettrans == Success) || c->hint;
|
|
free(t);
|
|
|
|
client_attach(c);
|
|
client_map(c);
|
|
client_get_name(c);
|
|
client_raise(c);
|
|
client_focus(c);
|
|
setwinstate(c->win, NormalState);
|
|
ewmh_get_client_list();
|
|
ewmh_manage_window_type(c);
|
|
arrange(c->screen);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Move and Resize a client
|
|
* \param c Client pointer
|
|
* \param geo Coordinate info for the future size
|
|
* of the client
|
|
* \param r Bool for resize hint or not
|
|
*/
|
|
void
|
|
client_moveresize(Client *c, XRectangle geo, Bool r)
|
|
{
|
|
if(!c || c->state_dock)
|
|
return;
|
|
|
|
/* Resize hints {{{ */
|
|
if(r)
|
|
{
|
|
/* minimum possible */
|
|
if(geo.width < 1)
|
|
geo.width = 1;
|
|
if(geo.height < 1)
|
|
geo.height = 1;
|
|
|
|
/* base */
|
|
geo.width -= c->basew;
|
|
geo.height -= c->baseh;
|
|
|
|
/* aspect */
|
|
if(c->minay > 0 && c->maxay > 0
|
|
&& c->minax > 0 && c->maxax > 0)
|
|
{
|
|
if(geo.width * c->maxay > geo.height * c->maxax)
|
|
geo.width = geo.height * c->maxax / c->maxay;
|
|
else if(geo.width * c->minay < geo.height * c->minax)
|
|
geo.height = geo.width * c->minay / c->minax;
|
|
}
|
|
|
|
/* incremental */
|
|
if(c->incw)
|
|
geo.width -= geo.width % c->incw;
|
|
if(c->inch)
|
|
geo.height -= geo.height % c->inch;
|
|
|
|
/* base dimension */
|
|
geo.width += c->basew;
|
|
geo.height += c->baseh;
|
|
|
|
if(c->minw > 0 && geo.width < c->minw)
|
|
geo.width = c->minw;
|
|
if(c->minh > 0 && geo.height < c->minh)
|
|
geo.height = c->minh;
|
|
if(c->maxw > 0 && geo.width > c->maxw)
|
|
geo.width = c->maxw;
|
|
if(c->maxh > 0 && geo.height > c->maxh)
|
|
geo.height = c->maxh;
|
|
if(geo.width <= 0 || geo.height <= 0)
|
|
return;
|
|
}
|
|
/* }}} */
|
|
|
|
c->max = False;
|
|
|
|
if(tags[selscreen][seltag[selscreen]].layout.func == freelayout
|
|
|| c->free);
|
|
c->geo = c->ogeo = geo;
|
|
|
|
c->screen = screen_get_with_geo(c->geo.x, c->geo.y);
|
|
c->tag = seltag[c->screen];
|
|
|
|
frame_moveresize(c, c->geo);
|
|
|
|
XMoveResizeWindow(dpy, c->win, BORDH, BORDH + TBARH, c->geo.width, c->geo.height);
|
|
|
|
client_configure(c);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Maximize a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_maximize(Client *c)
|
|
{
|
|
XRectangle geo;
|
|
|
|
CHECK(c);
|
|
|
|
client_focus(c);
|
|
|
|
c->screen = screen_get_with_geo(c->geo.x, c->geo.y);
|
|
|
|
geo.x = sgeo[c->screen].x;
|
|
geo.y = sgeo[c->screen].y;
|
|
geo.width = sgeo[c->screen].width - BORDH * 2;
|
|
geo.height = sgeo[c->screen].height - BORDH * 2;
|
|
|
|
|
|
if(c->state_fullscreen)
|
|
{
|
|
geo.y -= INFOBARH;
|
|
geo.height += INFOBARH;
|
|
}
|
|
|
|
client_moveresize(c, geo, False);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Get client size hints
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_size_hints(Client *c)
|
|
{
|
|
long msize;
|
|
XSizeHints size;
|
|
|
|
if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
|
|
size.flags = PSize;
|
|
|
|
/* base */
|
|
if(size.flags & PBaseSize)
|
|
{
|
|
c->basew = size.base_width;
|
|
c->baseh = size.base_height;
|
|
}
|
|
else if(size.flags & PMinSize)
|
|
{
|
|
c->basew = size.min_width;
|
|
c->baseh = size.min_height;
|
|
}
|
|
else
|
|
c->basew = c->baseh = 0;
|
|
|
|
/* inc */
|
|
if(size.flags & PResizeInc)
|
|
{
|
|
c->incw = size.width_inc;
|
|
c->inch = size.height_inc;
|
|
}
|
|
else
|
|
c->incw = c->inch = 0;
|
|
|
|
/* max */
|
|
if(size.flags & PMaxSize)
|
|
{
|
|
c->maxw = size.max_width;
|
|
c->maxh = size.max_height;
|
|
}
|
|
else
|
|
c->maxw = c->maxh = 0;
|
|
|
|
/* min */
|
|
if(size.flags & PMinSize)
|
|
{
|
|
c->minw = size.min_width;
|
|
c->minh = size.min_height;
|
|
}
|
|
else if(size.flags & PBaseSize)
|
|
{
|
|
c->minw = size.base_width;
|
|
c->minh = size.base_height;
|
|
}
|
|
else
|
|
c->minw = c->minh = 0;
|
|
|
|
/* aspect */
|
|
if(size.flags & PAspect)
|
|
{
|
|
c->minax = size.min_aspect.x;
|
|
c->maxax = size.max_aspect.x;
|
|
c->minay = size.min_aspect.y;
|
|
c->maxay = size.max_aspect.y;
|
|
}
|
|
else
|
|
c->minax = c->maxax = c->minay = c->maxay = 0;
|
|
|
|
c->hint = (c->maxw && c->minw && c->maxh && c->minh
|
|
&& c->maxw == c->minw && c->maxh == c->minh);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Swap 2 clients
|
|
* \param a First client
|
|
* \param b Second client
|
|
*/
|
|
void
|
|
client_swap(Client *a, Client *b)
|
|
{
|
|
Client *an, *bn, *ap, *bp;
|
|
int ts, tt;
|
|
XRectangle tgeo;
|
|
|
|
if(!a || !b || !a->tile || !b->tile)
|
|
return;
|
|
|
|
/* Set temp variable */
|
|
ap = a->prev;
|
|
an = a->next;
|
|
bp = b->prev;
|
|
bn = b->next;
|
|
ts = a->screen;
|
|
tt = a->tag;
|
|
tgeo = a->geo;
|
|
|
|
/* Swap client in the double linked list */
|
|
if(a == b->next)
|
|
{
|
|
a->next = b;
|
|
b->prev = a;
|
|
|
|
if((a->prev = bp))
|
|
bp->next = a;
|
|
if((b->next = an))
|
|
an->prev = b;
|
|
}
|
|
else if(b == a->next)
|
|
{
|
|
a->prev = b;
|
|
b->next = a;
|
|
|
|
if((b->prev = ap))
|
|
ap->next = b;
|
|
if((a->next = bn))
|
|
bn->prev = a;
|
|
}
|
|
else
|
|
{
|
|
if((a->next = bn))
|
|
a->next->prev = a;
|
|
if((a->prev = bp))
|
|
a->prev->next = a;
|
|
if((b->prev = ap))
|
|
b->prev->next = b;
|
|
if((b->next = an))
|
|
b->next->prev = b;
|
|
}
|
|
|
|
if(clients == a)
|
|
clients = b;
|
|
else if(clients == b)
|
|
clients = a;
|
|
|
|
/* Swap tag/screen property */
|
|
a->screen = b->screen;
|
|
b->screen = ts;
|
|
a->tag = b->tag;
|
|
b->tag = tt;
|
|
|
|
/* Swap position/size an move them */
|
|
client_moveresize(a, b->geo, False);
|
|
client_moveresize(b, tgeo, False);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Raise a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_raise(Client *c)
|
|
{
|
|
if(!c || c->tile)
|
|
return;
|
|
|
|
XRaiseWindow(dpy, c->frame);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Raise the selected client
|
|
* \param cmd uicb_t type unused
|
|
*/
|
|
void
|
|
uicb_client_raise(uicb_t cmd)
|
|
{
|
|
client_raise(sel);
|
|
|
|
return;
|
|
}
|
|
|
|
/** UnHide a client (for tag switching)
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_unhide(Client *c)
|
|
{
|
|
CHECK(c->hide);
|
|
|
|
client_map(c);
|
|
c->hide = False;
|
|
setwinstate(c->win, NormalState);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Unmanage a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_unmanage(Client *c)
|
|
{
|
|
XGrabServer(dpy);
|
|
XSetErrorHandler(errorhandlerdummy);
|
|
XReparentWindow(dpy, c->win, ROOT, c->geo.x, c->geo.y);
|
|
|
|
if(sel == c)
|
|
client_focus(NULL);
|
|
|
|
client_detach(c);
|
|
XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
|
|
setwinstate(c->win, WithdrawnState);
|
|
frame_delete(c);
|
|
XSync(dpy, False);
|
|
XUngrabServer(dpy);
|
|
ewmh_get_client_list();
|
|
arrange(c->screen);
|
|
XFree(c->title);
|
|
free(c);
|
|
|
|
return;
|
|
}
|
|
|
|
/** Unmap a client
|
|
* \param c Client pointer
|
|
*/
|
|
void
|
|
client_unmap(Client *c)
|
|
{
|
|
CHECK(c);
|
|
|
|
if(TBARH)
|
|
{
|
|
barwin_unmap_subwin(c->titlebar);
|
|
barwin_unmap(c->titlebar);
|
|
}
|
|
|
|
XUnmapWindow(dpy, c->frame);
|
|
XUnmapSubwindows(dpy, c->frame);
|
|
c->unmapped = True;
|
|
|
|
return;
|
|
}
|