1959 lines
44 KiB
C
1959 lines
44 KiB
C
/*
|
|
* Copyright (C) 2000-2005 Carsten Haitzler, Geoff Harrison and various contributors
|
|
* Copyright (C) 2004-2005 Kim Woelders
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies of the Software, its documentation and marketing & publicity
|
|
* materials, and acknowledgment shall be given in the documentation, materials
|
|
* and software packages that this Software was used.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#include "E.h"
|
|
#include "desktops.h"
|
|
#include "ecompmgr.h"
|
|
#include "emodule.h"
|
|
#include "ewins.h"
|
|
#include "icons.h"
|
|
#include "snaps.h"
|
|
#include "xwin.h"
|
|
#include <sys/time.h>
|
|
|
|
#define EWIN_TOP_EVENT_MASK \
|
|
(ButtonPressMask | ButtonReleaseMask | \
|
|
EnterWindowMask | LeaveWindowMask | PointerMotionMask /* | \
|
|
StructureNotifyMask */)
|
|
|
|
#define EWIN_CONTAINER_EVENT_MASK \
|
|
(/* ButtonPressMask | ButtonReleaseMask | */ \
|
|
/* StructureNotifyMask | ResizeRedirectMask | */ \
|
|
SubstructureNotifyMask | SubstructureRedirectMask)
|
|
|
|
#define EWIN_CLIENT_EVENT_MASK \
|
|
(EnterWindowMask | LeaveWindowMask | FocusChangeMask | \
|
|
/* StructureNotifyMask | */ ResizeRedirectMask | \
|
|
PropertyChangeMask | ColormapChangeMask | VisibilityChangeMask)
|
|
|
|
static void EwinSlideIn(int val __UNUSED__, void *data);
|
|
|
|
static void EwinChangesStart(EWin * ewin);
|
|
static void EwinChangesProcess(EWin * ewin);
|
|
|
|
static void EwinHandleEventsToplevel(XEvent * ev, void *prm);
|
|
static void EwinHandleEventsContainer(XEvent * ev, void *prm);
|
|
static void EwinHandleEventsClient(XEvent * ev, void *prm);
|
|
|
|
static void
|
|
EwinEventsConfigure(EWin * ewin, int mode)
|
|
{
|
|
long emask;
|
|
|
|
emask = (mode) ? ~((long)0) : ~(EnterWindowMask | LeaveWindowMask);
|
|
|
|
ESelectInput(EoGetWin(ewin), EWIN_TOP_EVENT_MASK & emask);
|
|
ESelectInput(_EwinGetClientWin(ewin), ewin->client.event_mask & emask);
|
|
EwinBorderEventsConfigure(ewin, mode);
|
|
}
|
|
|
|
static EWin *
|
|
EwinCreate(Window win, int type)
|
|
{
|
|
EWin *ewin;
|
|
XSetWindowAttributes att;
|
|
Window frame;
|
|
int use_argb;
|
|
XWindowAttributes win_attr;
|
|
|
|
use_argb = 0;
|
|
if (type == EWIN_TYPE_NORMAL)
|
|
{
|
|
if (!XGetWindowAttributes(disp, win, &win_attr))
|
|
return NULL;
|
|
use_argb = Conf.argb_client_mode > 0 && EVisualIsARGB(win_attr.visual);
|
|
}
|
|
|
|
ewin = Ecalloc(1, sizeof(EWin));
|
|
|
|
ewin->type = type;
|
|
ewin->state.state = (Mode.wm.startup) ? EWIN_STATE_STARTUP : EWIN_STATE_NEW;
|
|
ewin->update.shape = 1;
|
|
ewin->update.border = 1;
|
|
ewin->lx = -1;
|
|
ewin->ly = -1;
|
|
ewin->lw = -1;
|
|
ewin->lh = -1;
|
|
ewin->client.x = -1;
|
|
ewin->client.y = -1;
|
|
ewin->client.w = -1;
|
|
ewin->client.h = -1;
|
|
ewin->icccm.need_input = 1;
|
|
ewin->client.aspect_min = 0.0;
|
|
ewin->client.aspect_max = 65535.0;
|
|
ewin->client.w_inc = 1;
|
|
ewin->client.h_inc = 1;
|
|
ewin->client.width.max = 65535;
|
|
ewin->client.height.max = 65535;
|
|
#if 0 /* ENABLE_GNOME - Not actually used */
|
|
ewin->expanded_width = -1;
|
|
ewin->expanded_height = -1;
|
|
#endif
|
|
ewin->area_x = -1;
|
|
ewin->area_y = -1;
|
|
|
|
ewin->ewmh.opacity = 0; /* If 0, ignore */
|
|
|
|
if (use_argb)
|
|
frame = ECreateVisualWindow(VRoot.win, -10, -10, 1, 1, 1, &win_attr);
|
|
else
|
|
frame = None;
|
|
|
|
ewin->o.stacked = -1; /* Not placed on desk yet */
|
|
EoSetDesk(ewin, DesksGetCurrent());
|
|
EobjInit(EoObj(ewin), EOBJ_TYPE_EWIN, frame, -10, -10, -1, -1, 1, NULL);
|
|
EoSetLayer(ewin, 4);
|
|
EoSetShadow(ewin, 1);
|
|
EobjListFocusAdd(&ewin->o, 0);
|
|
EobjListOrderAdd(&ewin->o);
|
|
|
|
if (use_argb)
|
|
ewin->win_container =
|
|
ECreateVisualWindow(EoGetWin(ewin), 0, 0, 1, 1, 0, &win_attr);
|
|
else
|
|
ewin->win_container = ECreateWindow(EoGetWin(ewin), 0, 0, 1, 1, 0);
|
|
att.event_mask = EWIN_CONTAINER_EVENT_MASK;
|
|
att.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
|
|
EChangeWindowAttributes(ewin->win_container,
|
|
CWEventMask | CWDontPropagate, &att);
|
|
EMapWindow(ewin->win_container);
|
|
|
|
att.event_mask = EWIN_TOP_EVENT_MASK;
|
|
att.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
|
|
EChangeWindowAttributes(EoGetWin(ewin), CWEventMask | CWDontPropagate, &att);
|
|
|
|
ewin->client.win = win;
|
|
FocusEwinSetGrabs(ewin);
|
|
|
|
ewin->client.event_mask = EWIN_CLIENT_EVENT_MASK;
|
|
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinCreate %#lx frame=%#lx cont=%#lx st=%d\n",
|
|
_EwinGetClientXwin(ewin), EoGetWin(ewin),
|
|
_EwinGetContainerXwin(ewin), ewin->state.state);
|
|
|
|
EventCallbackRegister(EoGetWin(ewin), 0, EwinHandleEventsToplevel, ewin);
|
|
EventCallbackRegister(ewin->win_container, 0, EwinHandleEventsContainer,
|
|
ewin);
|
|
ERegisterWindow(_EwinGetClientXwin(ewin));
|
|
EventCallbackRegister(_EwinGetClientWin(ewin), 0, EwinHandleEventsClient,
|
|
ewin);
|
|
|
|
if (!EwinIsInternal(ewin))
|
|
{
|
|
XShapeSelectInput(disp, win, ShapeNotifyMask);
|
|
ESetWindowBorderWidth(win, 0);
|
|
ewin->client.bw = 0;
|
|
}
|
|
|
|
if (use_argb && Conf.argb_client_mode < 2)
|
|
ewin->border =
|
|
FindItem("BORDERLESS", 0, LIST_FINDBY_NAME, LIST_TYPE_BORDER);
|
|
|
|
ModulesSignal(ESIGNAL_EWIN_CREATE, ewin);
|
|
|
|
return ewin;
|
|
}
|
|
|
|
static void
|
|
EwinCleanup(EWin * ewin)
|
|
{
|
|
EwinBorderDetach(ewin);
|
|
}
|
|
|
|
static void
|
|
EwinDestroy(EWin * ewin)
|
|
{
|
|
EWin **lst;
|
|
int i, num;
|
|
|
|
if (!ewin)
|
|
return;
|
|
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinDestroy %#lx st=%d: %s\n", _EwinGetClientXwin(ewin),
|
|
ewin->state.state, EwinGetName(ewin));
|
|
|
|
EventCallbackUnregister(EoGetWin(ewin), 0, EwinHandleEventsToplevel, ewin);
|
|
EventCallbackUnregister(ewin->win_container, 0, EwinHandleEventsContainer,
|
|
ewin);
|
|
EventCallbackUnregister(_EwinGetClientWin(ewin), 0, EwinHandleEventsClient,
|
|
ewin);
|
|
if (!EwinIsInternal(ewin))
|
|
EUnregisterWindow(_EwinGetClientWin(ewin));
|
|
|
|
SnapshotEwinUnmatch(ewin);
|
|
|
|
ModulesSignal(ESIGNAL_EWIN_DESTROY, ewin);
|
|
|
|
lst = EwinListTransientFor(ewin, &num);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
lst[i]->icccm.transient_count--;
|
|
if (lst[i]->icccm.transient_count < 0) /* Paranoia? */
|
|
lst[i]->icccm.transient_count = 0;
|
|
}
|
|
if (lst)
|
|
Efree(lst);
|
|
|
|
EwinCleanup(ewin);
|
|
EobjListOrderDel(&ewin->o);
|
|
EobjListFocusDel(&ewin->o);
|
|
EobjFini(&ewin->o);
|
|
EDestroyWindow(EoGetWin(ewin));
|
|
|
|
HintsSetClientList();
|
|
|
|
if (ewin->icccm.wm_name)
|
|
Efree(ewin->icccm.wm_name);
|
|
if (ewin->icccm.wm_icon_name)
|
|
Efree(ewin->icccm.wm_icon_name);
|
|
if (ewin->icccm.wm_res_class)
|
|
Efree(ewin->icccm.wm_res_class);
|
|
if (ewin->icccm.wm_res_name)
|
|
Efree(ewin->icccm.wm_res_name);
|
|
if (ewin->icccm.wm_role)
|
|
Efree(ewin->icccm.wm_role);
|
|
if (ewin->icccm.wm_command)
|
|
Efree(ewin->icccm.wm_command);
|
|
if (ewin->icccm.wm_machine)
|
|
Efree(ewin->icccm.wm_machine);
|
|
#if ENABLE_EWMH
|
|
if (ewin->ewmh.wm_name)
|
|
Efree(ewin->ewmh.wm_name);
|
|
if (ewin->ewmh.wm_icon_name)
|
|
Efree(ewin->ewmh.wm_icon_name);
|
|
if (ewin->ewmh.wm_icon)
|
|
Efree(ewin->ewmh.wm_icon);
|
|
#endif
|
|
if (ewin->bits)
|
|
Efree(ewin->bits);
|
|
if (ewin->session_id)
|
|
Efree(ewin->session_id);
|
|
FreePmapMask(&ewin->mini_pmm);
|
|
GroupsEwinRemove(ewin);
|
|
Efree(ewin);
|
|
}
|
|
|
|
void
|
|
DetermineEwinFloat(EWin * ewin, int dx, int dy)
|
|
{
|
|
char dofloat = 0;
|
|
int x, y, w, h, xd, yd;
|
|
Desk *dsk;
|
|
|
|
dsk = EoGetDesk(ewin);
|
|
x = EoGetX(ewin);
|
|
y = EoGetY(ewin);
|
|
w = EoGetW(ewin);
|
|
h = EoGetH(ewin);
|
|
|
|
xd = EoGetX(dsk);
|
|
yd = EoGetY(dsk);
|
|
|
|
if ((dsk->num != 0) && (EoIsFloating(ewin) < 2) &&
|
|
((xd != 0) || (yd != 0) || (DesksGetCurrent() != dsk)))
|
|
{
|
|
switch (Conf.desks.dragdir)
|
|
{
|
|
case 0:
|
|
if (((x + dx < 0) ||
|
|
((x + dx + w <= VRoot.w) &&
|
|
((DesktopAt(xd + x + dx + w - 1, yd) != dsk)))))
|
|
dofloat = 1;
|
|
break;
|
|
case 1:
|
|
if (((x + dx + w > VRoot.w) ||
|
|
((x + dx >= 0) && ((DesktopAt(xd + x + dx, yd) != dsk)))))
|
|
dofloat = 1;
|
|
break;
|
|
case 2:
|
|
if (((y + dy < 0) ||
|
|
((y + dy + h <= VRoot.h) &&
|
|
((DesktopAt(xd, yd + y + dy + h - 1) != dsk)))))
|
|
dofloat = 1;
|
|
break;
|
|
case 3:
|
|
if (((y + dy + h > VRoot.h) ||
|
|
((y + dy >= 0) && ((DesktopAt(xd, yd + y + dy) != dsk)))))
|
|
dofloat = 1;
|
|
break;
|
|
}
|
|
|
|
if (dofloat)
|
|
EwinFloatAt(ewin, x + xd, y + yd);
|
|
}
|
|
}
|
|
|
|
EWin *
|
|
GetEwinByCurrentPointer(void)
|
|
{
|
|
Window child;
|
|
|
|
EQueryPointer(EoGetWin(DesksGetCurrent()), NULL, NULL, &child, NULL);
|
|
|
|
return EwinFindByFrame(child);
|
|
}
|
|
|
|
EWin *
|
|
GetEwinPointerInClient(void)
|
|
{
|
|
int px, py;
|
|
EWin *const *lst, *ewin;
|
|
int i, num;
|
|
Desk *dsk;
|
|
|
|
dsk = DesktopAt(Mode.events.x, Mode.events.y);
|
|
EQueryPointer(EoGetWin(dsk), &px, &py, NULL, NULL);
|
|
|
|
lst = EwinListGetForDesk(&num, dsk);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
int x, y, w, h;
|
|
|
|
ewin = lst[i];
|
|
x = EoGetX(ewin);
|
|
y = EoGetY(ewin);
|
|
w = EoGetW(ewin);
|
|
h = EoGetH(ewin);
|
|
if ((px >= x) && (py >= y) && (px < (x + w)) && (py < (y + h)) &&
|
|
EwinIsMapped(ewin))
|
|
return ewin;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
EWin *
|
|
GetFocusEwin(void)
|
|
{
|
|
return Mode.focuswin;
|
|
}
|
|
|
|
EWin *
|
|
GetContextEwin(void)
|
|
{
|
|
EWin *ewin;
|
|
|
|
#if 0 /* FIXME - Remove? */
|
|
ewin = Mode.mouse_over_ewin;
|
|
if (ewin && ewin->type != EWIN_TYPE_MENU)
|
|
return ewin;
|
|
#endif
|
|
|
|
ewin = Mode.context_ewin;
|
|
if (ewin)
|
|
goto done;
|
|
|
|
#if 0 /* FIXME - Remove? */
|
|
ewin = Mode.focuswin;
|
|
if (ewin && ewin->type != EWIN_TYPE_MENU)
|
|
goto done;
|
|
#endif
|
|
|
|
ewin = NULL;
|
|
|
|
done:
|
|
#if 0
|
|
Eprintf("GetContextEwin %#lx %s\n", _EwinGetClientXwin(ewin),
|
|
EwinGetName(ewin));
|
|
#endif
|
|
return ewin;
|
|
}
|
|
|
|
void
|
|
SetContextEwin(EWin * ewin)
|
|
{
|
|
if (ewin && ewin->type == EWIN_TYPE_MENU)
|
|
return;
|
|
#if 0
|
|
Eprintf("SetContextEwin %#lx %s\n", _EwinGetClientXwin(ewin),
|
|
EwinGetName(ewin));
|
|
#endif
|
|
Mode.context_ewin = ewin;
|
|
}
|
|
|
|
/*
|
|
* Derive frame window position from client window and border properties
|
|
*/
|
|
void
|
|
EwinGetPosition(const EWin * ewin, int x, int y, int bw, int grav,
|
|
int *px, int *py)
|
|
{
|
|
int bd_lr, bd_tb;
|
|
|
|
bd_lr = ewin->border->border.left + ewin->border->border.right;
|
|
bd_tb = ewin->border->border.top + ewin->border->border.bottom;
|
|
|
|
switch (grav)
|
|
{
|
|
case NorthWestGravity:
|
|
case SouthWestGravity:
|
|
case WestGravity:
|
|
x -= bw;
|
|
break;
|
|
case NorthEastGravity:
|
|
case EastGravity:
|
|
case SouthEastGravity:
|
|
x -= bd_lr;
|
|
break;
|
|
case NorthGravity:
|
|
case CenterGravity:
|
|
case SouthGravity:
|
|
x -= bd_lr - bw;
|
|
break;
|
|
case StaticGravity:
|
|
x -= ewin->border->border.left;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (grav)
|
|
{
|
|
case NorthWestGravity:
|
|
case NorthGravity:
|
|
case NorthEastGravity:
|
|
y -= bw;
|
|
break;
|
|
case WestGravity:
|
|
case CenterGravity:
|
|
case EastGravity:
|
|
y -= bd_tb;
|
|
break;
|
|
case SouthWestGravity:
|
|
case SouthGravity:
|
|
case SouthEastGravity:
|
|
y -= bd_tb - bw;
|
|
break;
|
|
case StaticGravity:
|
|
y -= ewin->border->border.top;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
*px = x;
|
|
*py = y;
|
|
}
|
|
|
|
/*
|
|
* Derive frame window geometry from client window properties
|
|
*/
|
|
static void
|
|
EwinGetGeometry(EWin * ewin)
|
|
{
|
|
int x, y, l, r, t, b;
|
|
|
|
EwinGetPosition(ewin, ewin->client.x, ewin->client.y, ewin->client.bw,
|
|
ewin->client.grav, &x, &y);
|
|
|
|
l = ewin->border->border.left;
|
|
r = ewin->border->border.right;
|
|
t = ewin->border->border.top;
|
|
b = ewin->border->border.bottom;
|
|
|
|
ewin->client.x = x + l;
|
|
ewin->client.y = y + t;
|
|
|
|
EoMoveResize(ewin, x, y, ewin->client.w + l + r, ewin->client.h + t + b);
|
|
}
|
|
|
|
void
|
|
EwinUpdateShapeInfo(EWin * ewin)
|
|
{
|
|
EGrabServer();
|
|
ewin->state.shaped =
|
|
EShapeCopy(ewin->win_container, _EwinGetClientWin(ewin));
|
|
EUngrabServer();
|
|
|
|
#if 0 /* Debug */
|
|
Eprintf("EwinUpdateShapeInfo %#lx cont=%#lx shaped=%d\n",
|
|
_EwinGetClientXwin(ewin), ewin->win_container, ewin->client.shaped);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
EwinPropagateShapes(EWin * ewin)
|
|
{
|
|
if (!EoIsShown(ewin))
|
|
return;
|
|
|
|
if (ewin->state.docked)
|
|
return;
|
|
|
|
#if 0
|
|
Eprintf("EwinPropagateShapes %#lx %#lx %s\n", EoGetWin(ewin),
|
|
_EwinGetClientXwin(ewin), EoGetName(ewin));
|
|
#endif
|
|
if (ewin->update.shape)
|
|
{
|
|
EShapePropagate(EoGetWin(ewin));
|
|
EoChangeShape(ewin);
|
|
ewin->update.shape = 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
EwinStateUpdate(EWin * ewin)
|
|
{
|
|
ewin->state.inhibit_actions = ewin->props.no_actions;
|
|
ewin->state.inhibit_focus = !ewin->icccm.need_input ||
|
|
ewin->props.never_focus || ewin->state.iconified;
|
|
|
|
ewin->state.no_border = ewin->props.no_border || ewin->state.docked ||
|
|
(ewin->mwm.valid && !ewin->mwm.decor_title && !ewin->mwm.decor_border);
|
|
|
|
ewin->state.inhibit_move = ewin->props.fixedpos || ewin->state.fullscreen;
|
|
ewin->state.inhibit_resize = ewin->props.fixedsize || ewin->state.shaded ||
|
|
ewin->state.fullscreen;
|
|
ewin->state.inhibit_iconify = ewin->props.never_iconify;
|
|
ewin->state.inhibit_shade = ewin->state.no_border || ewin->state.fullscreen;
|
|
ewin->state.inhibit_stick = 0;
|
|
ewin->state.inhibit_max_hor =
|
|
ewin->client.no_resize_h || ewin->state.fullscreen;
|
|
ewin->state.inhibit_max_ver =
|
|
ewin->client.no_resize_v || ewin->state.fullscreen;
|
|
ewin->state.inhibit_fullscreeen =
|
|
ewin->state.inhibit_move || ewin->state.inhibit_resize;
|
|
ewin->state.inhibit_change_desk = 0;
|
|
ewin->state.inhibit_close = 0;
|
|
}
|
|
|
|
static void
|
|
Adopt(EWin * ewin)
|
|
{
|
|
ICCCM_AdoptStart(ewin);
|
|
ICCCM_GetTitle(ewin, 0);
|
|
ICCCM_GetHints(ewin, 0);
|
|
ICCCM_GetInfo(ewin, 0);
|
|
EwinUpdateShapeInfo(ewin);
|
|
ICCCM_GetGeoms(ewin, 0);
|
|
if (!EwinIsInternal(ewin))
|
|
{
|
|
MWM_GetHints(ewin, 0);
|
|
ICCCM_GetColormap(ewin);
|
|
HintsGetWindowHints(ewin);
|
|
SessionGetInfo(ewin, 0);
|
|
}
|
|
|
|
#if 0 /* Do we want this? */
|
|
if (!EwinIsInternal(ewin))
|
|
MatchEwinToSM(ewin);
|
|
#endif
|
|
WindowMatchEwinOps(ewin); /* Window matches */
|
|
SnapshotEwinMatch(ewin); /* Saved settings */
|
|
if (!EwinIsInternal(ewin) && Mode.wm.startup)
|
|
EHintsGetInfo(ewin); /* E restart hints */
|
|
|
|
EoSetName(ewin, Estrdup(ewin->icccm.wm_name)); /* FIXME */
|
|
|
|
if (ewin->ewmh.opacity == 0)
|
|
ewin->ewmh.opacity = 0xffffffff;
|
|
EoChangeOpacity(ewin, ewin->ewmh.opacity);
|
|
|
|
EwinStateUpdate(ewin);
|
|
|
|
if (!ewin->props.no_button_grabs)
|
|
GrabButtonGrabs(ewin);
|
|
|
|
/* We must reparent after getting original window position */
|
|
EReparentWindow(_EwinGetClientWin(ewin), ewin->win_container, 0, 0);
|
|
ICCCM_Adopt(ewin);
|
|
|
|
EwinBorderSelect(ewin); /* Select border before calculating geometry */
|
|
EwinGetGeometry(ewin); /* Calculate window geometry before border parts */
|
|
EwinBorderSetTo(ewin, NULL);
|
|
|
|
EwinEventsConfigure(ewin, 1);
|
|
|
|
if (ewin->state.shaded)
|
|
EwinInstantShade(ewin, 1);
|
|
|
|
HintsSetWindowState(ewin);
|
|
HintsSetWindowOpacity(ewin);
|
|
|
|
HintsSetClientList();
|
|
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("Adopt %#lx st=%d: %s\n", _EwinGetClientXwin(ewin),
|
|
ewin->state.state, EwinGetName(ewin));
|
|
}
|
|
|
|
void
|
|
AddToFamily(EWin * ewin, Window win)
|
|
{
|
|
EWin *ewin2;
|
|
EWin **lst;
|
|
int i, k, num, fx, fy, x, y;
|
|
char doslide, manplace;
|
|
Desk *dsk;
|
|
|
|
EGrabServer();
|
|
|
|
if (ewin)
|
|
EwinCleanup(ewin);
|
|
else
|
|
ewin = EwinCreate(win, EWIN_TYPE_NORMAL);
|
|
if (!ewin)
|
|
{
|
|
Eprintf("Window is gone %#lx\n", win);
|
|
goto done;
|
|
}
|
|
|
|
/* adopt the new baby */
|
|
Adopt(ewin);
|
|
|
|
/* if it hasn't been planted on a desktop - assign it the current desktop */
|
|
dsk = EoGetDesk(ewin);
|
|
|
|
/* if is an afterstep/windowmaker dock app - dock it */
|
|
if (Conf.dock.enable && ewin->state.docked)
|
|
DockIt(ewin);
|
|
|
|
ewin2 = NULL;
|
|
if (ewin->icccm.transient)
|
|
{
|
|
if (ewin->icccm.transient_for == None ||
|
|
ewin->icccm.transient_for == VRoot.win)
|
|
{
|
|
/* Group transient */
|
|
ewin->icccm.transient_for = VRoot.win;
|
|
#if 0 /* Maybe? */
|
|
ewin->layer++;
|
|
#endif
|
|
/* Don't treat this as a normal transient */
|
|
ewin->icccm.transient = -1;
|
|
}
|
|
else if (ewin->icccm.transient_for == _EwinGetClientXwin(ewin))
|
|
{
|
|
/* Some apps actually do this. Why? */
|
|
ewin->icccm.transient = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Regular transient */
|
|
}
|
|
|
|
if (ewin->icccm.transient)
|
|
{
|
|
/* Tag the parent window if this is a transient */
|
|
lst = EwinListTransientFor(ewin, &num);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
lst[i]->icccm.transient_count++;
|
|
if (EoGetLayer(ewin) < EoGetLayer(lst[i]))
|
|
EoSetLayer(ewin, EoGetLayer(lst[i]));
|
|
}
|
|
if (lst)
|
|
{
|
|
ewin2 = lst[0];
|
|
EoSetSticky(ewin, EoIsSticky(lst[0]));
|
|
Efree(lst);
|
|
}
|
|
else
|
|
{
|
|
/* No parents? - not a transient */
|
|
ewin->icccm.transient = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
x = EoGetX(ewin);
|
|
y = EoGetY(ewin);
|
|
if (ewin->icccm.transient && Conf.focus.transientsfollowleader)
|
|
{
|
|
EWin *const *lst2;
|
|
|
|
if (!ewin2)
|
|
ewin2 = EwinFindByClient(ewin->icccm.group);
|
|
|
|
if (!ewin2)
|
|
{
|
|
lst2 = EwinListGetAll(&num);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
if ((lst2[i]->state.iconified) ||
|
|
(ewin->icccm.group != lst2[i]->icccm.group))
|
|
continue;
|
|
|
|
ewin2 = lst2[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ewin2)
|
|
{
|
|
dsk = EoGetDesk(ewin2);
|
|
if (!Mode.wm.startup && Conf.focus.switchfortransientmap &&
|
|
!ewin->state.iconified)
|
|
DeskGotoByEwin(ewin2);
|
|
}
|
|
}
|
|
|
|
if (ewin->state.fullscreen)
|
|
{
|
|
EwinSetFullscreen(ewin, 2);
|
|
ewin->state.placed = 1;
|
|
EwinMoveToDesktopAt(ewin, dsk, EoGetX(ewin), EoGetY(ewin));
|
|
ShowEwin(ewin);
|
|
goto done;
|
|
}
|
|
|
|
EwinResize(ewin, ewin->client.w, ewin->client.h);
|
|
|
|
doslide = manplace = 0;
|
|
if (Mode.place.enable_features)
|
|
{
|
|
/* if set for borderless then dont slide it in */
|
|
if (Conf.place.slidein && !Mode.place.doing_slide &&
|
|
!ewin->state.no_border)
|
|
doslide = 1;
|
|
|
|
if (Conf.place.manual && !Mode.place.doing_manual &&
|
|
!ewin->state.placed && !ewin->icccm.transient)
|
|
{
|
|
if (GrabPointerSet(VRoot.win, ECSR_GRAB, 0) == GrabSuccess)
|
|
manplace = 1;
|
|
}
|
|
}
|
|
|
|
/* if it hasn't been placed yet.... find a spot for it */
|
|
if ((!ewin->state.placed) && (!manplace))
|
|
{
|
|
/* Place the window below the mouse pointer */
|
|
if (Conf.place.manual_mouse_pointer)
|
|
{
|
|
int rx, ry;
|
|
int newWinX = 0, newWinY = 0;
|
|
|
|
/* if the loser has manual placement on and the app asks to be on */
|
|
/* a desktop, then send E to that desktop so the user can place */
|
|
/* the window there */
|
|
DeskGoto(dsk);
|
|
|
|
EQueryPointer(VRoot.win, &rx, &ry, NULL, NULL);
|
|
Mode.events.x = rx;
|
|
Mode.events.y = ry;
|
|
ewin->state.placed = 1;
|
|
|
|
/* try to center the window on the mouse pointer */
|
|
newWinX = rx;
|
|
newWinY = ry;
|
|
if (EoGetW(ewin))
|
|
newWinX -= EoGetW(ewin) / 2;
|
|
if (EoGetH(ewin))
|
|
newWinY -= EoGetH(ewin) / 2;
|
|
|
|
/* keep it all on this screen if possible */
|
|
newWinX = MIN(newWinX, VRoot.w - EoGetW(ewin));
|
|
newWinY = MIN(newWinY, VRoot.h - EoGetH(ewin));
|
|
newWinX = MAX(newWinX, 0);
|
|
newWinY = MAX(newWinY, 0);
|
|
|
|
/* this works for me... */
|
|
x = newWinX;
|
|
y = newWinY;
|
|
}
|
|
else
|
|
{
|
|
ewin->state.placed = 1;
|
|
ArrangeEwinXY(ewin, &x, &y);
|
|
}
|
|
}
|
|
|
|
/* if the window asked to be iconified at the start */
|
|
if (ewin->icccm.start_iconified)
|
|
{
|
|
EwinMoveToDesktopAt(ewin, dsk, x, y);
|
|
ewin->state.state = EWIN_STATE_MAPPED;
|
|
EwinIconify(ewin);
|
|
ewin->state.state = EWIN_STATE_ICONIC;
|
|
goto done;
|
|
}
|
|
|
|
/* if we should slide it in and are not currently in the middle of a slide */
|
|
if ((manplace) && (!ewin->state.placed))
|
|
{
|
|
int rx, ry;
|
|
|
|
/* if the loser has manual placement on and the app asks to be on */
|
|
/* a desktop, then send E to that desktop so the user can place */
|
|
/* the window there */
|
|
DeskGoto(dsk);
|
|
|
|
EQueryPointer(VRoot.win, &rx, &ry, NULL, NULL);
|
|
Mode.events.x = rx;
|
|
Mode.events.y = ry;
|
|
ewin->state.placed = 1;
|
|
x = Mode.events.x + 1;
|
|
y = Mode.events.y + 1;
|
|
EwinMoveToDesktopAt(ewin, dsk, x, y);
|
|
EwinMove(ewin, x, y);
|
|
ShowEwin(ewin);
|
|
GrabPointerSet(VRoot.win, ECSR_GRAB, 0);
|
|
Mode.place.doing_manual = 1;
|
|
EoSetFloating(ewin, 1); /* Causes reparenting to root */
|
|
ActionMoveStart(ewin, 1, 0, 0);
|
|
goto done;
|
|
}
|
|
else if (doslide)
|
|
{
|
|
k = rand() % 4;
|
|
if (k == 0)
|
|
{
|
|
fx = (rand() % (VRoot.w)) - EoGetW(ewin);
|
|
fy = -EoGetH(ewin);
|
|
}
|
|
else if (k == 1)
|
|
{
|
|
fx = (rand() % (VRoot.w));
|
|
fy = VRoot.h;
|
|
}
|
|
else if (k == 2)
|
|
{
|
|
fx = -EoGetW(ewin);
|
|
fy = (rand() % (VRoot.h));
|
|
}
|
|
else
|
|
{
|
|
fx = VRoot.w;
|
|
fy = (rand() % (VRoot.h)) - EoGetH(ewin);
|
|
}
|
|
Mode.place.doing_slide = 1;
|
|
ewin->state.animated = 1;
|
|
FocusEnable(0);
|
|
|
|
EwinMoveToDesktopAt(ewin, dsk, fx, fy);
|
|
ShowEwin(ewin);
|
|
ewin->req_x = x;
|
|
ewin->req_y = y;
|
|
DoIn("Slide", 0.05, EwinSlideIn, 0, ewin);
|
|
}
|
|
else
|
|
{
|
|
EwinMoveToDesktopAt(ewin, dsk, x, y);
|
|
ShowEwin(ewin);
|
|
}
|
|
|
|
done:
|
|
EUngrabServer();
|
|
}
|
|
|
|
EWin *
|
|
AddInternalToFamily(Window win, const char *bname, int type, void *ptr,
|
|
void (*init) (EWin * ewin, void *ptr))
|
|
{
|
|
EWin *ewin;
|
|
|
|
EGrabServer();
|
|
|
|
ewin = EwinCreate(win, type);
|
|
if (!ewin)
|
|
goto done;
|
|
|
|
if (bname)
|
|
ewin->border = FindItem(bname, 0, LIST_FINDBY_NAME, LIST_TYPE_BORDER);
|
|
|
|
if (init)
|
|
init(ewin, ptr); /* Type specific initialisation */
|
|
|
|
Adopt(ewin);
|
|
|
|
#if 0
|
|
Eprintf("Desk=%d, layer=%d, sticky=%d, floating=%d\n",
|
|
EoGetDesk(ewin), EoGetLayer(ewin), EoIsSticky(ewin),
|
|
EoIsFloating(ewin));
|
|
#endif
|
|
|
|
done:
|
|
EUngrabServer();
|
|
|
|
return ewin;
|
|
}
|
|
|
|
static void
|
|
EwinUnmap1(EWin * ewin)
|
|
{
|
|
/* The client may have been unmapped but the frame is not yet */
|
|
|
|
if (GetZoomEWin() == ewin)
|
|
Zoom(NULL);
|
|
|
|
ActionsEnd(ewin);
|
|
|
|
if (Mode.place.doing_slide)
|
|
{
|
|
#if 0 /* No event processing during slides - No use doing this here */
|
|
DrawEwinShape(ewin, Conf.slidemode, ewin->shape_x, ewin->shape_y,
|
|
ewin->client.w, ewin->client.h, 2);
|
|
#endif
|
|
#if 0 /* FIXME - Do this right */
|
|
RemoveTimerEvent("Slide");
|
|
Mode.place.doing_slide = 0;
|
|
FocusEnable(1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinUnmap2(EWin * ewin)
|
|
{
|
|
/* The frame has been unmapped */
|
|
|
|
FocusToEWin(ewin, FOCUS_EWIN_GONE);
|
|
if (ewin == Mode.mouse_over_ewin)
|
|
Mode.mouse_over_ewin = NULL;
|
|
if (ewin == Mode.context_ewin)
|
|
Mode.context_ewin = NULL;
|
|
|
|
ModulesSignal(ESIGNAL_EWIN_UNMAP, ewin);
|
|
}
|
|
|
|
static void
|
|
EwinWithdraw(EWin * ewin)
|
|
{
|
|
Window win;
|
|
int x, y;
|
|
|
|
/* Only external clients should go here */
|
|
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinWithdraw %#lx st=%d: %s\n", _EwinGetClientXwin(ewin),
|
|
ewin->state.state, EwinGetName(ewin));
|
|
|
|
EGrabServer();
|
|
|
|
/* Park the client window on the root */
|
|
x = ewin->client.x;
|
|
y = ewin->client.y;
|
|
ETranslateCoordinates(_EwinGetClientWin(ewin), VRoot.win,
|
|
-ewin->border->border.left,
|
|
-ewin->border->border.top, &x, &y, &win);
|
|
EReparentWindow(_EwinGetClientWin(ewin), VRoot.win, x, y);
|
|
ICCCM_Withdraw(ewin);
|
|
HintsDelWindowHints(ewin);
|
|
|
|
ESync();
|
|
EUngrabServer();
|
|
}
|
|
|
|
static void
|
|
EwinEventMapRequest(EWin * ewin, Window win)
|
|
{
|
|
if (ewin)
|
|
{
|
|
if (ewin->state.state == EWIN_STATE_ICONIC)
|
|
EwinDeIconify(ewin);
|
|
if (ewin->state.state == EWIN_STATE_WITHDRAWN)
|
|
AddToFamily(ewin, win);
|
|
else
|
|
{
|
|
Eprintf("AddToFamily: Already managing %s %#lx\n", "A",
|
|
_EwinGetClientXwin(ewin));
|
|
EReparentWindow(_EwinGetClientWin(ewin), ewin->win_container, 0,
|
|
0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check if we are already managing it */
|
|
ewin = EwinFindByClient(win);
|
|
|
|
/* Some clients MapRequest more than once ?!? */
|
|
if (ewin)
|
|
{
|
|
Eprintf("AddToFamily: Already managing %s %#lx\n", "B",
|
|
_EwinGetClientXwin(ewin));
|
|
EReparentWindow(_EwinGetClientWin(ewin), ewin->win_container, 0,
|
|
0);
|
|
ShowEwin(ewin);
|
|
}
|
|
else
|
|
AddToFamily(NULL, win);
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinEventDestroy(EWin * ewin)
|
|
{
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinEventDestroy %#lx st=%d: %s\n", _EwinGetClientXwin(ewin),
|
|
ewin->state.state, EwinGetName(ewin));
|
|
|
|
EwinDestroy(ewin);
|
|
}
|
|
|
|
static void
|
|
EwinEventReparent(EWin * ewin)
|
|
{
|
|
Window parent;
|
|
|
|
EGrabServer();
|
|
|
|
/* Refetch parent window. We cannot rely on the one in the event. */
|
|
parent = EWindowGetParent(_EwinGetClientWin(ewin));
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinEventReparent %#lx st=%d parent=%#lx: %s\n",
|
|
_EwinGetClientXwin(ewin), ewin->state.state, parent,
|
|
EwinGetName(ewin));
|
|
if (parent != _EwinGetContainerXwin(ewin))
|
|
EwinDestroy(ewin);
|
|
|
|
EUngrabServer();
|
|
}
|
|
|
|
static void
|
|
EwinEventMap(EWin * ewin)
|
|
{
|
|
int old_state = ewin->state.state;
|
|
|
|
ewin->state.state = EWIN_STATE_MAPPED;
|
|
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinEventMap %#lx st=%d: %s\n", _EwinGetClientXwin(ewin),
|
|
ewin->state.state, EwinGetName(ewin));
|
|
|
|
/* If first time we may want to focus it (unless during startup) */
|
|
if (old_state == EWIN_STATE_NEW)
|
|
FocusToEWin(ewin, FOCUS_EWIN_NEW);
|
|
else
|
|
FocusToEWin(ewin, FOCUS_SET);
|
|
|
|
ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
|
|
}
|
|
|
|
static void
|
|
EwinEventUnmap(EWin * ewin)
|
|
{
|
|
if (EventDebug(EDBUG_TYPE_EWINS))
|
|
Eprintf("EwinEventUnmap %#lx st=%d: %s\n", _EwinGetClientXwin(ewin),
|
|
ewin->state.state, EwinGetName(ewin));
|
|
|
|
if (ewin->state.state == EWIN_STATE_WITHDRAWN)
|
|
return;
|
|
|
|
if (ewin->state.state == EWIN_STATE_ICONIC || !ewin->state.iconified)
|
|
ewin->state.state = EWIN_STATE_WITHDRAWN;
|
|
else
|
|
ewin->state.state = EWIN_STATE_ICONIC;
|
|
|
|
EwinUnmap1(ewin);
|
|
EWindowSetMapped(_EwinGetClientWin(ewin), 0);
|
|
EoUnmap(ewin);
|
|
EwinUnmap2(ewin);
|
|
|
|
if (ewin->state.state == EWIN_STATE_ICONIC)
|
|
return;
|
|
|
|
if (EwinIsInternal(ewin))
|
|
{
|
|
#if 1 /* FIXME - Remove? */
|
|
/* We should never get here */
|
|
Eprintf("FIXME: This cannot happen (%s)\n", EoGetName(ewin));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (EWindowGetParent(_EwinGetClientWin(ewin)) == _EwinGetContainerXwin(ewin))
|
|
EwinWithdraw(ewin);
|
|
}
|
|
|
|
static void
|
|
EwinEventConfigureRequest(EWin * ewin, XEvent * ev)
|
|
{
|
|
Window winrel;
|
|
EWin *ewin2;
|
|
int x = 0, y = 0, w = 0, h = 0;
|
|
XWindowChanges xwc;
|
|
|
|
if (ewin)
|
|
{
|
|
x = EoGetX(ewin);
|
|
y = EoGetY(ewin);
|
|
w = ewin->client.w;
|
|
h = ewin->client.h;
|
|
winrel = 0;
|
|
if (ev->xconfigurerequest.value_mask & CWX)
|
|
x = ev->xconfigurerequest.x;
|
|
if (ev->xconfigurerequest.value_mask & CWY)
|
|
y = ev->xconfigurerequest.y;
|
|
if (ev->xconfigurerequest.value_mask & CWWidth)
|
|
w = ev->xconfigurerequest.width;
|
|
if (ev->xconfigurerequest.value_mask & CWHeight)
|
|
h = ev->xconfigurerequest.height;
|
|
if (ev->xconfigurerequest.value_mask & CWSibling)
|
|
winrel = ev->xconfigurerequest.above;
|
|
if (ev->xconfigurerequest.value_mask & CWStackMode)
|
|
{
|
|
ewin2 = EwinFindByClient(winrel);
|
|
if (ewin2)
|
|
winrel = EoGetWin(ewin2);
|
|
xwc.sibling = winrel;
|
|
xwc.stack_mode = ev->xconfigurerequest.detail;
|
|
if (Mode.mode == MODE_NONE)
|
|
{
|
|
if (xwc.stack_mode == Above)
|
|
RaiseEwin(ewin);
|
|
else if (xwc.stack_mode == Below)
|
|
LowerEwin(ewin);
|
|
}
|
|
}
|
|
|
|
if (ev->xconfigurerequest.value_mask & (CWX | CWY))
|
|
{
|
|
/* Correct position taking gravity into account */
|
|
EwinGetPosition(ewin, x, y, ewin->client.bw, ewin->client.grav,
|
|
&x, &y);
|
|
}
|
|
|
|
Mode.move.check = 0; /* Don't restrict client requests */
|
|
EwinMoveResize(ewin, x, y, w, h);
|
|
Mode.move.check = 1;
|
|
ReZoom(ewin);
|
|
}
|
|
else
|
|
{
|
|
xwc.x = ev->xconfigurerequest.x;
|
|
xwc.y = ev->xconfigurerequest.y;
|
|
xwc.width = ev->xconfigurerequest.width;
|
|
xwc.height = ev->xconfigurerequest.height;
|
|
xwc.border_width = ev->xconfigurerequest.border_width;
|
|
xwc.sibling = ev->xconfigurerequest.above;
|
|
xwc.stack_mode = ev->xconfigurerequest.detail;
|
|
EConfigureWindow(ev->xconfigurerequest.window,
|
|
ev->xconfigurerequest.value_mask, &xwc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinEventResizeRequest(EWin * ewin, XEvent * ev)
|
|
{
|
|
if (ewin)
|
|
{
|
|
EwinResize(ewin, ev->xresizerequest.width, ev->xresizerequest.height);
|
|
ReZoom(ewin);
|
|
}
|
|
else
|
|
{
|
|
EResizeWindow(ev->xresizerequest.window,
|
|
ev->xresizerequest.width, ev->xresizerequest.height);
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinEventCirculateRequest(EWin * ewin, XEvent * ev)
|
|
{
|
|
if (ewin)
|
|
{
|
|
if (ev->xcirculaterequest.place == PlaceOnTop)
|
|
RaiseEwin(ewin);
|
|
else
|
|
LowerEwin(ewin);
|
|
}
|
|
else
|
|
{
|
|
if (ev->xcirculaterequest.place == PlaceOnTop)
|
|
ERaiseWindow(ev->xcirculaterequest.window);
|
|
else
|
|
ELowerWindow(ev->xcirculaterequest.window);
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinEventPropertyNotify(EWin * ewin, XEvent * ev)
|
|
{
|
|
EGrabServer();
|
|
EwinChangesStart(ewin);
|
|
|
|
HintsProcessPropertyChange(ewin, ev->xproperty.atom);
|
|
SessionGetInfo(ewin, ev->xproperty.atom);
|
|
EwinStateUpdate(ewin);
|
|
|
|
EwinChangesProcess(ewin);
|
|
EUngrabServer();
|
|
}
|
|
|
|
static void
|
|
EwinEventShapeChange(EWin * ewin)
|
|
{
|
|
EwinUpdateShapeInfo(ewin);
|
|
ewin->update.shape = 1;
|
|
EwinPropagateShapes(ewin);
|
|
}
|
|
|
|
static void
|
|
EwinEventVisibility(EWin * ewin, int state)
|
|
{
|
|
ewin->state.visibility = state;
|
|
}
|
|
|
|
void
|
|
EwinReparent(EWin * ewin, Window parent)
|
|
{
|
|
EReparentWindow(_EwinGetClientWin(ewin), parent, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Place particular EWin at appropriate location in the window stack
|
|
*/
|
|
static void
|
|
RestackEwin(EWin * ewin)
|
|
{
|
|
EWin *const *lst;
|
|
int i, num;
|
|
XWindowChanges xwc;
|
|
unsigned int value_mask;
|
|
|
|
if (EventDebug(EDBUG_TYPE_STACKING))
|
|
Eprintf("RestackEwin %#lx %s\n", _EwinGetClientXwin(ewin),
|
|
EwinGetName(ewin));
|
|
|
|
lst = EwinListGetForDesk(&num, EoGetDesk(ewin));
|
|
if (num < 2)
|
|
goto done;
|
|
|
|
for (i = 0; i < num; i++)
|
|
if (lst[i] == ewin)
|
|
break;
|
|
if (i < num - 1)
|
|
{
|
|
xwc.stack_mode = Above;
|
|
xwc.sibling = EoGetWin(lst[i + 1]);
|
|
}
|
|
else
|
|
{
|
|
xwc.stack_mode = Below;
|
|
xwc.sibling = EoGetWin(lst[i - 1]);
|
|
}
|
|
value_mask = CWSibling | CWStackMode;
|
|
if (EventDebug(EDBUG_TYPE_STACKING))
|
|
Eprintf("RestackEwin %#10lx %s %#10lx\n", EoGetWin(ewin),
|
|
(xwc.stack_mode == Above) ? "Above" : "Below", xwc.sibling);
|
|
XConfigureWindow(disp, EoGetWin(ewin), value_mask, &xwc);
|
|
HintsSetClientStacking();
|
|
ModulesSignal(ESIGNAL_EWIN_CHANGE, ewin);
|
|
|
|
done:
|
|
;
|
|
}
|
|
|
|
void
|
|
RaiseEwin(EWin * ewin)
|
|
{
|
|
static int call_depth = 0;
|
|
EWin **lst;
|
|
int i, num;
|
|
|
|
if (EoGetWin(ewin) == None)
|
|
return;
|
|
|
|
if (call_depth > 256)
|
|
return;
|
|
call_depth++;
|
|
|
|
num = EoRaise(ewin);
|
|
|
|
if (EventDebug(EDBUG_TYPE_RAISELOWER))
|
|
Eprintf("RaiseEwin(%d) %#lx %s n=%d\n", call_depth,
|
|
_EwinGetClientXwin(ewin), EwinGetName(ewin), num);
|
|
|
|
if (num == 0) /* Quit if stacking is unchanged */
|
|
goto done;
|
|
|
|
lst = EwinListTransients(ewin, &num, 1);
|
|
for (i = 0; i < num; i++)
|
|
RaiseEwin(lst[i]);
|
|
if (lst)
|
|
Efree(lst);
|
|
|
|
if (call_depth == 1)
|
|
{
|
|
if (num > 0)
|
|
StackDesktop(EoGetDesk(ewin)); /* Do the full stacking */
|
|
else
|
|
RestackEwin(ewin); /* Restack this one only */
|
|
}
|
|
|
|
done:
|
|
call_depth--;
|
|
}
|
|
|
|
void
|
|
LowerEwin(EWin * ewin)
|
|
{
|
|
static int call_depth = 0;
|
|
EWin **lst;
|
|
int i, num;
|
|
|
|
if (EoGetWin(ewin) == None)
|
|
return;
|
|
|
|
if (call_depth > 256)
|
|
return;
|
|
call_depth++;
|
|
|
|
num = EoLower(ewin);
|
|
|
|
if (EventDebug(EDBUG_TYPE_RAISELOWER))
|
|
Eprintf("LowerEwin(%d) %#lx %s n=%d\n", call_depth,
|
|
_EwinGetClientXwin(ewin), EwinGetName(ewin), num);
|
|
|
|
if (num == 0) /* Quit if stacking is unchanged */
|
|
goto done;
|
|
|
|
lst = EwinListTransientFor(ewin, &num);
|
|
for (i = 0; i < num; i++)
|
|
LowerEwin(lst[i]);
|
|
if (lst)
|
|
Efree(lst);
|
|
|
|
if (call_depth == 1)
|
|
{
|
|
if (num > 0)
|
|
StackDesktop(EoGetDesk(ewin)); /* Do the full stacking */
|
|
else
|
|
RestackEwin(ewin); /* Restack this one only */
|
|
}
|
|
|
|
done:
|
|
call_depth--;
|
|
}
|
|
|
|
void
|
|
ShowEwin(EWin * ewin)
|
|
{
|
|
if (EoIsShown(ewin))
|
|
return;
|
|
|
|
if (_EwinGetClientWin(ewin))
|
|
{
|
|
#if 0 /* FIXME - Why? */
|
|
if (ewin->state.shaded)
|
|
EMoveResizeWindow(ewin->win_container, -30, -30, 1, 1);
|
|
#endif
|
|
EMapWindow(_EwinGetClientWin(ewin));
|
|
}
|
|
|
|
if (ewin->update.shape)
|
|
{
|
|
ewin->o.shown = 1;
|
|
EwinPropagateShapes(ewin);
|
|
ewin->o.shown = 0;
|
|
}
|
|
|
|
EoMap(ewin, 0);
|
|
|
|
EwinStateUpdate(ewin);
|
|
}
|
|
|
|
void
|
|
HideEwin(EWin * ewin)
|
|
{
|
|
if (!EwinIsInternal(ewin) && (!EoIsShown(ewin) || !EwinIsMapped(ewin)))
|
|
return;
|
|
|
|
EwinUnmap1(ewin);
|
|
|
|
EUnmapWindow(_EwinGetClientWin(ewin));
|
|
EoUnmap(ewin);
|
|
|
|
EwinUnmap2(ewin);
|
|
|
|
EwinStateUpdate(ewin);
|
|
|
|
if (!EwinIsInternal(ewin) || ewin->state.iconified)
|
|
return;
|
|
|
|
if (ewin->Close)
|
|
ewin->Close(ewin);
|
|
|
|
EwinDestroy(ewin);
|
|
}
|
|
|
|
Window
|
|
EwinGetClientWin(const EWin * ewin)
|
|
{
|
|
return (ewin) ? _EwinGetClientWin(ewin) : None;
|
|
}
|
|
|
|
const char *
|
|
EwinGetName(const EWin * ewin)
|
|
{
|
|
const char *name;
|
|
|
|
if (!ewin)
|
|
return NULL;
|
|
#if ENABLE_EWMH
|
|
name = ewin->ewmh.wm_name;
|
|
if (name)
|
|
goto done;
|
|
#endif
|
|
name = ewin->icccm.wm_name;
|
|
if (name)
|
|
goto done;
|
|
|
|
done:
|
|
return (name && name[0]) ? name : NULL;
|
|
}
|
|
|
|
const char *
|
|
EwinGetIconName(const EWin * ewin)
|
|
{
|
|
const char *name;
|
|
|
|
#if ENABLE_EWMH
|
|
name = ewin->ewmh.wm_icon_name;
|
|
if (name)
|
|
goto done;
|
|
#endif
|
|
name = ewin->icccm.wm_icon_name;
|
|
if (name)
|
|
goto done;
|
|
|
|
return EwinGetName(ewin);
|
|
|
|
done:
|
|
return (name && strlen(name)) ? name : NULL;
|
|
}
|
|
|
|
int
|
|
EwinIsOnScreen(const EWin * ewin)
|
|
{
|
|
int x, y, w, h;
|
|
|
|
if (EoIsSticky(ewin))
|
|
return 1;
|
|
if (EoGetDesk(ewin) != DesksGetCurrent())
|
|
return 0;
|
|
|
|
x = EoGetX(ewin);
|
|
y = EoGetY(ewin);
|
|
w = EoGetW(ewin);
|
|
h = EoGetH(ewin);
|
|
|
|
if (x + w <= 0 || x >= VRoot.w || y + h <= 0 || y >= VRoot.h)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Save current position in absolute viewport coordinates
|
|
*/
|
|
void
|
|
EwinRememberPositionSet(EWin * ewin)
|
|
{
|
|
int ax, ay;
|
|
|
|
ewin->req_x = EoGetX(ewin);
|
|
ewin->req_y = EoGetY(ewin);
|
|
if (!EoIsSticky(ewin))
|
|
{
|
|
DeskGetArea(EoGetDesk(ewin), &ax, &ay);
|
|
ewin->req_x += ax * VRoot.w;
|
|
ewin->req_y += ay * VRoot.h;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get saved position in relative viewport coordinates
|
|
*/
|
|
void
|
|
EwinRememberPositionGet(EWin * ewin, int *px, int *py)
|
|
{
|
|
int x, y, ax, ay;
|
|
|
|
x = ewin->req_x;
|
|
y = ewin->req_y;
|
|
if (!EoIsSticky(ewin))
|
|
{
|
|
DeskGetArea(EoGetDesk(ewin), &ax, &ay);
|
|
x -= ax * VRoot.w;
|
|
y -= ay * VRoot.h;
|
|
}
|
|
|
|
*px = x;
|
|
*py = y;
|
|
}
|
|
|
|
/*
|
|
* Slidein
|
|
*/
|
|
static void
|
|
EwinSlideIn(int val __UNUSED__, void *data)
|
|
{
|
|
EWin *ewin = data;
|
|
|
|
/* May be gone */
|
|
if (!EwinFindByPtr(ewin))
|
|
goto done;
|
|
|
|
SlideEwinTo(ewin, EoGetX(ewin), EoGetY(ewin), ewin->req_x, ewin->req_y,
|
|
Conf.slidespeedmap);
|
|
|
|
done:
|
|
Mode.place.doing_slide = 0;
|
|
FocusEnable(1);
|
|
}
|
|
|
|
/*
|
|
* Change requests
|
|
*/
|
|
static struct
|
|
{
|
|
unsigned int flags;
|
|
EWin ewin_old;
|
|
} EWinChanges;
|
|
|
|
void
|
|
EwinChange(EWin * ewin, unsigned int flag)
|
|
{
|
|
EWinChanges.flags |= flag;
|
|
return;
|
|
ewin = NULL;
|
|
}
|
|
|
|
void
|
|
EwinChangesStart(EWin * ewin)
|
|
{
|
|
EWinChanges.flags = 0;
|
|
/* Brute force :) */
|
|
EWinChanges.ewin_old = *ewin;
|
|
}
|
|
|
|
void
|
|
EwinChangesProcess(EWin * ewin)
|
|
{
|
|
if (!EWinChanges.flags)
|
|
return;
|
|
|
|
if (EWinChanges.flags & EWIN_CHANGE_NAME)
|
|
{
|
|
EwinBorderUpdateInfo(ewin);
|
|
EwinBorderCalcSizes(ewin, 1);
|
|
}
|
|
|
|
if (EWinChanges.flags & EWIN_CHANGE_DESKTOP)
|
|
{
|
|
Desk *desk, *pdesk;
|
|
|
|
desk = EoGetDesk(ewin);
|
|
pdesk = EoGetDesk(&EWinChanges.ewin_old);
|
|
if (desk != pdesk && !EoIsSticky(ewin))
|
|
{
|
|
EoSetDesk(ewin, pdesk);
|
|
EwinMoveToDesktop(ewin, desk);
|
|
}
|
|
}
|
|
|
|
if (EWinChanges.flags & EWIN_CHANGE_ICON_PMAP)
|
|
{
|
|
ModulesSignal(ESIGNAL_EWIN_CHANGE_ICON, ewin);
|
|
}
|
|
|
|
if (EWinChanges.flags & EWIN_CHANGE_OPACITY)
|
|
{
|
|
EoChangeOpacity(ewin, ewin->ewmh.opacity);
|
|
SnapshotEwinUpdate(ewin, SNAP_USE_OPACITY);
|
|
}
|
|
|
|
if (EWinChanges.flags & EWIN_CHANGE_ATTENTION)
|
|
{
|
|
HintsSetWindowState(ewin);
|
|
}
|
|
|
|
EWinChanges.flags = 0;
|
|
}
|
|
|
|
void
|
|
EwinsEventsConfigure(int mode)
|
|
{
|
|
EWin *const *lst, *ewin;
|
|
int i, num;
|
|
|
|
lst = EwinListGetAll(&num);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
ewin = lst[i];
|
|
|
|
EwinEventsConfigure(lst[i], mode);
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinsTouch(Desk * dsk)
|
|
{
|
|
int i, num;
|
|
EWin *const *lst, *ewin;
|
|
|
|
if (!dsk)
|
|
lst = EwinListGetAll(&num);
|
|
else
|
|
lst = EwinListGetForDesk(&num, dsk);
|
|
|
|
for (i = num - 1; i >= 0; i--)
|
|
{
|
|
ewin = lst[i];
|
|
if (EwinIsMapped(ewin) && EwinIsOnScreen(ewin))
|
|
#if 1 /* FIXME - Which one? */
|
|
EwinMove(ewin, EoGetX(ewin), EoGetY(ewin));
|
|
#else
|
|
EwinResize(ewin, ewin->client.w, ewin->client.h);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
EwinsMoveStickyToDesk(Desk * dsk)
|
|
{
|
|
EWin *const *lst, *ewin;
|
|
int i, num;
|
|
|
|
lst = EwinListStackGet(&num);
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
ewin = lst[num - 1 - i];
|
|
if (!EoIsSticky(ewin) && !EoIsFloating(ewin))
|
|
continue;
|
|
if (EwinIsTransientChild(ewin))
|
|
continue;
|
|
|
|
EwinMoveToDesktop(ewin, dsk);
|
|
}
|
|
}
|
|
|
|
void
|
|
EwinsSetFree(void)
|
|
{
|
|
int i, num;
|
|
EWin *const *lst, *ewin;
|
|
|
|
if (EventDebug(EDBUG_TYPE_SESSION))
|
|
Eprintf("EwinsSetFree\n");
|
|
|
|
lst = EwinListStackGet(&num);
|
|
for (i = num - 1; i >= 0; i--)
|
|
{
|
|
ewin = lst[i];
|
|
if (EwinIsInternal(ewin))
|
|
continue;
|
|
|
|
if (ewin->state.iconified)
|
|
ICCCM_DeIconify(ewin);
|
|
|
|
/* This makes E determine the client window stacking at exit */
|
|
EwinInstantUnShade(ewin);
|
|
EReparentWindow(_EwinGetClientWin(ewin), RRoot.win,
|
|
ewin->client.x, ewin->client.y);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Event handlers
|
|
*/
|
|
|
|
static int
|
|
ActionsCheck(const char *which, EWin * ewin, XEvent * ev)
|
|
{
|
|
ActionClass *ac;
|
|
|
|
if (Mode.action_inhibit) /* Probably not here */
|
|
return 0;
|
|
|
|
ac = FindItem(which, 0, LIST_FINDBY_NAME, LIST_TYPE_ACLASS);
|
|
if (!ac)
|
|
return 0;
|
|
|
|
if (ev->type == ButtonPress)
|
|
{
|
|
GrabPointerSet(EoGetWin(ewin), ECSR_GRAB, 0);
|
|
FocusToEWin(ewin, FOCUS_CLICK);
|
|
}
|
|
else if (ev->type == ButtonRelease)
|
|
{
|
|
GrabPointerRelease();
|
|
}
|
|
|
|
return ActionclassEvent(ac, ev, ewin);
|
|
}
|
|
|
|
#define DEBUG_EWIN_EVENTS 0
|
|
static void
|
|
EwinHandleEventsToplevel(XEvent * ev, void *prm)
|
|
{
|
|
EWin *ewin = (EWin *) prm;
|
|
|
|
switch (ev->type)
|
|
{
|
|
case ButtonPress:
|
|
ActionsCheck("BUTTONBINDINGS", ewin, ev);
|
|
break;
|
|
case ButtonRelease:
|
|
ActionsCheck("BUTTONBINDINGS", ewin, ev);
|
|
break;
|
|
case EnterNotify:
|
|
FocusHandleEnter(ewin, ev);
|
|
break;
|
|
case LeaveNotify:
|
|
FocusHandleLeave(ewin, ev);
|
|
break;
|
|
case MotionNotify:
|
|
break;
|
|
default:
|
|
#if DEBUG_EWIN_EVENTS
|
|
Eprintf("EwinHandleEventsToplevel: type=%2d win=%#lx: %s\n",
|
|
ev->type, _EwinGetClientXwin(ewin), EwinGetName(ewin));
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinHandleEventsContainer(XEvent * ev, void *prm)
|
|
{
|
|
EWin *ewin = (EWin *) prm;
|
|
|
|
#if 0
|
|
Eprintf("EwinHandleEventsContainer: type=%2d win=%#lx: %s\n",
|
|
ev->type, _EwinGetClientXwin(ewin), EwinGetName(ewin));
|
|
#endif
|
|
switch (ev->type)
|
|
{
|
|
case ButtonPress:
|
|
FocusHandleClick(ewin, ev->xany.window);
|
|
break;
|
|
case MapRequest:
|
|
EwinEventMapRequest(ewin, ev->xmaprequest.window);
|
|
break;
|
|
case ConfigureRequest:
|
|
EwinEventConfigureRequest(ewin, ev);
|
|
break;
|
|
case ResizeRequest:
|
|
EwinEventResizeRequest(ewin, ev);
|
|
break;
|
|
case CirculateRequest:
|
|
EwinEventCirculateRequest(ewin, ev);
|
|
break;
|
|
|
|
case DestroyNotify:
|
|
EwinEventDestroy(ewin);
|
|
break;
|
|
case UnmapNotify:
|
|
#if 0
|
|
if (ewin->state.state == EWIN_STATE_NEW)
|
|
{
|
|
Eprintf("EwinEventUnmap %#lx: Ignoring bogus Unmap event\n",
|
|
_EwinGetClientXwin(ewin));
|
|
break;
|
|
}
|
|
#endif
|
|
EwinEventUnmap(ewin);
|
|
break;
|
|
case MapNotify:
|
|
EwinEventMap(ewin);
|
|
break;
|
|
case ReparentNotify:
|
|
EwinEventReparent(ewin);
|
|
break;
|
|
|
|
case GravityNotify:
|
|
case ConfigureNotify:
|
|
break;
|
|
|
|
default:
|
|
Eprintf("EwinHandleEventsContainer: type=%2d win=%#lx: %s\n",
|
|
ev->type, _EwinGetClientXwin(ewin), EwinGetName(ewin));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinHandleEventsClient(XEvent * ev, void *prm)
|
|
{
|
|
EWin *ewin = (EWin *) prm;
|
|
|
|
switch (ev->type)
|
|
{
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
case MotionNotify:
|
|
case EnterNotify:
|
|
case LeaveNotify:
|
|
break;
|
|
case FocusIn:
|
|
case FocusOut:
|
|
if (ev->xfocus.detail == NotifyInferior)
|
|
break;
|
|
if (ewin->border->aclass)
|
|
ActionclassEvent(ewin->border->aclass, ev, ewin);
|
|
break;
|
|
case ConfigureNotify:
|
|
case GravityNotify:
|
|
break;
|
|
case VisibilityNotify:
|
|
EwinEventVisibility(ewin, ev->xvisibility.state);
|
|
break;
|
|
|
|
case PropertyNotify:
|
|
EwinEventPropertyNotify(ewin, ev);
|
|
break;
|
|
case EX_EVENT_SHAPE_NOTIFY:
|
|
EwinEventShapeChange(ewin);
|
|
default:
|
|
#if DEBUG_EWIN_EVENTS
|
|
Eprintf("EwinHandleEventsClient: type=%2d win=%#lx: %s\n",
|
|
ev->type, _EwinGetClientXwin(ewin), EwinGetName(ewin));
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinHandleEventsRoot(XEvent * ev, void *prm __UNUSED__)
|
|
{
|
|
EWin *ewin;
|
|
|
|
switch (ev->type)
|
|
{
|
|
case MapRequest:
|
|
EwinEventMapRequest(NULL, ev->xmaprequest.window);
|
|
break;
|
|
case ConfigureRequest:
|
|
#if 0
|
|
Eprintf("EwinHandleEventsRoot ConfigureRequest %#lx\n",
|
|
ev->xconfigurerequest.window);
|
|
#endif
|
|
ewin = EwinFindByClient(ev->xconfigurerequest.window);
|
|
EwinEventConfigureRequest(ewin, ev);
|
|
break;
|
|
case ResizeRequest:
|
|
#if 0
|
|
Eprintf("EwinHandleEventsRoot ResizeRequest %#lx\n",
|
|
ev->xresizerequest.window);
|
|
#endif
|
|
ewin = EwinFindByClient(ev->xresizerequest.window);
|
|
EwinEventResizeRequest(ewin, ev);
|
|
break;
|
|
case CirculateRequest:
|
|
#if 0
|
|
Eprintf("EwinHandleEventsRoot CirculateRequest %#lx\n",
|
|
ev->xcirculaterequest.window);
|
|
#endif
|
|
EwinEventCirculateRequest(NULL, ev);
|
|
break;
|
|
|
|
case UnmapNotify:
|
|
/* Catch clients unmapped after MapRequest but before being reparented */
|
|
ewin = EwinFindByClient(ev->xunmap.window);
|
|
if (ewin)
|
|
EwinEventUnmap(ewin);
|
|
break;
|
|
|
|
case DestroyNotify:
|
|
/* Catch clients destroyed after MapRequest but before being reparented */
|
|
ewin = EwinFindByClient(ev->xdestroywindow.window);
|
|
#if 0 /* FIXME - Should not be here - Remove? */
|
|
if (!ewin)
|
|
ewin = EwinFindByFrame(ev->xdestroywindow.window);
|
|
#endif
|
|
if (ewin)
|
|
EwinEventDestroy(ewin);
|
|
break;
|
|
|
|
case ReparentNotify:
|
|
ewin = EwinFindByClient(ev->xreparent.window);
|
|
if (ewin)
|
|
EwinEventReparent(ewin);
|
|
break;
|
|
|
|
default:
|
|
#if 0
|
|
Eprintf("EwinHandleEventsRoot: type=%2d win=%#lx\n",
|
|
ev->type, ev->xany.window);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
EwinsInit(void)
|
|
{
|
|
EventCallbackRegister(VRoot.win, 0, EwinHandleEventsRoot, NULL);
|
|
}
|
|
|
|
/*
|
|
* Ewins module
|
|
* This is the WM.
|
|
*/
|
|
|
|
static void
|
|
EwinsSighan(int sig, void *prm)
|
|
{
|
|
switch (sig)
|
|
{
|
|
case ESIGNAL_INIT:
|
|
EwinsInit();
|
|
break;
|
|
#if 0
|
|
case ESIGNAL_CONFIGURE:
|
|
if (!Conf.mapslide || Mode.wm.restart)
|
|
MapUnmap(1);
|
|
break;
|
|
case ESIGNAL_START:
|
|
if (Conf.mapslide && !Mode.wm.restart)
|
|
MapUnmap(1);
|
|
break;
|
|
#endif
|
|
case ESIGNAL_DESK_RESIZE:
|
|
EwinsTouch(NULL);
|
|
break;
|
|
case ESIGNAL_THEME_TRANS_CHANGE:
|
|
EwinsTouch(DesksGetCurrent());
|
|
break;
|
|
case ESIGNAL_BACKGROUND_CHANGE:
|
|
EwinsTouch(prm);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
IpcItem EwinsIpcArray[] = {
|
|
};
|
|
#define N_IPC_FUNCS (sizeof(EwinsIpcArray)/sizeof(IpcItem))
|
|
#else
|
|
#define N_IPC_FUNCS 0
|
|
#define EwinsIpcArray NULL
|
|
#endif
|
|
|
|
/*
|
|
* Module descriptor
|
|
*/
|
|
EModule ModEwins = {
|
|
"ewins", NULL,
|
|
EwinsSighan,
|
|
{N_IPC_FUNCS, EwinsIpcArray}
|
|
,
|
|
{0, NULL}
|
|
};
|